diff --git a/autotests/trackmetadatamodeltest.cpp b/autotests/trackmetadatamodeltest.cpp index edf14f68..3e3d27ed 100644 --- a/autotests/trackmetadatamodeltest.cpp +++ b/autotests/trackmetadatamodeltest.cpp @@ -1,75 +1,148 @@ /* * 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 "models/trackmetadatamodel.h" + #include "qabstractitemmodeltester.h" +#include "databasetestdata.h" #include #include -class TrackMetadataModelTests: public QObject +class TrackMetadataModelTests: public QObject, public DatabaseTestData { Q_OBJECT private Q_SLOTS: void initTestCase() { } void loadOneTrackData() { TrackMetadataModel myModel; QAbstractItemModelTester testModel(&myModel); QSignalSpy beginResetSpy(&myModel, &TrackMetadataModel::modelAboutToBeReset); QSignalSpy endResetSpy(&myModel, &TrackMetadataModel::modelReset); QSignalSpy beginInsertRowsSpy(&myModel, &TrackMetadataModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&myModel, &TrackMetadataModel::rowsInserted); QSignalSpy dataChangedSpy(&myModel, &TrackMetadataModel::dataChanged); QSignalSpy beginRemovedRowsSpy(&myModel, &TrackMetadataModel::rowsAboutToBeRemoved); QSignalSpy endRemovedRowsSpy(&myModel, &TrackMetadataModel::rowsRemoved); QCOMPARE(beginResetSpy.count(), 0); QCOMPARE(endResetSpy.count(), 0); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(beginRemovedRowsSpy.count(), 0); QCOMPARE(endRemovedRowsSpy.count(), 0); QCOMPARE(myModel.rowCount(), 0); auto trackData = TrackMetadataModel::TrackDataType{{DatabaseInterface::DatabaseIdRole, 1}, {DatabaseInterface::TitleRole, QStringLiteral("title")}}; myModel.trackData(trackData); QCOMPARE(beginResetSpy.count(), 1); QCOMPARE(endResetSpy.count(), 1); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(beginRemovedRowsSpy.count(), 0); QCOMPARE(endRemovedRowsSpy.count(), 0); QCOMPARE(myModel.rowCount(), 1); } + + void modifyTrackInDatabase() + { + QTemporaryFile databaseFile; + databaseFile.open(); + + qDebug() << "addOneTrackWithoutAlbumArtist" << databaseFile.fileName(); + + DatabaseInterface musicDb; + + musicDb.init(QStringLiteral("testDb"), databaseFile.fileName()); + + musicDb.insertTracksList(mNewTracks, mNewCovers); + + TrackMetadataModel myModel; + QAbstractItemModelTester testModel(&myModel); + + QSignalSpy beginResetSpy(&myModel, &TrackMetadataModel::modelAboutToBeReset); + QSignalSpy endResetSpy(&myModel, &TrackMetadataModel::modelReset); + QSignalSpy beginInsertRowsSpy(&myModel, &TrackMetadataModel::rowsAboutToBeInserted); + QSignalSpy endInsertRowsSpy(&myModel, &TrackMetadataModel::rowsInserted); + QSignalSpy dataChangedSpy(&myModel, &TrackMetadataModel::dataChanged); + QSignalSpy beginRemovedRowsSpy(&myModel, &TrackMetadataModel::rowsAboutToBeRemoved); + QSignalSpy endRemovedRowsSpy(&myModel, &TrackMetadataModel::rowsRemoved); + + QCOMPARE(beginResetSpy.count(), 0); + QCOMPARE(endResetSpy.count(), 0); + QCOMPARE(beginInsertRowsSpy.count(), 0); + QCOMPARE(endInsertRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(beginRemovedRowsSpy.count(), 0); + QCOMPARE(endRemovedRowsSpy.count(), 0); + QCOMPARE(myModel.rowCount(), 0); + + myModel.setDatabase(&musicDb); + + auto trackId = musicDb.trackIdFromFileName(QUrl::fromLocalFile(QStringLiteral("/$1"))); + + myModel.initializeByTrackId(trackId); + + QCOMPARE(beginResetSpy.count(), 1); + QCOMPARE(endResetSpy.count(), 1); + QCOMPARE(beginInsertRowsSpy.count(), 0); + QCOMPARE(endInsertRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(beginRemovedRowsSpy.count(), 0); + QCOMPARE(endRemovedRowsSpy.count(), 0); + QCOMPARE(myModel.rowCount(), 11); + + musicDb.trackHasStartedPlaying(QUrl::fromLocalFile(QStringLiteral("/$2")), QDateTime::currentDateTime()); + + QCOMPARE(beginResetSpy.count(), 1); + QCOMPARE(endResetSpy.count(), 1); + QCOMPARE(beginInsertRowsSpy.count(), 0); + QCOMPARE(endInsertRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(beginRemovedRowsSpy.count(), 0); + QCOMPARE(endRemovedRowsSpy.count(), 0); + QCOMPARE(myModel.rowCount(), 11); + + musicDb.trackHasStartedPlaying(QUrl::fromLocalFile(QStringLiteral("/$1")), QDateTime::currentDateTime()); + + QCOMPARE(beginResetSpy.count(), 2); + QCOMPARE(endResetSpy.count(), 2); + QCOMPARE(beginInsertRowsSpy.count(), 0); + QCOMPARE(endInsertRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(beginRemovedRowsSpy.count(), 0); + QCOMPARE(endRemovedRowsSpy.count(), 0); + QCOMPARE(myModel.rowCount(), 12); + } }; QTEST_GUILESS_MAIN(TrackMetadataModelTests) #include "trackmetadatamodeltest.moc" diff --git a/src/models/trackmetadatamodel.cpp b/src/models/trackmetadatamodel.cpp index 16e6bbb3..6e48ef7d 100644 --- a/src/models/trackmetadatamodel.cpp +++ b/src/models/trackmetadatamodel.cpp @@ -1,334 +1,355 @@ /* * 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 "trackmetadatamodel.h" #include "musiclistenersmanager.h" #include TrackMetadataModel::TrackMetadataModel(QObject *parent) : QAbstractListModel(parent) { } int TrackMetadataModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return mTrackData.count(); } QVariant TrackMetadataModel::data(const QModelIndex &index, int role) const { auto result = QVariant{}; const auto currentKey = mTrackKeys[index.row()]; switch (role) { case Qt::DisplayRole: result = mTrackData[currentKey]; break; case ItemNameRole: switch (currentKey) { case DatabaseInterface::TitleRole: result = i18nc("Track title for track metadata view", "Title"); break; case DatabaseInterface::DurationRole: result = i18nc("Duration label for track metadata view", "Duration"); break; case DatabaseInterface::ArtistRole: result = i18nc("Track artist for track metadata view", "Artist"); break; case DatabaseInterface::AlbumRole: result = i18nc("Album name for track metadata view", "Album"); break; case DatabaseInterface::AlbumArtistRole: result = i18nc("Album artist for track metadata view", "Album Artist"); break; case DatabaseInterface::TrackNumberRole: result = i18nc("Track number for track metadata view", "Track Number"); break; case DatabaseInterface::DiscNumberRole: result = i18nc("Disc number for track metadata view", "Disc Number"); break; case DatabaseInterface::RatingRole: result = i18nc("Rating label for information panel", "Rating"); break; case DatabaseInterface::GenreRole: result = i18nc("Genre label for track metadata view", "Genre"); break; case DatabaseInterface::LyricistRole: result = i18nc("Lyricist label for track metadata view", "Lyricist"); break; case DatabaseInterface::ComposerRole: result = i18nc("Composer name for track metadata view", "Composer"); break; case DatabaseInterface::CommentRole: result = i18nc("Comment label for track metadata view", "Comment"); break; case DatabaseInterface::YearRole: result = i18nc("Year label for track metadata view", "Year"); break; case DatabaseInterface::ChannelsRole: result = i18nc("Channels label for track metadata view", "Channels"); break; case DatabaseInterface::BitRateRole: result = i18nc("Bit rate label for track metadata view", "Bit Rate"); break; case DatabaseInterface::SampleRateRole: result = i18nc("Sample Rate label for track metadata view", "Sample Rate"); break; case DatabaseInterface::LastPlayDate: result = i18nc("Last play date label for track metadata view", "Last played"); break; case DatabaseInterface::PlayCounter: result = i18nc("Play counter label for track metadata view", "Play count"); break; case DatabaseInterface::SecondaryTextRole: case DatabaseInterface::ImageUrlRole: case DatabaseInterface::ShadowForImageRole: case DatabaseInterface::ChildModelRole: case DatabaseInterface::StringDurationRole: case DatabaseInterface::MilliSecondsDurationRole: case DatabaseInterface::AllArtistsRole: case DatabaseInterface::HighestTrackRating: case DatabaseInterface::ResourceRole: case DatabaseInterface::IdRole: case DatabaseInterface::DatabaseIdRole: case DatabaseInterface::IsSingleDiscAlbumRole: case DatabaseInterface::ContainerDataRole: case DatabaseInterface::IsPartialDataRole: case DatabaseInterface::AlbumIdRole: case DatabaseInterface::HasEmbeddedCover: case DatabaseInterface::FileModificationTime: case DatabaseInterface::FirstPlayDate: case DatabaseInterface::PlayFrequency: case DatabaseInterface::ElementTypeRole: break; } break; case ItemTypeRole: switch (currentKey) { case DatabaseInterface::TitleRole: result = TextEntry; break; case DatabaseInterface::ArtistRole: result = TextEntry; break; case DatabaseInterface::AlbumRole: result = TextEntry; break; case DatabaseInterface::AlbumArtistRole: result = TextEntry; break; case DatabaseInterface::TrackNumberRole: result = IntegerEntry; break; case DatabaseInterface::DiscNumberRole: result = IntegerEntry; break; case DatabaseInterface::RatingRole: result = RatingEntry; break; case DatabaseInterface::GenreRole: result = TextEntry; break; case DatabaseInterface::LyricistRole: result = TextEntry; break; case DatabaseInterface::ComposerRole: result = TextEntry; break; case DatabaseInterface::CommentRole: result = TextEntry; break; case DatabaseInterface::YearRole: result = IntegerEntry; break; case DatabaseInterface::LastPlayDate: result = DateEntry; break; case DatabaseInterface::PlayCounter: result = IntegerEntry; break; case DatabaseInterface::DurationRole: case DatabaseInterface::SampleRateRole: case DatabaseInterface::BitRateRole: case DatabaseInterface::ChannelsRole: case DatabaseInterface::SecondaryTextRole: case DatabaseInterface::ImageUrlRole: case DatabaseInterface::ShadowForImageRole: case DatabaseInterface::ChildModelRole: case DatabaseInterface::StringDurationRole: case DatabaseInterface::MilliSecondsDurationRole: case DatabaseInterface::AllArtistsRole: case DatabaseInterface::HighestTrackRating: case DatabaseInterface::ResourceRole: case DatabaseInterface::IdRole: case DatabaseInterface::DatabaseIdRole: case DatabaseInterface::IsSingleDiscAlbumRole: case DatabaseInterface::ContainerDataRole: case DatabaseInterface::IsPartialDataRole: case DatabaseInterface::AlbumIdRole: case DatabaseInterface::HasEmbeddedCover: case DatabaseInterface::FileModificationTime: case DatabaseInterface::FirstPlayDate: case DatabaseInterface::PlayFrequency: case DatabaseInterface::ElementTypeRole: break; } break; } return result; } bool TrackMetadataModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (data(index, role) != value) { // FIXME: Implement me! emit dataChanged(index, index, QVector() << role); return true; } return false; } QHash TrackMetadataModel::roleNames() const { auto names = QAbstractListModel::roleNames(); names[ItemNameRole] = "name"; names[ItemTypeRole] = "type"; return names; } QString TrackMetadataModel::fileUrl() const { return mFileUrl; } const QUrl &TrackMetadataModel::coverUrl() const { return mCoverImage; } MusicListenersManager *TrackMetadataModel::manager() const { return mManager; } void TrackMetadataModel::trackData(const TrackMetadataModel::TrackDataType &trackData) { + if (!mFullData.isEmpty() && trackData.databaseId() != mFullData.databaseId()) { + return; + } + fillDataFromTrackData(trackData); } void TrackMetadataModel::fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData) { beginResetModel(); mFullData = trackData; mTrackData.clear(); mTrackKeys.clear(); for (auto role : {DatabaseInterface::TitleRole, DatabaseInterface::ArtistRole, DatabaseInterface::AlbumRole, DatabaseInterface::AlbumArtistRole, DatabaseInterface::TrackNumberRole, DatabaseInterface::DiscNumberRole, DatabaseInterface::RatingRole, DatabaseInterface::GenreRole, DatabaseInterface::LyricistRole, DatabaseInterface::ComposerRole, DatabaseInterface::CommentRole, DatabaseInterface::YearRole, DatabaseInterface::LastPlayDate, DatabaseInterface::PlayCounter}) { if (trackData.constFind(role) != trackData.constEnd()) { if (role == DatabaseInterface::RatingRole) { if (trackData[role].toInt() == 0) { continue; } } mTrackKeys.push_back(role); mTrackData[role] = trackData[role]; } } filterDataFromTrackData(); endResetModel(); mCoverImage = trackData[DatabaseInterface::ImageUrlRole].toUrl(); Q_EMIT coverUrlChanged(); auto rawFileUrl = trackData[DatabaseInterface::ResourceRole].toUrl(); if (rawFileUrl.isLocalFile()) { mFileUrl = rawFileUrl.toLocalFile(); } else { mFileUrl = rawFileUrl.toString(); } Q_EMIT fileUrlChanged(); } void TrackMetadataModel::filterDataFromTrackData() { } void TrackMetadataModel::removeMetaData(DatabaseInterface::ColumnsRoles metaData) { auto itMetaData = std::find(mTrackKeys.begin(), mTrackKeys.end(), metaData); if (itMetaData == mTrackKeys.end()) { return; } mTrackKeys.erase(itMetaData); mTrackData.remove(metaData); } TrackMetadataModel::TrackDataType::mapped_type TrackMetadataModel::dataFromType(TrackDataType::key_type metaData) const { return mFullData[metaData]; } +void TrackMetadataModel::initialize(MusicListenersManager *newManager, DatabaseInterface *trackDatabase) +{ + mManager = newManager; + Q_EMIT managerChanged(); + + if (mManager) { + mDataLoader.setDatabase(mManager->viewDatabase()); + } else if (trackDatabase) { + mDataLoader.setDatabase(trackDatabase); + } + + if (mManager) { + mManager->connectModel(&mDataLoader); + } + + connect(this, &TrackMetadataModel::needDataByDatabaseId, + &mDataLoader, &ModelDataLoader::loadDataByDatabaseId); + connect(this, &TrackMetadataModel::needDataByFileName, + &mDataLoader, &ModelDataLoader::loadDataByFileName); + connect(&mDataLoader, &ModelDataLoader::allTrackData, + this, &TrackMetadataModel::trackData); + connect(&mDataLoader, &ModelDataLoader::trackModified, + this, &TrackMetadataModel::trackData); +} + void TrackMetadataModel::initializeByTrackId(qulonglong databaseId) { Q_EMIT needDataByDatabaseId(ElisaUtils::Track, databaseId); } void TrackMetadataModel::initializeByTrackFileName(const QUrl &fileName) { Q_EMIT needDataByFileName(ElisaUtils::FileName, fileName); } void TrackMetadataModel::setManager(MusicListenersManager *newManager) { - mManager = newManager; - Q_EMIT managerChanged(); - - if (mManager) { - mDataLoader.setDatabase(mManager->viewDatabase()); - mManager->connectModel(&mDataLoader); + initialize(newManager, nullptr); +} - connect(this, &TrackMetadataModel::needDataByDatabaseId, - &mDataLoader, &ModelDataLoader::loadDataByDatabaseId); - connect(this, &TrackMetadataModel::needDataByFileName, - &mDataLoader, &ModelDataLoader::loadDataByFileName); - connect(&mDataLoader, &ModelDataLoader::allTrackData, - this, &TrackMetadataModel::trackData); - } +void TrackMetadataModel::setDatabase(DatabaseInterface *trackDatabase) +{ + initialize(nullptr, trackDatabase); } #include "moc_trackmetadatamodel.cpp" diff --git a/src/models/trackmetadatamodel.h b/src/models/trackmetadatamodel.h index e0864df7..f17262f2 100644 --- a/src/models/trackmetadatamodel.h +++ b/src/models/trackmetadatamodel.h @@ -1,136 +1,141 @@ /* * 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 TRACKMETADATAMODEL_H #define TRACKMETADATAMODEL_H #include "elisaLib_export.h" #include "elisautils.h" #include "databaseinterface.h" #include "modeldataloader.h" #include #include class MusicListenersManager; class ELISALIB_EXPORT TrackMetadataModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QUrl coverUrl READ coverUrl NOTIFY coverUrlChanged) Q_PROPERTY(QString fileUrl READ fileUrl NOTIFY fileUrlChanged) Q_PROPERTY(MusicListenersManager* manager READ manager WRITE setManager NOTIFY managerChanged) public: enum ColumnRoles { ItemNameRole = Qt::UserRole + 1, ItemTypeRole, }; enum ItemType { TextEntry, IntegerEntry, RatingEntry, DateEntry, }; Q_ENUM(ItemType) using TrackDataType = DatabaseInterface::TrackDataType; explicit TrackMetadataModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; QHash roleNames() const override; const QUrl& coverUrl() const; QString fileUrl() const; MusicListenersManager* manager() const; Q_SIGNALS: void needDataByDatabaseId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId); void needDataByFileName(ElisaUtils::PlayListEntryType dataType, const QUrl &fileName); void coverUrlChanged(); void fileUrlChanged(); void managerChanged(); public Q_SLOTS: void trackData(const TrackMetadataModel::TrackDataType &trackData); void initializeByTrackId(qulonglong databaseId); void initializeByTrackFileName(const QUrl &fileName); void setManager(MusicListenersManager *newManager); + void setDatabase(DatabaseInterface *trackDatabase); + protected: void fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData); virtual void filterDataFromTrackData(); void removeMetaData(DatabaseInterface::ColumnsRoles metaData); TrackDataType::mapped_type dataFromType(TrackDataType::key_type metaData) const; private: + void initialize(MusicListenersManager *newManager, + DatabaseInterface *trackDatabase); + TrackDataType mFullData; TrackDataType mTrackData; QUrl mCoverImage; QString mFileUrl; QList mTrackKeys; ModelDataLoader mDataLoader; MusicListenersManager *mManager = nullptr; }; #endif // TRACKMETADATAMODEL_H