diff --git a/src/android/androidmusiclistener.cpp b/src/android/androidmusiclistener.cpp index a96e4273..d20f84b1 100644 --- a/src/android/androidmusiclistener.cpp +++ b/src/android/androidmusiclistener.cpp @@ -1,223 +1,219 @@ /* * Copyright 2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "androidmusiclistener.h" #include "databaseinterface.h" -#include "musicaudiotrack.h" #include #include // C++ code #include #include #include #include #include class AndroidMusicListenerPrivate { public: QString mSourceName; QHash mAllFiles; - QList mNewTracks; + DataTypes::ListTrackDataType mNewTracks; QHash mCovers; }; static void tracksAndroidScanStarted(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicTracksScanStarted(); } static void sentMusicFile(JNIEnv */*env*/, jobject /*obj*/, jstring musicFile) { QAndroidJniObject musicFileJavaString(musicFile); AndroidMusicListener::currentInstance()->newMusicTrack(musicFileJavaString.toString()); } static void tracksAndroidScanFinished(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicTracksScanFinished(); } static void albumsAndroidScanStarted(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicAlbumsScanStarted(); } static void sentMusicAlbum(JNIEnv */*env*/, jobject /*obj*/, jstring musicAlbum) { QAndroidJniObject musicAlbumJavaString(musicAlbum); AndroidMusicListener::currentInstance()->newMusicAlbum(musicAlbumJavaString.toString()); } static void albumsAndroidScanFinished(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicAlbumsScanFinished(); } AndroidMusicListener::AndroidMusicListener(QObject *parent) : QObject(parent), d(std::make_unique()) { AndroidMusicListener::mCurrentInstance = this; d->mSourceName = QStringLiteral("Android"); registerNativeMethods(); } DatabaseInterface *AndroidMusicListener::databaseInterface() const { return nullptr; } void AndroidMusicListener::registerNativeMethods() { JNINativeMethod methods[] {{"androidMusicScanTracksStarting", "()V", reinterpret_cast(tracksAndroidScanStarted)}, {"sendMusicFile", "(Ljava/lang/String;)V", reinterpret_cast(sentMusicFile)}, {"androidMusicScanTracksFinishing", "()V", reinterpret_cast(tracksAndroidScanFinished)}, {"androidMusicScanAlbumsStarting", "()V", reinterpret_cast(albumsAndroidScanStarted)}, {"sendMusicAlbum", "(Ljava/lang/String;)V", reinterpret_cast(sentMusicAlbum)}, {"androidMusicScanAlbumsFinishing", "()V", reinterpret_cast(albumsAndroidScanFinished)}, }; QAndroidJniObject javaClass("org/kde/elisa/ElisaAndroidMusicScanner"); QAndroidJniEnvironment env; jclass objectClass = env->GetObjectClass(javaClass.object()); env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0])); env->DeleteLocalRef(objectClass); } AndroidMusicListener *AndroidMusicListener::mCurrentInstance = nullptr; AndroidMusicListener *AndroidMusicListener::currentInstance() { return mCurrentInstance; } void AndroidMusicListener::androidMusicTracksScanStarted() { } void AndroidMusicListener::newMusicTrack(const QString &trackDescription) { auto trackData = trackDescription.split(QStringLiteral("||")); - auto newTrack = MusicAudioTrack{}; - newTrack.setTitle(trackData[1]); + auto newTrack = DataTypes::TrackDataType{}; + newTrack[DataTypes::TitleRole] = trackData[1]; bool conversionOK = false; if (trackData[2] != QLatin1String("null")) { - newTrack.setTrackNumber(trackData[2].toInt(&conversionOK)); + newTrack[DataTypes::TrackNumberRole] = trackData[2].toInt(&conversionOK); if (!conversionOK) { qInfo() << "newMusicTrack" << trackData[1] << trackData[2]; } } if (trackData[3] != QLatin1String("null")) { - newTrack.setYear(trackData[3].toInt(&conversionOK)); + newTrack[DataTypes::YearRole] = trackData[3].toInt(&conversionOK); if (!conversionOK) { qInfo() << "newMusicTrack" << trackData[1] << trackData[3]; } } if (trackData[4] != QLatin1String("null")) { - newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(trackData[4].toInt())); + newTrack[DataTypes::DurationRole] = QTime::fromMSecsSinceStartOfDay(trackData[4].toInt()); } - newTrack.setResourceURI(QUrl::fromLocalFile(trackData[5])); - newTrack.setArtist(trackData[6]); - newTrack.setAlbumName(trackData[8]); - newTrack.setComposer(trackData[10]); + newTrack[DataTypes::ResourceRole] = QUrl::fromLocalFile(trackData[5]); + newTrack[DataTypes::ArtistRole] = trackData[6]; + newTrack[DataTypes::AlbumRole] = trackData[8]; + newTrack[DataTypes::ComposerRole] = trackData[10]; d->mNewTracks.push_back(newTrack); } void AndroidMusicListener::androidMusicTracksScanFinished() { } void AndroidMusicListener::androidMusicAlbumsScanStarted() { } void AndroidMusicListener::newMusicAlbum(const QString &albumDescription) { auto albumData = albumDescription.split(QStringLiteral("||")); if (albumData[2] != QLatin1String("null)")) { d->mCovers[albumData[1]] = QUrl::fromLocalFile(albumData[2]); } } void AndroidMusicListener::androidMusicAlbumsScanFinished() { - Q_EMIT tracksList(d->mNewTracks, d->mCovers, d->mSourceName); + Q_EMIT tracksList(d->mNewTracks, d->mCovers); } void AndroidMusicListener::setDatabaseInterface(DatabaseInterface *model) { if (model) { connect(this, &AndroidMusicListener::tracksList, model, &DatabaseInterface::insertTracksList); connect(this, &AndroidMusicListener::removedTracksList, model, &DatabaseInterface::removeTracksList); - connect(this, &AndroidMusicListener::modifyTracksList, model, &DatabaseInterface::modifyTracksList); connect(this, &AndroidMusicListener::askRestoredTracks, model, &DatabaseInterface::askRestoredTracks); connect(model, &DatabaseInterface::restoredTracks, this, &AndroidMusicListener::restoredTracks); } Q_EMIT databaseInterfaceChanged(); if (model) { QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); } } void AndroidMusicListener::applicationAboutToQuit() { } void AndroidMusicListener::quitListener() { applicationAboutToQuit(); Q_EMIT clearDatabase(d->mSourceName); } -void AndroidMusicListener::restoredTracks(const QString &musicSource, QHash allFiles) +void AndroidMusicListener::restoredTracks(QHash allFiles) { - if (d->mSourceName == musicSource) { - d->mAllFiles = allFiles; + d->mAllFiles = allFiles; - QAndroidJniObject::callStaticMethod("org/kde/elisa/ElisaAndroidMusicScanner", - "listAudioFiles", - "(Landroid/content/Context;)V", - QtAndroid::androidContext().object()); - } + QAndroidJniObject::callStaticMethod("org/kde/elisa/ElisaAndroidMusicScanner", + "listAudioFiles", + "(Landroid/content/Context;)V", + QtAndroid::androidContext().object()); } void AndroidMusicListener::init() { Q_EMIT askRestoredTracks(d->mSourceName); } AndroidMusicListener::~AndroidMusicListener() = default; #include "moc_androidmusiclistener.cpp" diff --git a/src/android/androidmusiclistener.h b/src/android/androidmusiclistener.h index 1659ebbb..bf5fab31 100644 --- a/src/android/androidmusiclistener.h +++ b/src/android/androidmusiclistener.h @@ -1,107 +1,107 @@ /* * Copyright 2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef ANDROIDMUSICLISTENER_H #define ANDROIDMUSICLISTENER_H +#include "datatypes.h" + #include #include #include #include #include #include class DatabaseInterface; class MusicAudioTrack; class AndroidMusicListenerPrivate; class AndroidMusicListener : public QObject { Q_OBJECT Q_PROPERTY(DatabaseInterface* databaseInterface READ databaseInterface WRITE setDatabaseInterface NOTIFY databaseInterfaceChanged) public: explicit AndroidMusicListener(QObject *parent = nullptr); ~AndroidMusicListener() override; DatabaseInterface* databaseInterface() const; static void registerNativeMethods(); static AndroidMusicListener* currentInstance(); void androidMusicTracksScanStarted(); void newMusicTrack(const QString &trackDescription); void androidMusicTracksScanFinished(); void androidMusicAlbumsScanStarted(); void newMusicAlbum(const QString &albumDescription); void androidMusicAlbumsScanFinished(); Q_SIGNALS: void databaseInterfaceChanged(); void indexingStarted(); void indexingFinished(); void clearDatabase(const QString &listenerName); - void tracksList(const QList &tracks, const QHash &covers, const QString &musicSource); + void tracksList(const DataTypes::ListTrackDataType &tracks, const QHash &covers); void removedTracksList(const QList &removedTracks); - void modifyTracksList(const QList &modifiedTracks, const QHash &covers, const QString &musicSource); - void askRestoredTracks(const QString &musicSource); public Q_SLOTS: void setDatabaseInterface(DatabaseInterface* databaseInterface); void applicationAboutToQuit(); void quitListener(); - void restoredTracks(const QString &musicSource, QHash allFiles); + void restoredTracks(QHash allFiles); void init(); private: static AndroidMusicListener* mCurrentInstance; std::unique_ptr d; }; #endif // ANDROIDMUSICLISTENER_H diff --git a/src/manageheaderbar.cpp b/src/manageheaderbar.cpp index b04aaf2d..dae50970 100644 --- a/src/manageheaderbar.cpp +++ b/src/manageheaderbar.cpp @@ -1,364 +1,370 @@ /* * Copyright 2016 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "manageheaderbar.h" #include #include ManageHeaderBar::ManageHeaderBar(QObject *parent) : QObject(parent) { } void ManageHeaderBar::setArtistRole(int value) { mArtistRole = value; Q_EMIT artistRoleChanged(); } int ManageHeaderBar::artistRole() const { return mArtistRole; } void ManageHeaderBar::setTitleRole(int value) { mTitleRole = value; Q_EMIT titleRoleChanged(); } int ManageHeaderBar::titleRole() const { return mTitleRole; } void ManageHeaderBar::setAlbumRole(int value) { mAlbumRole = value; Q_EMIT albumRoleChanged(); } void ManageHeaderBar::setAlbumArtistRole(int value) { mAlbumArtistRole = value; Q_EMIT albumArtistRoleChanged(); } void ManageHeaderBar::setFileNameRole(int value) { mFileNameRole = value; Q_EMIT fileNameRoleChanged(); } int ManageHeaderBar::albumRole() const { return mAlbumRole; } int ManageHeaderBar::albumArtistRole() const { return mAlbumArtistRole; } int ManageHeaderBar::fileNameRole() const { return mFileNameRole; } void ManageHeaderBar::setImageRole(int value) { mImageRole = value; Q_EMIT imageRoleChanged(); } void ManageHeaderBar::setDatabaseIdRole(int databaseIdRole) { mDatabaseIdRole = databaseIdRole; Q_EMIT databaseIdRoleChanged(); } void ManageHeaderBar::setTrackTypeRole(int trackTypeRole) { mTrackTypeIdRole = trackTypeRole; Q_EMIT trackTypeRoleChanged(); } int ManageHeaderBar::imageRole() const { return mImageRole; } int ManageHeaderBar::databaseIdRole() const { return mDatabaseIdRole; } int ManageHeaderBar::trackTypeRole() const { return mTrackTypeIdRole; } void ManageHeaderBar::setAlbumIdRole(int albumIdRole) { mAlbumIdRole = albumIdRole; Q_EMIT albumIdRoleChanged(); } int ManageHeaderBar::albumIdRole() const { return mAlbumIdRole; } QVariant ManageHeaderBar::album() const { if (!mCurrentTrack.isValid()) { return QString(); } return mCurrentTrack.data(mAlbumRole); } QVariant ManageHeaderBar::albumArtist() const { if (!mCurrentTrack.isValid()) { return QString(); } return mCurrentTrack.data(mAlbumArtistRole); } QUrl ManageHeaderBar::fileUrl() const { QUrl result; if (!mCurrentTrack.isValid()) { return result; } result = mCurrentTrack.data(mFileNameRole).toUrl(); return result; } QVariant ManageHeaderBar::title() const { if (!mCurrentTrack.isValid()) { return QString(); } return mCurrentTrack.data(mTitleRole); } QVariant ManageHeaderBar::artist() const { if (!mCurrentTrack.isValid()) { return QString(); } - return mCurrentTrack.data(mArtistRole); + auto artistValue = mCurrentTrack.data(mArtistRole); + + if (!artistValue.isValid()) { + return mCurrentTrack.data(mAlbumArtistRole); + } + + return artistValue; } QUrl ManageHeaderBar::image() const { if (!mCurrentTrack.isValid()) { return {}; } return mCurrentTrack.data(mImageRole).toUrl(); } qulonglong ManageHeaderBar::databaseId() const { if (!mCurrentTrack.isValid()) { return 0; } return mCurrentTrack.data(mDatabaseIdRole).toULongLong(); } ElisaUtils::PlayListEntryType ManageHeaderBar::trackType() const { if (!mCurrentTrack.isValid()) { return ElisaUtils::Unknown; } return mCurrentTrack.data(mTrackTypeIdRole).value(); } qulonglong ManageHeaderBar::albumId() const { if (!mCurrentTrack.isValid()) { return 0; } return mCurrentTrack.data(mAlbumIdRole).toULongLong(); } bool ManageHeaderBar::isValid() const { if (!mCurrentTrack.isValid()) { return false; } return mCurrentTrack.data(mIsValidRole).toBool(); } int ManageHeaderBar::isValidRole() const { return mIsValidRole; } QPersistentModelIndex ManageHeaderBar::currentTrack() const { return mCurrentTrack; } void ManageHeaderBar::notifyArtistProperty() { auto newArtistValue = mCurrentTrack.data(mArtistRole); if (mOldArtist != newArtistValue) { Q_EMIT artistChanged(); mOldArtist = newArtistValue; } } void ManageHeaderBar::notifyTitleProperty() { auto newTitleValue = mCurrentTrack.data(mTitleRole); if (mOldTitle != newTitleValue) { Q_EMIT titleChanged(); mOldTitle = newTitleValue; } } void ManageHeaderBar::notifyAlbumProperty() { auto newAlbumValue = mCurrentTrack.data(mAlbumRole); if (mOldAlbum != newAlbumValue) { Q_EMIT albumChanged(); mOldAlbum = newAlbumValue; } } void ManageHeaderBar::notifyAlbumArtistProperty() { auto newAlbumArtistValue = mCurrentTrack.data(mAlbumArtistRole); if (mOldAlbumArtist != newAlbumArtistValue) { Q_EMIT albumArtistChanged(); mOldAlbumArtist = newAlbumArtistValue; } } void ManageHeaderBar::notifyFileNameProperty() { auto newFileNameValue = mCurrentTrack.data(mFileNameRole); if (mOldFileName != newFileNameValue) { Q_EMIT fileUrlChanged(); mOldFileName = newFileNameValue; } } void ManageHeaderBar::notifyImageProperty() { auto newImageValue = mCurrentTrack.data(mImageRole); if (mOldImage != newImageValue) { Q_EMIT imageChanged(); mOldImage = newImageValue; } } void ManageHeaderBar::notifyDatabaseIdProperty() { bool conversionOk; auto newDatabaseIdValue = mCurrentTrack.data(mDatabaseIdRole).toULongLong(&conversionOk); if (conversionOk && mOldDatabaseId != newDatabaseIdValue) { Q_EMIT databaseIdChanged(); mOldDatabaseId = newDatabaseIdValue; } else if (!conversionOk && mOldDatabaseId != 0) { Q_EMIT databaseIdChanged(); mOldDatabaseId = 0; } } void ManageHeaderBar::notifyTrackTypeProperty() { auto newTrackTypeValue = mCurrentTrack.data(mTrackTypeIdRole).value(); if (mOldTrackType != newTrackTypeValue) { Q_EMIT trackTypeRoleChanged(); mOldTrackType = newTrackTypeValue; } } void ManageHeaderBar::notifyAlbumIdProperty() { bool conversionOk; auto newAlbumIdValue = mCurrentTrack.data(mAlbumIdRole).toULongLong(&conversionOk); if (conversionOk && mOldAlbumId != newAlbumIdValue) { Q_EMIT albumIdChanged(); mOldAlbumId = newAlbumIdValue; } else if (!conversionOk && mOldAlbumId != 0) { Q_EMIT albumIdChanged(); mOldAlbumId = 0; } } void ManageHeaderBar::notifyIsValidProperty() { auto newIsValidValue = mCurrentTrack.data(mIsValidRole).toBool(); if (mOldIsValid != newIsValidValue) { Q_EMIT isValidChanged(); mOldIsValid = newIsValidValue; } } void ManageHeaderBar::setIsValidRole(int isValidRole) { mIsValidRole = isValidRole; emit isValidRoleChanged(); } void ManageHeaderBar::setCurrentTrack(const QPersistentModelIndex ¤tTrack) { mCurrentTrack = currentTrack; Q_EMIT currentTrackChanged(); notifyArtistProperty(); notifyTitleProperty(); notifyAlbumProperty(); notifyAlbumArtistProperty(); notifyFileNameProperty(); notifyImageProperty(); notifyDatabaseIdProperty(); notifyTrackTypeProperty(); notifyAlbumIdProperty(); notifyIsValidProperty(); } #include "moc_manageheaderbar.cpp" diff --git a/src/models/datamodel.cpp b/src/models/datamodel.cpp index 4ae7dbca..70a16b4e 100644 --- a/src/models/datamodel.cpp +++ b/src/models/datamodel.cpp @@ -1,849 +1,883 @@ /* * Copyright 2015-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "datamodel.h" #include "modeldataloader.h" #include "musiclistenersmanager.h" #include #include #include #include #include #include class DataModelPrivate { public: DataModel::ListTrackDataType mAllTrackData; DataModel::ListRadioDataType mAllRadiosData; DataModel::ListAlbumDataType mAllAlbumData; DataModel::ListArtistDataType mAllArtistData; DataModel::ListGenreDataType mAllGenreData; ModelDataLoader *mDataLoader = nullptr; ElisaUtils::PlayListEntryType mModelType = ElisaUtils::Unknown; ElisaUtils::FilterType mFilterType = ElisaUtils::UnknownFilter; QString mArtist; QString mAlbumTitle; QString mAlbumArtist; QString mGenre; qulonglong mDatabaseId = 0; bool mIsBusy = false; }; DataModel::DataModel(QObject *parent) : QAbstractListModel(parent), d(std::make_unique()) { d->mDataLoader = new ModelDataLoader; connect(this, &DataModel::destroyed, d->mDataLoader, &ModelDataLoader::deleteLater); } DataModel::~DataModel() = default; int DataModel::rowCount(const QModelIndex &parent) const { auto dataCount = 0; if (parent.isValid()) { return dataCount; } dataCount = d->mAllTrackData.size() + d->mAllAlbumData.size() + d->mAllArtistData.size() + d->mAllGenreData.size(); return dataCount; } QHash DataModel::roleNames() const { auto roles = QAbstractListModel::roleNames(); roles[static_cast(DataTypes::ColumnsRoles::TitleRole)] = "title"; roles[static_cast(DataTypes::ColumnsRoles::SecondaryTextRole)] = "secondaryText"; roles[static_cast(DataTypes::ColumnsRoles::ImageUrlRole)] = "imageUrl"; roles[static_cast(DataTypes::ColumnsRoles::DatabaseIdRole)] = "databaseId"; roles[static_cast(DataTypes::ColumnsRoles::ElementTypeRole)] = "dataType"; roles[static_cast(DataTypes::ColumnsRoles::ResourceRole)] = "url"; roles[static_cast(DataTypes::ColumnsRoles::ArtistRole)] = "artist"; roles[static_cast(DataTypes::ColumnsRoles::AllArtistsRole)] = "allArtists"; roles[static_cast(DataTypes::ColumnsRoles::HighestTrackRating)] = "highestTrackRating"; roles[static_cast(DataTypes::ColumnsRoles::GenreRole)] = "genre"; roles[static_cast(DataTypes::ColumnsRoles::AlbumRole)] = "album"; roles[static_cast(DataTypes::ColumnsRoles::AlbumArtistRole)] = "albumArtist"; roles[static_cast(DataTypes::ColumnsRoles::DurationRole)] = "duration"; roles[static_cast(DataTypes::ColumnsRoles::TrackNumberRole)] = "trackNumber"; roles[static_cast(DataTypes::ColumnsRoles::DiscNumberRole)] = "discNumber"; roles[static_cast(DataTypes::ColumnsRoles::RatingRole)] = "rating"; roles[static_cast(DataTypes::ColumnsRoles::IsSingleDiscAlbumRole)] = "isSingleDiscAlbum"; return roles; } Qt::ItemFlags DataModel::flags(const QModelIndex &index) const { if (!index.isValid()) { return Qt::NoItemFlags; } return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } QVariant DataModel::data(const QModelIndex &index, int role) const { auto result = QVariant(); if (!index.isValid()) { return result; } const auto dataCount = d->mModelType == ElisaUtils::Radio ? d->mAllRadiosData.size() : d->mAllTrackData.size() + d->mAllAlbumData.size() + d->mAllArtistData.size() + d->mAllGenreData.size(); Q_ASSERT(index.isValid()); Q_ASSERT(index.column() == 0); Q_ASSERT(!index.parent().isValid()); Q_ASSERT(index.model() == this); Q_ASSERT(index.internalId() == 0); Q_ASSERT(index.row() >= 0 && index.row() < dataCount); switch(role) { case Qt::DisplayRole: switch(d->mModelType) { case ElisaUtils::Track: result = d->mAllTrackData[index.row()][TrackDataType::key_type::TitleRole]; break; case ElisaUtils::Album: result = d->mAllAlbumData[index.row()][AlbumDataType::key_type::TitleRole]; break; case ElisaUtils::Artist: result = d->mAllArtistData[index.row()][ArtistDataType::key_type::TitleRole]; break; case ElisaUtils::Genre: result = d->mAllGenreData[index.row()][GenreDataType::key_type::TitleRole]; break; case ElisaUtils::Radio: result = d->mAllRadiosData[index.row()][GenreDataType::key_type::TitleRole]; break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } break; case DataTypes::ColumnsRoles::DurationRole: { switch (d->mModelType) { case ElisaUtils::Track: { auto trackDuration = d->mAllTrackData[index.row()][TrackDataType::key_type::DurationRole].toTime(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(); } break; } case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: case ElisaUtils::Radio: case ElisaUtils::Unknown: break; } break; } case DataTypes::ColumnsRoles::IsSingleDiscAlbumRole: { switch (d->mModelType) { case ElisaUtils::Track: result = d->mAllTrackData[index.row()][TrackDataType::key_type::IsSingleDiscAlbumRole]; break; case ElisaUtils::Radio: result = false; break; case ElisaUtils::Album: result = d->mAllAlbumData[index.row()][AlbumDataType::key_type::IsSingleDiscAlbumRole]; break; case ElisaUtils::Artist: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } break; } + case DataTypes::ColumnsRoles::ArtistRole: + { + switch (d->mModelType) + { + case ElisaUtils::Track: + { + auto itArtist = d->mAllTrackData[index.row()].find(TrackDataType::key_type::ArtistRole); + if (itArtist != d->mAllTrackData[index.row()].end()) { + result = d->mAllTrackData[index.row()][TrackDataType::key_type::ArtistRole]; + } else { + result = d->mAllTrackData[index.row()][TrackDataType::key_type::AlbumArtistRole]; + } + break; + } + case ElisaUtils::Album: + result = d->mAllAlbumData[index.row()][static_cast(role)]; + break; + case ElisaUtils::Artist: + result = d->mAllArtistData[index.row()][static_cast(role)]; + break; + case ElisaUtils::Genre: + result = d->mAllGenreData[index.row()][static_cast(role)]; + break; + case ElisaUtils::Radio: + result = d->mAllRadiosData[index.row()][static_cast(role)]; + break; + case ElisaUtils::Lyricist: + case ElisaUtils::Composer: + case ElisaUtils::FileName: + case ElisaUtils::Unknown: + break; + } + break; + } default: switch(d->mModelType) { case ElisaUtils::Track: result = d->mAllTrackData[index.row()][static_cast(role)]; break; case ElisaUtils::Album: result = d->mAllAlbumData[index.row()][static_cast(role)]; break; case ElisaUtils::Artist: result = d->mAllArtistData[index.row()][static_cast(role)]; break; case ElisaUtils::Genre: result = d->mAllGenreData[index.row()][static_cast(role)]; break; case ElisaUtils::Radio: result = d->mAllRadiosData[index.row()][static_cast(role)]; break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } } return result; } QModelIndex DataModel::index(int row, int column, const QModelIndex &parent) const { auto result = QModelIndex(); if (column != 0) { return result; } if (parent.isValid()) { return result; } result = createIndex(row, column); return result; } QModelIndex DataModel::parent(const QModelIndex &child) const { Q_UNUSED(child) auto result = QModelIndex(); return result; } QString DataModel::title() const { return d->mAlbumTitle; } QString DataModel::author() const { return d->mAlbumArtist; } bool DataModel::isBusy() const { return d->mIsBusy; } void DataModel::initialize(MusicListenersManager *manager, DatabaseInterface *database, ElisaUtils::PlayListEntryType modelType, ElisaUtils::FilterType filter, const QString &genre, const QString &artist, qulonglong databaseId) { d->mDatabaseId = databaseId; d->mGenre = genre; d->mArtist = artist; initializeModel(manager, database, modelType, filter); } void DataModel::setBusy(bool value) { if (d->mIsBusy == value) { return; } d->mIsBusy = value; Q_EMIT isBusyChanged(); } void DataModel::initializeModel(MusicListenersManager *manager, DatabaseInterface *database, ElisaUtils::PlayListEntryType modelType, DataModel::FilterType type) { d->mModelType = modelType; d->mFilterType = type; if (manager) { manager->connectModel(d->mDataLoader); } if (manager) { connectModel(manager->viewDatabase()); } else if (database) { connectModel(database); } else { return; } switch(d->mFilterType) { case ElisaUtils::NoFilter: connect(this, &DataModel::needData, d->mDataLoader, &ModelDataLoader::loadData); break; case ElisaUtils::FilterById: connect(this, &DataModel::needDataById, d->mDataLoader, &ModelDataLoader::loadDataByAlbumId); break; case ElisaUtils::FilterByGenre: connect(this, &DataModel::needDataByGenre, d->mDataLoader, &ModelDataLoader::loadDataByGenre); break; case ElisaUtils::FilterByArtist: connect(this, &DataModel::needDataByArtist, d->mDataLoader, &ModelDataLoader::loadDataByArtist); break; case ElisaUtils::FilterByGenreAndArtist: connect(this, &DataModel::needDataByGenreAndArtist, d->mDataLoader, &ModelDataLoader::loadDataByGenreAndArtist); break; case ElisaUtils::FilterByRecentlyPlayed: connect(this, &DataModel::needRecentlyPlayedData, d->mDataLoader, &ModelDataLoader::loadRecentlyPlayedData); break; case ElisaUtils::FilterByFrequentlyPlayed: connect(this, &DataModel::needFrequentlyPlayedData, d->mDataLoader, &ModelDataLoader::loadFrequentlyPlayedData); break; case ElisaUtils::UnknownFilter: break; } setBusy(true); askModelData(); } void DataModel::askModelData() { switch(d->mFilterType) { case ElisaUtils::NoFilter: Q_EMIT needData(d->mModelType); break; case ElisaUtils::FilterById: Q_EMIT needDataById(d->mModelType, d->mDatabaseId); break; case ElisaUtils::FilterByGenre: Q_EMIT needDataByGenre(d->mModelType, d->mGenre); break; case ElisaUtils::FilterByArtist: Q_EMIT needDataByArtist(d->mModelType, d->mArtist); break; case ElisaUtils::FilterByGenreAndArtist: Q_EMIT needDataByGenreAndArtist(d->mModelType, d->mGenre, d->mArtist); break; case ElisaUtils::FilterByRecentlyPlayed: Q_EMIT needRecentlyPlayedData(d->mModelType); break; case ElisaUtils::FilterByFrequentlyPlayed: Q_EMIT needFrequentlyPlayedData(d->mModelType); break; case ElisaUtils::UnknownFilter: break; } } int DataModel::indexFromId(qulonglong id) const { int result; DataModel::ListTrackDataType mAllData = d->mModelType == ElisaUtils::Radio ? d->mAllRadiosData: d->mAllTrackData; for (result = 0; result < mAllData.size(); ++result) { if (mAllData[result].databaseId() == id) { return result; } } result = -1; return result; } void DataModel::connectModel(DatabaseInterface *database) { d->mDataLoader->setDatabase(database); connect(d->mDataLoader, &ModelDataLoader::allTracksData, this, &DataModel::tracksAdded); connect(d->mDataLoader, &ModelDataLoader::allRadiosData, this, &DataModel::radiosAdded); connect(d->mDataLoader, &ModelDataLoader::allAlbumsData, this, &DataModel::albumsAdded); connect(d->mDataLoader, &ModelDataLoader::allArtistsData, this, &DataModel::artistsAdded); connect(d->mDataLoader, &ModelDataLoader::allGenresData, this, &DataModel::genresAdded); connect(d->mDataLoader, &ModelDataLoader::genresAdded, this, &DataModel::genresAdded); connect(d->mDataLoader, &ModelDataLoader::albumsAdded, this, &DataModel::albumsAdded); connect(d->mDataLoader, &ModelDataLoader::albumModified, this, &DataModel::albumModified); connect(d->mDataLoader, &ModelDataLoader::albumRemoved, this, &DataModel::albumRemoved); connect(d->mDataLoader, &ModelDataLoader::tracksAdded, this, &DataModel::tracksAdded); connect(d->mDataLoader, &ModelDataLoader::trackModified, this, &DataModel::trackModified); connect(d->mDataLoader, &ModelDataLoader::trackRemoved, this, &DataModel::trackRemoved); connect(d->mDataLoader, &ModelDataLoader::artistsAdded, this, &DataModel::artistsAdded); connect(d->mDataLoader, &ModelDataLoader::artistRemoved, this, &DataModel::artistRemoved); connect(d->mDataLoader, &ModelDataLoader::radioAdded, this, &DataModel::radioAdded); connect(d->mDataLoader, &ModelDataLoader::radioModified, this, &DataModel::radioModified); connect(d->mDataLoader, &ModelDataLoader::radioRemoved, this, &DataModel::radioRemoved); connect(d->mDataLoader, &ModelDataLoader::clearedDatabase, this, &DataModel::cleanedDatabase); } void DataModel::tracksAdded(ListTrackDataType newData) { if (newData.isEmpty() && d->mModelType == ElisaUtils::Track) { setBusy(false); } if (newData.isEmpty() || d->mModelType != ElisaUtils::Track) { return; } if (d->mFilterType == ElisaUtils::FilterById && !d->mAllTrackData.isEmpty()) { for (const auto &newTrack : newData) { auto trackIndex = indexFromId(newTrack.databaseId()); if (trackIndex != -1) { continue; } bool trackInserted = false; for (int trackIndex = 0; trackIndex < d->mAllTrackData.count(); ++trackIndex) { const auto &oneTrack = d->mAllTrackData[trackIndex]; if (oneTrack.discNumber() >= newTrack.discNumber() && oneTrack.trackNumber() > newTrack.trackNumber()) { beginInsertRows({}, trackIndex, trackIndex); d->mAllTrackData.insert(trackIndex, newTrack); endInsertRows(); if (d->mAllTrackData.size() == 1) { setBusy(false); } trackInserted = true; break; } } if (!trackInserted) { beginInsertRows({}, d->mAllTrackData.count(), d->mAllTrackData.count()); d->mAllTrackData.insert(d->mAllTrackData.count(), newTrack); endInsertRows(); if (d->mAllTrackData.size() == 1) { setBusy(false); } } } } else { if (d->mAllTrackData.isEmpty()) { beginInsertRows({}, 0, newData.size() - 1); d->mAllTrackData.swap(newData); endInsertRows(); setBusy(false); } else { beginInsertRows({}, d->mAllTrackData.size(), d->mAllTrackData.size() + newData.size() - 1); d->mAllTrackData.append(newData); endInsertRows(); } } } void DataModel::radiosAdded(ListRadioDataType newData) { if (newData.isEmpty() && d->mModelType == ElisaUtils::Radio) { setBusy(false); } if (newData.isEmpty() || d->mModelType != ElisaUtils::Radio) { return; } if (d->mFilterType == ElisaUtils::FilterById && !d->mAllRadiosData.isEmpty()) { for (const auto &newTrack : newData) { auto trackIndex = indexFromId(newTrack.databaseId()); if (trackIndex != -1) { continue; } bool trackInserted = false; for (int trackIndex = 0; trackIndex < d->mAllRadiosData.count(); ++trackIndex) { const auto &oneTrack = d->mAllRadiosData[trackIndex]; if (oneTrack.trackNumber() > newTrack.trackNumber()) { beginInsertRows({}, trackIndex, trackIndex); d->mAllRadiosData.insert(trackIndex, newTrack); endInsertRows(); if (d->mAllRadiosData.size() == 1) { setBusy(false); } trackInserted = true; break; } } if (!trackInserted) { beginInsertRows({}, d->mAllRadiosData.count(), d->mAllRadiosData.count()); d->mAllRadiosData.insert(d->mAllRadiosData.count(), newTrack); endInsertRows(); if (d->mAllRadiosData.size() == 1) { setBusy(false); } } } } else { if (d->mAllRadiosData.isEmpty()) { beginInsertRows({}, 0, newData.size() - 1); d->mAllRadiosData.swap(newData); endInsertRows(); setBusy(false); } else { beginInsertRows({}, d->mAllRadiosData.size(), d->mAllRadiosData.size() + newData.size() - 1); d->mAllRadiosData.append(newData); endInsertRows(); } } } void DataModel::trackModified(const TrackDataType &modifiedTrack) { if (d->mModelType != ElisaUtils::Track) { return; } if (!d->mAlbumTitle.isEmpty() && !d->mAlbumArtist.isEmpty()) { if (modifiedTrack.album() != d->mAlbumTitle) { return; } auto trackIndex = indexFromId(modifiedTrack.databaseId()); if (trackIndex == -1) { return; } d->mAllTrackData[trackIndex] = modifiedTrack; Q_EMIT dataChanged(index(trackIndex, 0), index(trackIndex, 0)); } else { auto itTrack = std::find_if(d->mAllTrackData.begin(), d->mAllTrackData.end(), [modifiedTrack](auto track) { return track.databaseId() == modifiedTrack.databaseId(); }); if (itTrack == d->mAllTrackData.end()) { return; } auto position = itTrack - d->mAllTrackData.begin(); d->mAllTrackData[position] = modifiedTrack; Q_EMIT dataChanged(index(position, 0), index(position, 0)); } } void DataModel::radioModified(const TrackDataType &modifiedRadio) { if (d->mModelType != ElisaUtils::Radio) { return; } auto trackIndex = indexFromId(modifiedRadio.databaseId()); if (trackIndex == -1) { return; } d->mAllRadiosData[trackIndex] = modifiedRadio; Q_EMIT dataChanged(index(trackIndex, 0), index(trackIndex, 0)); } void DataModel::trackRemoved(qulonglong removedTrackId) { if (d->mModelType != ElisaUtils::Track) { return; } if (!d->mAlbumTitle.isEmpty() && !d->mAlbumArtist.isEmpty()) { auto trackIndex = indexFromId(removedTrackId); if (trackIndex == -1) { return; } beginRemoveRows({}, trackIndex, trackIndex); d->mAllTrackData.removeAt(trackIndex); endRemoveRows(); } else { auto itTrack = std::find_if(d->mAllTrackData.begin(), d->mAllTrackData.end(), [removedTrackId](auto track) {return track.databaseId() == removedTrackId;}); if (itTrack == d->mAllTrackData.end()) { return; } auto position = itTrack - d->mAllTrackData.begin(); beginRemoveRows({}, position, position); d->mAllTrackData.erase(itTrack); endRemoveRows(); } } void DataModel::radioRemoved(qulonglong removedRadioId) { if (d->mModelType != ElisaUtils::Radio) { return; } auto itRadio = std::find_if(d->mAllRadiosData.begin(), d->mAllRadiosData.end(), [removedRadioId](auto track) {return track.databaseId() == removedRadioId;}); if (itRadio == d->mAllRadiosData.end()) { return; } auto position = itRadio - d->mAllRadiosData.begin(); beginRemoveRows({}, position, position); d->mAllRadiosData.erase(itRadio); endRemoveRows(); } void DataModel::radioAdded(const DataModel::TrackDataType &radioData) { if (d->mModelType != ElisaUtils::Radio) { return; } ListRadioDataType list; list.append(radioData); radiosAdded(list); } void DataModel::removeRadios() { if (d->mModelType != ElisaUtils::Radio) { return; } beginRemoveRows({}, 0, d->mAllRadiosData.size()); d->mAllRadiosData.clear(); endRemoveRows(); } void DataModel::genresAdded(DataModel::ListGenreDataType newData) { if (newData.isEmpty() && d->mModelType == ElisaUtils::Genre) { setBusy(false); } if (newData.isEmpty() || d->mModelType != ElisaUtils::Genre) { return; } if (d->mAllGenreData.isEmpty()) { beginInsertRows({}, d->mAllGenreData.size(), newData.size() - 1); d->mAllGenreData.swap(newData); endInsertRows(); setBusy(false); } else { beginInsertRows({}, d->mAllGenreData.size(), d->mAllGenreData.size() + newData.size() - 1); d->mAllGenreData.append(newData); endInsertRows(); } } void DataModel::artistsAdded(DataModel::ListArtistDataType newData) { if (newData.isEmpty() && d->mModelType == ElisaUtils::Artist) { setBusy(false); } if (newData.isEmpty() || d->mModelType != ElisaUtils::Artist) { return; } if (d->mAllArtistData.isEmpty()) { beginInsertRows({}, d->mAllArtistData.size(), newData.size() - 1); d->mAllArtistData.swap(newData); endInsertRows(); setBusy(false); } else { beginInsertRows({}, d->mAllArtistData.size(), d->mAllArtistData.size() + newData.size() - 1); d->mAllArtistData.append(newData); endInsertRows(); } } void DataModel::artistRemoved(qulonglong removedDatabaseId) { if (d->mModelType != ElisaUtils::Artist) { return; } auto removedDataIterator = d->mAllArtistData.end(); removedDataIterator = std::find_if(d->mAllArtistData.begin(), d->mAllArtistData.end(), [removedDatabaseId](auto album) {return album.databaseId() == removedDatabaseId;}); if (removedDataIterator == d->mAllArtistData.end()) { return; } int dataIndex = removedDataIterator - d->mAllArtistData.begin(); beginRemoveRows({}, dataIndex, dataIndex); d->mAllArtistData.erase(removedDataIterator); endRemoveRows(); } void DataModel::albumsAdded(DataModel::ListAlbumDataType newData) { if (newData.isEmpty() && d->mModelType == ElisaUtils::Album) { setBusy(false); } if (newData.isEmpty() || d->mModelType != ElisaUtils::Album) { return; } if (d->mAllAlbumData.isEmpty()) { beginInsertRows({}, d->mAllAlbumData.size(), newData.size() - 1); d->mAllAlbumData.swap(newData); endInsertRows(); setBusy(false); } else { beginInsertRows({}, d->mAllAlbumData.size(), d->mAllAlbumData.size() + newData.size() - 1); d->mAllAlbumData.append(newData); endInsertRows(); } } void DataModel::albumRemoved(qulonglong removedDatabaseId) { if (d->mModelType != ElisaUtils::Album) { return; } auto removedDataIterator = d->mAllAlbumData.end(); removedDataIterator = std::find_if(d->mAllAlbumData.begin(), d->mAllAlbumData.end(), [removedDatabaseId](auto album) {return album.databaseId() == removedDatabaseId;}); if (removedDataIterator == d->mAllAlbumData.end()) { return; } int dataIndex = removedDataIterator - d->mAllAlbumData.begin(); beginRemoveRows({}, dataIndex, dataIndex); d->mAllAlbumData.erase(removedDataIterator); endRemoveRows(); } void DataModel::albumModified(const DataModel::AlbumDataType &modifiedAlbum) { if (d->mModelType != ElisaUtils::Album) { return; } auto modifiedAlbumIterator = std::find_if(d->mAllAlbumData.begin(), d->mAllAlbumData.end(), [modifiedAlbum](auto album) { return album.databaseId() == modifiedAlbum.databaseId(); }); if (modifiedAlbumIterator == d->mAllAlbumData.end()) { return; } auto albumIndex = modifiedAlbumIterator - d->mAllAlbumData.begin(); Q_EMIT dataChanged(index(albumIndex, 0), index(albumIndex, 0)); } void DataModel::cleanedDatabase() { beginResetModel(); d->mAllAlbumData.clear(); d->mAllGenreData.clear(); d->mAllTrackData.clear(); d->mAllArtistData.clear(); endResetModel(); } #include "moc_datamodel.cpp" diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp index 6fac2092..aa6c0f33 100644 --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -1,556 +1,554 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "musiclistenersmanager.h" #include "config-upnp-qt.h" #include "indexersManager.h" #if defined UPNPQT_FOUND && UPNPQT_FOUND #include "upnp/upnplistener.h" #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND #include "baloo/baloolistener.h" #include "baloo/baloodetector.h" #endif #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND #include "android/androidmusiclistener.h" #endif #include "databaseinterface.h" #include "mediaplaylist.h" #include "file/filelistener.h" #include "file/localfilelisting.h" #include "trackslistener.h" #include "elisaapplication.h" #include "elisa_settings.h" #include "modeldataloader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class MusicListenersManagerPrivate { public: QThread mDatabaseThread; QThread mListenerThread; #if defined UPNPQT_FOUND && UPNPQT_FOUND UpnpListener mUpnpListener; #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND BalooDetector mBalooDetector; BalooListener mBalooListener; #endif FileListener mFileListener; #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND std::unique_ptr mAndroidMusicListener; #endif DatabaseInterface mDatabaseInterface; std::unique_ptr mTracksListener; QFileSystemWatcher mConfigFileWatcher; ElisaApplication *mElisaApplication = nullptr; int mImportedTracksCount = 0; bool mIndexerBusy = false; bool mFileSystemIndexerActive = false; bool mBalooIndexerActive = false; bool mBalooIndexerAvailable = false; bool mAndroidIndexerActive = false; bool mAndroidIndexerAvailable = false; }; MusicListenersManager::MusicListenersManager(QObject *parent) : QObject(parent), d(std::make_unique()) { connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, this, &MusicListenersManager::increaseImportedTracksCount); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND connect(&d->mBalooDetector, &BalooDetector::balooAvailabilityChanged, this, &MusicListenersManager::balooAvailabilityChanged); #endif connect(&d->mDatabaseInterface, &DatabaseInterface::requestsInitDone, this, &MusicListenersManager::databaseReady); connect(this, &MusicListenersManager::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::clearData); connect(&d->mDatabaseInterface, &DatabaseInterface::cleanedDatabase, this, &MusicListenersManager::cleanedDatabase); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &MusicListenersManager::applicationAboutToQuit); connect(&d->mConfigFileWatcher, &QFileSystemWatcher::fileChanged, this, &MusicListenersManager::configChanged); d->mListenerThread.start(); d->mDatabaseThread.start(); d->mDatabaseInterface.moveToThread(&d->mDatabaseThread); const auto &localDataPaths = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); auto databaseFileName = QString(); if (!localDataPaths.isEmpty()) { QDir myDataDirectory; myDataDirectory.mkpath(localDataPaths.first()); databaseFileName = localDataPaths.first() + QStringLiteral("/elisaDatabase.db"); } QMetaObject::invokeMethod(&d->mDatabaseInterface, "init", Qt::QueuedConnection, Q_ARG(QString, QStringLiteral("listeners")), Q_ARG(QString, databaseFileName)); qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is inactive"; qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable"; qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive"; } MusicListenersManager::~MusicListenersManager() = default; DatabaseInterface *MusicListenersManager::viewDatabase() const { return &d->mDatabaseInterface; } void MusicListenersManager::subscribeForTracks(MediaPlayList *client) { createTracksListener(); connect(d->mTracksListener.get(), &TracksListener::trackHasChanged, client, &MediaPlayList::trackChanged); connect(d->mTracksListener.get(), &TracksListener::trackHasBeenRemoved, client, &MediaPlayList::trackRemoved); connect(d->mTracksListener.get(), &TracksListener::tracksListAdded, client, &MediaPlayList::tracksListAdded); connect(client, &MediaPlayList::newEntryInList, d->mTracksListener.get(), &TracksListener::newEntryInList); connect(client, &MediaPlayList::newUrlInList, d->mTracksListener.get(), &TracksListener::newUrlInList); connect(client, &MediaPlayList::newTrackByNameInList, d->mTracksListener.get(), &TracksListener::trackByNameInList); } int MusicListenersManager::importedTracksCount() const { return d->mImportedTracksCount; } ElisaApplication *MusicListenersManager::elisaApplication() const { return d->mElisaApplication; } TracksListener *MusicListenersManager::tracksListener() const { return d->mTracksListener.get(); } bool MusicListenersManager::indexerBusy() const { return d->mIndexerBusy; } bool MusicListenersManager::fileSystemIndexerActive() const { return d->mFileSystemIndexerActive; } bool MusicListenersManager::balooIndexerActive() const { return d->mBalooIndexerActive; } bool MusicListenersManager::balooIndexerAvailable() const { return d->mBalooIndexerAvailable; } bool MusicListenersManager::androidIndexerActive() const { return d->mAndroidIndexerActive; } bool MusicListenersManager::androidIndexerAvailable() const { return d->mAndroidIndexerAvailable; } auto MusicListenersManager::initializeRootPath() { auto initialRootPath = QStringList{}; auto systemMusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation); for (const auto &musicPath : qAsConst(systemMusicPaths)) { initialRootPath.push_back(musicPath); } Elisa::ElisaConfiguration::setRootPath(initialRootPath); Elisa::ElisaConfiguration::self()->save(); return initialRootPath; } void MusicListenersManager::databaseReady() { auto initialRootPath = Elisa::ElisaConfiguration::rootPath(); if (initialRootPath.isEmpty()) { initializeRootPath(); } d->mConfigFileWatcher.addPath(Elisa::ElisaConfiguration::self()->config()->name()); configChanged(); } void MusicListenersManager::applicationAboutToQuit() { d->mDatabaseInterface.applicationAboutToQuit(); Q_EMIT applicationIsTerminating(); d->mDatabaseThread.exit(); d->mDatabaseThread.wait(); d->mListenerThread.exit(); d->mListenerThread.wait(); } void MusicListenersManager::showConfiguration() { auto configureAction = d->mElisaApplication->action(QStringLiteral("options_configure")); configureAction->trigger(); } void MusicListenersManager::setElisaApplication(ElisaApplication *elisaApplication) { if (d->mElisaApplication == elisaApplication) { return; } d->mElisaApplication = elisaApplication; emit elisaApplicationChanged(); } void MusicListenersManager::playBackError(const QUrl &sourceInError, QMediaPlayer::Error playerError) { qCDebug(orgKdeElisaIndexersManager) << "MusicListenersManager::playBackError" << sourceInError; if (playerError == QMediaPlayer::ResourceError) { Q_EMIT removeTracksInError({sourceInError}); if (sourceInError.isLocalFile()) { Q_EMIT displayTrackError(sourceInError.toLocalFile()); } else { Q_EMIT displayTrackError(sourceInError.toString()); } } } void MusicListenersManager::deleteElementById(ElisaUtils::PlayListEntryType entryType, qulonglong databaseId) { switch(entryType) { case ElisaUtils::Radio: QMetaObject::invokeMethod(&d->mDatabaseInterface, "removeRadio", Qt::QueuedConnection, Q_ARG(qulonglong, databaseId)); break; case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::Track: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } } void MusicListenersManager::connectModel(ModelDataLoader *dataLoader) { dataLoader->moveToThread(&d->mDatabaseThread); } void MusicListenersManager::resetMusicData() { Q_EMIT clearDatabase(); } void MusicListenersManager::configChanged() { auto currentConfiguration = Elisa::ElisaConfiguration::self(); d->mConfigFileWatcher.addPath(currentConfiguration->config()->name()); currentConfiguration->load(); currentConfiguration->read(); //resolve symlinks QStringList allRootPaths; auto inputRootPath = currentConfiguration->rootPath(); for (const auto &onePath : inputRootPath) { auto workPath = onePath; if (workPath.startsWith(QLatin1String("file:/"))) { auto urlPath = QUrl{workPath}; workPath = urlPath.toLocalFile(); } QFileInfo pathFileInfo(workPath); auto directoryPath = pathFileInfo.canonicalFilePath(); if (!directoryPath.isEmpty()) { if (directoryPath.rightRef(1) != QLatin1Char('/')) { directoryPath.append(QLatin1Char('/')); } allRootPaths.push_back(directoryPath); } } if (allRootPaths.isEmpty()) { allRootPaths = initializeRootPath(); } d->mFileListener.setAllRootPaths(allRootPaths); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.setAllRootPaths(allRootPaths); #endif if (!d->mBalooIndexerActive && !d->mFileSystemIndexerActive) { testBalooIndexerAvailability(); } #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (d->mBalooIndexerAvailable && !d->mBalooIndexerActive && d->mBalooListener.canHandleRootPaths()) { qCDebug(orgKdeElisaIndexersManager()) << "trigger start of baloo file indexer"; QMetaObject::invokeMethod(d->mFileListener.fileListing(), "stop", Qt::BlockingQueuedConnection); d->mFileSystemIndexerActive = false; startBalooIndexing(); } else if (!d->mFileSystemIndexerActive && d->mBalooIndexerActive && !d->mBalooListener.canHandleRootPaths()) { qCDebug(orgKdeElisaIndexersManager()) << "trigger stop of baloo file indexer"; QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "stop", Qt::BlockingQueuedConnection); d->mBalooIndexerActive = false; startLocalFileSystemIndexing(); } #endif if (d->mBalooIndexerActive) { qCInfo(orgKdeElisaIndexersManager()) << "trigger init of baloo file indexer"; #if defined KF5Baloo_FOUND && KF5Baloo_FOUND QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection); #endif } else if (d->mFileSystemIndexerActive) { qCInfo(orgKdeElisaIndexersManager()) << "trigger init of local file indexer"; QMetaObject::invokeMethod(d->mFileListener.fileListing(), "init", Qt::QueuedConnection); } #if defined UPNPQT_FOUND && UPNPQT_FOUND d->mUpnpListener.setDatabaseInterface(&d->mDatabaseInterface); d->mUpnpListener.moveToThread(&d->mDatabaseThread); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mUpnpListener, &UpnpListener::applicationAboutToQuit, Qt::DirectConnection); #endif #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND if (!d->mAndroidMusicListener) { d->mAndroidMusicListener = std::make_unique(); d->mAndroidMusicListener->moveToThread(&d->mListenerThread); d->mAndroidMusicListener->setDatabaseInterface(&d->mDatabaseInterface); connect(this, &MusicListenersManager::applicationIsTerminating, d->mAndroidMusicListener.get(), &AndroidMusicListener::applicationAboutToQuit, Qt::DirectConnection); connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); - connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::clearDatabase, - &d->mDatabaseInterface, &DatabaseInterface::removeAllTracksFromSource); } #endif } void MusicListenersManager::increaseImportedTracksCount(const DataTypes::ListTrackDataType &allTracks) { d->mImportedTracksCount += allTracks.size(); Q_EMIT importedTracksCountChanged(); } void MusicListenersManager::decreaseImportedTracksCount() { --d->mImportedTracksCount; Q_EMIT importedTracksCountChanged(); } void MusicListenersManager::monitorStartingListeners() { d->mIndexerBusy = true; Q_EMIT indexerBusyChanged(); } void MusicListenersManager::monitorEndingListeners() { d->mIndexerBusy = false; Q_EMIT indexerBusyChanged(); } void MusicListenersManager::cleanedDatabase() { d->mImportedTracksCount = 0; Q_EMIT importedTracksCountChanged(); Q_EMIT clearedDatabase(); } void MusicListenersManager::balooAvailabilityChanged() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (!d->mBalooDetector.balooAvailability() || !d->mBalooListener.canHandleRootPaths()) { if (d->mBalooDetector.balooAvailability()) { qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is available"; d->mBalooIndexerAvailable = true; } #else if (true) { #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (!d->mBalooListener.canHandleRootPaths() && d->mBalooDetector.balooAvailability()) { qCInfo(orgKdeElisaIndexersManager()) << "Baloo cannot handle all configured paths: falling back to plain filex indexer"; } #endif if (!d->mFileSystemIndexerActive) { startLocalFileSystemIndexing(); } return; } qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is available"; d->mBalooIndexerAvailable = true; Q_EMIT balooIndexerAvailableChanged(); startBalooIndexing(); } void MusicListenersManager::testBalooIndexerAvailability() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooDetector.checkBalooAvailability(); #else qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable"; d->mBalooIndexerAvailable = false; Q_EMIT balooIndexerAvailableChanged(); qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive"; d->mBalooIndexerActive = false; Q_EMIT balooIndexerActiveChanged(); startLocalFileSystemIndexing(); #endif } void MusicListenersManager::startLocalFileSystemIndexing() { if (d->mFileSystemIndexerActive) { return; } d->mFileListener.setDatabaseInterface(&d->mDatabaseInterface); d->mFileListener.moveToThread(&d->mListenerThread); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mFileListener, &FileListener::applicationAboutToQuit, Qt::DirectConnection); connect(&d->mFileListener, &FileListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(&d->mFileListener, &FileListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is active"; d->mFileSystemIndexerActive = true; Q_EMIT fileSystemIndexerActiveChanged(); } void MusicListenersManager::startBalooIndexing() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.moveToThread(&d->mListenerThread); d->mBalooListener.setDatabaseInterface(&d->mDatabaseInterface); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mBalooListener, &BalooListener::applicationAboutToQuit, Qt::DirectConnection); connect(&d->mBalooListener, &BalooListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(&d->mBalooListener, &BalooListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); connect(&d->mBalooListener, &BalooListener::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::clearData); qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is active"; d->mBalooIndexerActive = true; Q_EMIT balooIndexerActiveChanged(); #endif } void MusicListenersManager::createTracksListener() { if (!d->mTracksListener) { d->mTracksListener = std::make_unique(&d->mDatabaseInterface); d->mTracksListener->moveToThread(&d->mDatabaseThread); connect(this, &MusicListenersManager::removeTracksInError, &d->mDatabaseInterface, &DatabaseInterface::removeTracksList); connect(&d->mDatabaseInterface, &DatabaseInterface::trackRemoved, d->mTracksListener.get(), &TracksListener::trackRemoved); connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, d->mTracksListener.get(), &TracksListener::tracksAdded); connect(&d->mDatabaseInterface, &DatabaseInterface::trackModified, d->mTracksListener.get(), &TracksListener::trackModified); Q_EMIT tracksListenerChanged(); } } #include "moc_musiclistenersmanager.cpp"