diff --git a/src/modeldataloader.cpp b/src/modeldataloader.cpp index 5e2dedb2..14a8c93b 100644 --- a/src/modeldataloader.cpp +++ b/src/modeldataloader.cpp @@ -1,437 +1,439 @@ /* * Copyright 2018 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 "modeldataloader.h" #include "filescanner.h" class ModelDataLoaderPrivate { public: DatabaseInterface *mDatabase = nullptr; ElisaUtils::PlayListEntryType mModelType = ElisaUtils::Unknown; ModelDataLoader::FilterType mFilterType = ModelDataLoader::FilterType::UnknownFilter; QString mArtist; QString mAlbumTitle; QString mAlbumArtist; QString mGenre; qulonglong mDatabaseId = 0; FileScanner mFileScanner; }; ModelDataLoader::ModelDataLoader(QObject *parent) : QObject(parent), d(std::make_unique()) { } ModelDataLoader::~ModelDataLoader() = default; void ModelDataLoader::setDatabase(DatabaseInterface *database) { d->mDatabase = database; connect(database, &DatabaseInterface::genresAdded, this, &ModelDataLoader::genresAdded); connect(database, &DatabaseInterface::albumsAdded, this, &ModelDataLoader::databaseAlbumsAdded); connect(database, &DatabaseInterface::albumModified, this, &ModelDataLoader::albumModified); connect(database, &DatabaseInterface::albumRemoved, this, &ModelDataLoader::albumRemoved); connect(database, &DatabaseInterface::tracksAdded, this, &ModelDataLoader::databaseTracksAdded); connect(database, &DatabaseInterface::trackModified, this, &ModelDataLoader::trackModified); connect(database, &DatabaseInterface::trackRemoved, this, &ModelDataLoader::trackRemoved); connect(database, &DatabaseInterface::artistsAdded, this, &ModelDataLoader::databaseArtistsAdded); connect(database, &DatabaseInterface::artistRemoved, this, &ModelDataLoader::artistRemoved); connect(this, &ModelDataLoader::saveRadioModified, database, &DatabaseInterface::insertRadio); connect(this, &ModelDataLoader::removeRadio, database, &DatabaseInterface::removeRadio); connect(database, &DatabaseInterface::radioAdded, this, &ModelDataLoader::radioAdded); connect(database, &DatabaseInterface::radioModified, this, &ModelDataLoader::radioModified); connect(database, &DatabaseInterface::radioRemoved, this, &ModelDataLoader::radioRemoved); + connect(database, &DatabaseInterface::cleanedDatabase, + this, &ModelDataLoader::clearedDatabase); } void ModelDataLoader::loadData(ElisaUtils::PlayListEntryType dataType) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::NoFilter; switch (dataType) { case ElisaUtils::Album: Q_EMIT allAlbumsData(d->mDatabase->allAlbumsData()); break; case ElisaUtils::Artist: Q_EMIT allArtistsData(d->mDatabase->allArtistsData()); break; case ElisaUtils::Composer: break; case ElisaUtils::Genre: Q_EMIT allGenresData(d->mDatabase->allGenresData()); break; case ElisaUtils::Lyricist: break; case ElisaUtils::Track: Q_EMIT allTracksData(d->mDatabase->allTracksData()); break; case ElisaUtils::FileName: case ElisaUtils::Unknown: break; case ElisaUtils::Radio: Q_EMIT allRadiosData(d->mDatabase->allRadiosData()); break; } } void ModelDataLoader::loadDataByAlbumId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterById; d->mDatabaseId = databaseId; switch (dataType) { case ElisaUtils::Album: break; case ElisaUtils::Artist: break; case ElisaUtils::Composer: break; case ElisaUtils::Genre: break; case ElisaUtils::Lyricist: break; case ElisaUtils::Track: Q_EMIT allTracksData(d->mDatabase->albumData(databaseId)); break; case ElisaUtils::FileName: case ElisaUtils::Unknown: case ElisaUtils::Radio: break; } } void ModelDataLoader::loadDataByGenre(ElisaUtils::PlayListEntryType dataType, const QString &genre) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterByGenre; d->mGenre = genre; switch (dataType) { case ElisaUtils::Artist: Q_EMIT allArtistsData(d->mDatabase->allArtistsDataByGenre(genre)); break; case ElisaUtils::Album: case ElisaUtils::Composer: case ElisaUtils::Track: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: case ElisaUtils::Radio: break; } } void ModelDataLoader::loadDataByArtist(ElisaUtils::PlayListEntryType dataType, const QString &artist) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterByArtist; d->mArtist = artist; switch (dataType) { case ElisaUtils::Album: Q_EMIT allAlbumsData(d->mDatabase->allAlbumsDataByArtist(artist)); break; case ElisaUtils::Artist: case ElisaUtils::Composer: case ElisaUtils::Track: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: case ElisaUtils::Radio: break; } } void ModelDataLoader::loadDataByGenreAndArtist(ElisaUtils::PlayListEntryType dataType, const QString &genre, const QString &artist) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterByGenreAndArtist; d->mArtist = artist; d->mGenre = genre; switch (dataType) { case ElisaUtils::Album: Q_EMIT allAlbumsData(d->mDatabase->allAlbumsDataByGenreAndArtist(genre, artist)); break; case ElisaUtils::Artist: case ElisaUtils::Composer: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::Track: case ElisaUtils::FileName: case ElisaUtils::Unknown: case ElisaUtils::Radio: break; } } void ModelDataLoader::loadDataByDatabaseIdAndUrl(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId, const QUrl &url) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterById; d->mDatabaseId = databaseId; switch (dataType) { case ElisaUtils::Track: Q_EMIT allTrackData(d->mDatabase->trackDataFromDatabaseIdAndUrl(databaseId, url)); break; case ElisaUtils::Radio: Q_EMIT allRadioData(d->mDatabase->radioDataFromDatabaseId(databaseId)); break; case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Composer: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } } void ModelDataLoader::loadDataByUrl(ElisaUtils::PlayListEntryType dataType, const QUrl &url) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::UnknownFilter; switch (dataType) { case ElisaUtils::FileName: { auto databaseId = d->mDatabase->trackIdFromFileName(url); if (databaseId != 0) { Q_EMIT allTrackData(d->mDatabase->trackDataFromDatabaseIdAndUrl(databaseId, url)); } else { auto result = d->mFileScanner.scanOneFile(url); Q_EMIT allTrackData(result); } break; } case ElisaUtils::Track: case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Composer: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::Unknown: case ElisaUtils::Radio: break; } } void ModelDataLoader::loadRecentlyPlayedData(ElisaUtils::PlayListEntryType dataType) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterByRecentlyPlayed; switch (dataType) { case ElisaUtils::Track: Q_EMIT allTracksData(d->mDatabase->recentlyPlayedTracksData(50)); break; case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Composer: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: case ElisaUtils::Radio: break; } } void ModelDataLoader::loadFrequentlyPlayedData(ElisaUtils::PlayListEntryType dataType) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterByFrequentlyPlayed; switch (dataType) { case ElisaUtils::Track: Q_EMIT allTracksData(d->mDatabase->frequentlyPlayedTracksData(50)); break; case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Composer: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: case ElisaUtils::Radio: break; } } void ModelDataLoader::databaseTracksAdded(const ListTrackDataType &newData) { switch(d->mFilterType) { case ModelDataLoader::FilterType::NoFilter: Q_EMIT tracksAdded(newData); break; case ModelDataLoader::FilterType::FilterById: { auto filteredData = newData; auto new_end = std::remove_if(filteredData.begin(), filteredData.end(), [&](const auto &oneTrack) { return oneTrack.albumId() != d->mDatabaseId; }); filteredData.erase(new_end, filteredData.end()); Q_EMIT tracksAdded(filteredData); break; } case ModelDataLoader::FilterType::FilterByGenre: case ModelDataLoader::FilterType::FilterByGenreAndArtist: case ModelDataLoader::FilterType::FilterByArtist: case ModelDataLoader::FilterType::FilterByRecentlyPlayed: case ModelDataLoader::FilterType::FilterByFrequentlyPlayed: case ModelDataLoader::FilterType::UnknownFilter: break; } } void ModelDataLoader::databaseArtistsAdded(const ListArtistDataType &newData) { switch(d->mFilterType) { case ModelDataLoader::FilterType::FilterByGenre: { auto filteredData = newData; auto new_end = std::remove_if(filteredData.begin(), filteredData.end(), [&](const auto &oneArtist){return !d->mDatabase->internalArtistMatchGenre(oneArtist.databaseId(), d->mGenre);}); filteredData.erase(new_end, filteredData.end()); Q_EMIT artistsAdded(filteredData); break; } case ModelDataLoader::FilterType::NoFilter: Q_EMIT artistsAdded(newData); break; case ModelDataLoader::FilterType::FilterByGenreAndArtist: case ModelDataLoader::FilterType::FilterByArtist: case ModelDataLoader::FilterType::FilterById: case ModelDataLoader::FilterType::FilterByRecentlyPlayed: case ModelDataLoader::FilterType::FilterByFrequentlyPlayed: case ModelDataLoader::FilterType::UnknownFilter: break; } } void ModelDataLoader::databaseAlbumsAdded(const ListAlbumDataType &newData) { switch(d->mFilterType) { case ModelDataLoader::FilterType::FilterByArtist: { auto filteredData = newData; auto new_end = std::remove_if(filteredData.begin(), filteredData.end(), [&](const auto &oneAlbum){return oneAlbum.artist() != d->mArtist;}); filteredData.erase(new_end, filteredData.end()); Q_EMIT albumsAdded(filteredData); break; } case ModelDataLoader::FilterType::NoFilter: Q_EMIT albumsAdded(newData); break; case ModelDataLoader::FilterType::FilterByGenreAndArtist: { auto filteredData = newData; auto new_end = std::remove_if(filteredData.begin(), filteredData.end(), [&](const auto &oneAlbum){ const auto &allGenres = oneAlbum.genres(); return oneAlbum.artist() != d->mArtist || !allGenres.contains(d->mGenre); }); filteredData.erase(new_end, filteredData.end()); Q_EMIT albumsAdded(filteredData); break; } case ModelDataLoader::FilterType::FilterByGenre: case ModelDataLoader::FilterType::FilterById: case ModelDataLoader::FilterType::FilterByRecentlyPlayed: case ModelDataLoader::FilterType::FilterByFrequentlyPlayed: case ModelDataLoader::FilterType::UnknownFilter: break; } } #include "moc_modeldataloader.cpp" diff --git a/src/modeldataloader.h b/src/modeldataloader.h index e4d1f01c..f047cb20 100644 --- a/src/modeldataloader.h +++ b/src/modeldataloader.h @@ -1,140 +1,142 @@ /* * Copyright 2018 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 . */ #ifndef MODELDATALOADER_H #define MODELDATALOADER_H #include "elisaLib_export.h" #include "elisautils.h" #include "databaseinterface.h" #include "datatypes.h" #include "models/datamodel.h" #include #include class ModelDataLoaderPrivate; class ELISALIB_EXPORT ModelDataLoader : public QObject { Q_OBJECT public: using ListAlbumDataType = DataTypes::ListAlbumDataType; using ListArtistDataType = DataTypes::ListArtistDataType; using ListGenreDataType = DataTypes::ListGenreDataType; using ListTrackDataType = DataTypes::ListTrackDataType; using ListRadioDataType = DataTypes::ListRadioDataType; using TrackDataType = DataTypes::TrackDataType; using AlbumDataType = DataTypes::AlbumDataType; using FilterType = ElisaUtils::FilterType; explicit ModelDataLoader(QObject *parent = nullptr); ~ModelDataLoader() override; void setDatabase(DatabaseInterface *database); Q_SIGNALS: void allAlbumsData(const ModelDataLoader::ListAlbumDataType &allData); void allArtistsData(const ModelDataLoader::ListArtistDataType &allData); void allGenresData(const ModelDataLoader::ListGenreDataType &allData); void allTracksData(const ModelDataLoader::ListTrackDataType &allData); void allRadiosData(const ModelDataLoader::ListRadioDataType &radiosData); void radioAdded(const ModelDataLoader::TrackDataType &radiosData); void radioModified(const ModelDataLoader::TrackDataType &radiosData); void allTrackData(const ModelDataLoader::TrackDataType &allData); void allRadioData(const ModelDataLoader::TrackDataType &allData); void tracksAdded(const ModelDataLoader::ListTrackDataType &newData); void trackModified(const ModelDataLoader::TrackDataType &modifiedTrack); void trackRemoved(qulonglong removedTrackId); void genresAdded(const ModelDataLoader::ListGenreDataType &newData); void artistsAdded(const ModelDataLoader::ListArtistDataType &newData); void artistRemoved(qulonglong removedDatabaseId); void albumsAdded(const ModelDataLoader::ListAlbumDataType &newData); void albumRemoved(qulonglong removedDatabaseId); void albumModified(const ModelDataLoader::AlbumDataType &modifiedAlbum); void saveRadioModified(const ModelDataLoader::TrackDataType &trackDataType); void removeRadio(qulonglong radioId); void radioRemoved(qulonglong radioId); + void clearedDatabase(); + public Q_SLOTS: void loadData(ElisaUtils::PlayListEntryType dataType); void loadDataByAlbumId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId); void loadDataByGenre(ElisaUtils::PlayListEntryType dataType, const QString &genre); void loadDataByArtist(ElisaUtils::PlayListEntryType dataType, const QString &artist); void loadDataByGenreAndArtist(ElisaUtils::PlayListEntryType dataType, const QString &genre, const QString &artist); void loadDataByDatabaseIdAndUrl(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId, const QUrl &url); void loadDataByUrl(ElisaUtils::PlayListEntryType dataType, const QUrl &url); void loadRecentlyPlayedData(ElisaUtils::PlayListEntryType dataType); void loadFrequentlyPlayedData(ElisaUtils::PlayListEntryType dataType); private Q_SLOTS: void databaseTracksAdded(const ListTrackDataType &newData); void databaseArtistsAdded(const ListArtistDataType &newData); void databaseAlbumsAdded(const ListAlbumDataType &newData); private: std::unique_ptr d; }; #endif // MODELDATALOADER_H diff --git a/src/models/datamodel.cpp b/src/models/datamodel.cpp index 15b3834b..1aca47e4 100644 --- a/src/models/datamodel.cpp +++ b/src/models/datamodel.cpp @@ -1,847 +1,849 @@ /* * 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; } 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 763d9ba5..e0a18e18 100644 --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -1,492 +1,493 @@ /* * 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; } void MusicListenersManager::databaseReady() { auto initialRootPath = Elisa::ElisaConfiguration::rootPath(); if (initialRootPath.isEmpty()) { auto systemMusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation); for (const auto &musicPath : qAsConst(systemMusicPaths)) { initialRootPath.push_back(musicPath); } Elisa::ElisaConfiguration::setRootPath(initialRootPath); Elisa::ElisaConfiguration::self()->save(); } 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(); const auto &allRootPaths = currentConfiguration->rootPath(); d->mFileListener.setAllRootPaths(allRootPaths); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.setAllRootPaths(allRootPaths); #endif if (!d->mBalooIndexerActive && !d->mFileSystemIndexerActive) { testBalooIndexerAvailability(); } if (d->mBalooIndexerActive) { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection); #endif } else if (d->mFileSystemIndexerActive) { 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()) { #else if (true) { #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" diff --git a/src/musiclistenersmanager.h b/src/musiclistenersmanager.h index 2fc59884..27f2629d 100644 --- a/src/musiclistenersmanager.h +++ b/src/musiclistenersmanager.h @@ -1,190 +1,192 @@ /* * 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 . */ #ifndef MUSICLISTENERSMANAGER_H #define MUSICLISTENERSMANAGER_H #include "elisaLib_export.h" #include "datatypes.h" #include "elisautils.h" #include #include #include class DatabaseInterface; class MusicListenersManagerPrivate; class MediaPlayList; class ElisaApplication; class ModelDataLoader; class TracksListener; class ELISALIB_EXPORT MusicListenersManager : public QObject { Q_OBJECT Q_PROPERTY(DatabaseInterface* viewDatabase READ viewDatabase NOTIFY viewDatabaseChanged) Q_PROPERTY(int importedTracksCount READ importedTracksCount NOTIFY importedTracksCountChanged) Q_PROPERTY(ElisaApplication* elisaApplication READ elisaApplication WRITE setElisaApplication NOTIFY elisaApplicationChanged) Q_PROPERTY(TracksListener* tracksListener READ tracksListener NOTIFY tracksListenerChanged) Q_PROPERTY(bool indexerBusy READ indexerBusy NOTIFY indexerBusyChanged) Q_PROPERTY(bool fileSystemIndexerActive READ fileSystemIndexerActive NOTIFY fileSystemIndexerActiveChanged) Q_PROPERTY(bool balooIndexerActive READ balooIndexerActive NOTIFY balooIndexerActiveChanged) Q_PROPERTY(bool balooIndexerAvailable READ balooIndexerAvailable NOTIFY balooIndexerAvailableChanged) Q_PROPERTY(bool androidIndexerActive READ androidIndexerActive NOTIFY androidIndexerActiveChanged) Q_PROPERTY(bool androidIndexerAvailable READ androidIndexerAvailable NOTIFY androidIndexerAvailableChanged) public: explicit MusicListenersManager(QObject *parent = nullptr); ~MusicListenersManager() override; DatabaseInterface* viewDatabase() const; void subscribeForTracks(MediaPlayList *client); int importedTracksCount() const; ElisaApplication* elisaApplication() const; TracksListener* tracksListener() const; bool indexerBusy() const; bool fileSystemIndexerActive() const; bool balooIndexerActive() const; bool balooIndexerAvailable() const; bool androidIndexerActive() const; bool androidIndexerAvailable() const; Q_SIGNALS: void viewDatabaseChanged(); void applicationIsTerminating(); void tracksListenerChanged(); void importedTracksCountChanged(); void elisaApplicationChanged(); void removeTracksInError(const QList &tracks); void displayTrackError(const QString &fileName); void indexerBusyChanged(); void clearDatabase(); + void clearedDatabase(); + void fileSystemIndexerActiveChanged(); void balooIndexerActiveChanged(); void balooIndexerAvailableChanged(); void androidIndexerActiveChanged(); void androidIndexerAvailableChanged(); public Q_SLOTS: void databaseReady(); void applicationAboutToQuit(); void showConfiguration(); void setElisaApplication(ElisaApplication* elisaApplication); void playBackError(const QUrl &sourceInError, QMediaPlayer::Error playerError); void deleteElementById(ElisaUtils::PlayListEntryType entryType, qulonglong databaseId); void connectModel(ModelDataLoader *dataLoader); void resetMusicData(); private Q_SLOTS: void configChanged(); void increaseImportedTracksCount(const DataTypes::ListTrackDataType &allTracks); void decreaseImportedTracksCount(); void monitorStartingListeners(); void monitorEndingListeners(); void cleanedDatabase(); void balooAvailabilityChanged(); private: void testBalooIndexerAvailability(); void startLocalFileSystemIndexing(); void startBalooIndexing(); std::unique_ptr d; void createTracksListener(); }; #endif // MUSICLISTENERSMANAGER_H