diff --git a/autotests/alltracksmodeltest.cpp b/autotests/alltracksmodeltest.cpp index b8f64606..bec5a7f5 100644 --- a/autotests/alltracksmodeltest.cpp +++ b/autotests/alltracksmodeltest.cpp @@ -1,399 +1,434 @@ /* * Copyright 2015-2017 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 "musicalbum.h" #include "musicaudiotrack.h" #include "databaseinterface.h" #include "alltracksmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include class AllTracksModelTests: public QObject { Q_OBJECT private: QList mNewTracks; QHash mNewCovers; private Q_SLOTS: void initTestCase() { mNewTracks = { {true, QStringLiteral("$1"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist1"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 1, 1, QTime::fromMSecsSinceStartOfDay(1), {QUrl::fromLocalFile(QStringLiteral("/$1"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$1"))}, 1}, {true, QStringLiteral("$2"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist2"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 2, 2, QTime::fromMSecsSinceStartOfDay(2), {QUrl::fromLocalFile(QStringLiteral("/$2"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$2"))}, 2}, {true, QStringLiteral("$3"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist3"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 3, 3, QTime::fromMSecsSinceStartOfDay(3), {QUrl::fromLocalFile(QStringLiteral("/$3"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$3"))}, 3}, {true, QStringLiteral("$4"), QStringLiteral("0"), QStringLiteral("track4"), QStringLiteral("artist4"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 4, 4, QTime::fromMSecsSinceStartOfDay(4), {QUrl::fromLocalFile(QStringLiteral("/$4"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$4"))}, 4}, {true, QStringLiteral("$5"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 1, 1, QTime::fromMSecsSinceStartOfDay(5), {QUrl::fromLocalFile(QStringLiteral("/$5"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$5"))}, 5}, {true, QStringLiteral("$6"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 2, 1, QTime::fromMSecsSinceStartOfDay(6), {QUrl::fromLocalFile(QStringLiteral("/$6"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$6"))}, 1}, {true, QStringLiteral("$7"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 3, 1, QTime::fromMSecsSinceStartOfDay(7), {QUrl::fromLocalFile(QStringLiteral("/$7"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$7"))}, 2}, {true, QStringLiteral("$8"), QStringLiteral("0"), QStringLiteral("track4"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 4, 1, QTime::fromMSecsSinceStartOfDay(8), {QUrl::fromLocalFile(QStringLiteral("/$8"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$8"))}, 3}, {true, QStringLiteral("$9"), QStringLiteral("0"), QStringLiteral("track5"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 5, 1, QTime::fromMSecsSinceStartOfDay(9), {QUrl::fromLocalFile(QStringLiteral("/$9"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$9"))}, 4}, {true, QStringLiteral("$10"), QStringLiteral("0"), QStringLiteral("track6"), QStringLiteral("artist1 and artist2"), QStringLiteral("album2"), QStringLiteral("artist1"), 6, 1, QTime::fromMSecsSinceStartOfDay(10), {QUrl::fromLocalFile(QStringLiteral("/$10"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$10"))}, 5}, {true, QStringLiteral("$11"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist2"), QStringLiteral("album3"), QStringLiteral("artist2"), 1, 1, QTime::fromMSecsSinceStartOfDay(11), {QUrl::fromLocalFile(QStringLiteral("/$11"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$11"))}, 1}, {true, QStringLiteral("$12"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist2"), QStringLiteral("album3"), QStringLiteral("artist2"), 2, 1, QTime::fromMSecsSinceStartOfDay(12), {QUrl::fromLocalFile(QStringLiteral("/$12"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$12"))}, 2}, {true, QStringLiteral("$13"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist2"), QStringLiteral("album3"), QStringLiteral("artist2"), 3, 1, QTime::fromMSecsSinceStartOfDay(13), {QUrl::fromLocalFile(QStringLiteral("/$13"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$13"))}, 3}, {true, QStringLiteral("$14"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 1, 1, QTime::fromMSecsSinceStartOfDay(14), {QUrl::fromLocalFile(QStringLiteral("/$14"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$14"))}, 4}, {true, QStringLiteral("$15"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 2, 1, QTime::fromMSecsSinceStartOfDay(15), {QUrl::fromLocalFile(QStringLiteral("/$15"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$15"))}, 5}, {true, QStringLiteral("$16"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 3, 1, QTime::fromMSecsSinceStartOfDay(16), {QUrl::fromLocalFile(QStringLiteral("/$16"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$16"))}, 1}, {true, QStringLiteral("$17"), QStringLiteral("0"), QStringLiteral("track4"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 4, 1, QTime::fromMSecsSinceStartOfDay(17), {QUrl::fromLocalFile(QStringLiteral("/$17"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$17"))}, 2}, {true, QStringLiteral("$18"), QStringLiteral("0"), QStringLiteral("track5"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 5, 1, QTime::fromMSecsSinceStartOfDay(18), {QUrl::fromLocalFile(QStringLiteral("/$18"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$18"))}, 3} }; mNewCovers[QStringLiteral("album1")] = QUrl::fromLocalFile(QStringLiteral("album1")); mNewCovers[QStringLiteral("album2")] = QUrl::fromLocalFile(QStringLiteral("album2")); mNewCovers[QStringLiteral("album3")] = QUrl::fromLocalFile(QStringLiteral("album3")); mNewCovers[QStringLiteral("album4")] = QUrl::fromLocalFile(QStringLiteral("album4")); qRegisterMetaType>("QHash"); qRegisterMetaType>("QHash"); qRegisterMetaType>>("QHash>"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType("MusicArtist"); } void removeOneTrack() { auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); auto rootDirectory = QDir::root(); rootDirectory.mkpath(configDirectory.path()); auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); QFile dbFile(fileName); auto dbExists = dbFile.exists(); if (dbExists) { QCOMPARE(dbFile.remove(), true); } DatabaseInterface musicDb; AllTracksModel tracksModel; connect(&musicDb, &DatabaseInterface::tracksAdded, &tracksModel, &AllTracksModel::tracksAdded); connect(&musicDb, &DatabaseInterface::trackModified, &tracksModel, &AllTracksModel::trackModified); connect(&musicDb, &DatabaseInterface::trackRemoved, &tracksModel, &AllTracksModel::trackRemoved); musicDb.init(QStringLiteral("testDb")); QSignalSpy beginInsertRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&tracksModel, &AllTracksModel::rowsInserted); QSignalSpy beginRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeRemoved); QSignalSpy endRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsRemoved); QSignalSpy dataChangedSpy(&tracksModel, &AllTracksModel::dataChanged); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); musicDb.insertTracksList(mNewTracks, mNewCovers, QStringLiteral("autoTest")); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 18); auto trackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track1"), QStringLiteral("album1"), QStringLiteral("artist1")); auto firstTrack = musicDb.trackFromDatabaseId(trackId); musicDb.removeTracksList({firstTrack.resourceURI()}); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 1); QCOMPARE(endRemoveRowsSpy.count(), 1); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 17); } void removeOneAlbum() { auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); auto rootDirectory = QDir::root(); rootDirectory.mkpath(configDirectory.path()); auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); QFile dbFile(fileName); auto dbExists = dbFile.exists(); if (dbExists) { QCOMPARE(dbFile.remove(), true); } DatabaseInterface musicDb; AllTracksModel tracksModel; connect(&musicDb, &DatabaseInterface::tracksAdded, &tracksModel, &AllTracksModel::tracksAdded); connect(&musicDb, &DatabaseInterface::trackModified, &tracksModel, &AllTracksModel::trackModified); connect(&musicDb, &DatabaseInterface::trackRemoved, &tracksModel, &AllTracksModel::trackRemoved); musicDb.init(QStringLiteral("testDb")); QSignalSpy beginInsertRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&tracksModel, &AllTracksModel::rowsInserted); QSignalSpy beginRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeRemoved); QSignalSpy endRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsRemoved); QSignalSpy dataChangedSpy(&tracksModel, &AllTracksModel::dataChanged); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); musicDb.insertTracksList(mNewTracks, mNewCovers, QStringLiteral("autoTest")); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 18); auto firstTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track1"), QStringLiteral("album1"), QStringLiteral("artist1")); auto firstTrack = musicDb.trackFromDatabaseId(firstTrackId); auto secondTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track2"), QStringLiteral("album1"), QStringLiteral("artist2")); auto secondTrack = musicDb.trackFromDatabaseId(secondTrackId); auto thirdTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track3"), QStringLiteral("album1"), QStringLiteral("artist3")); auto thirdTrack = musicDb.trackFromDatabaseId(thirdTrackId); auto fourthTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track4"), QStringLiteral("album1"), QStringLiteral("artist4")); auto fourthTrack = musicDb.trackFromDatabaseId(fourthTrackId); musicDb.removeTracksList({firstTrack.resourceURI(), secondTrack.resourceURI(), thirdTrack.resourceURI(), fourthTrack.resourceURI()}); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 4); QCOMPARE(endRemoveRowsSpy.count(), 4); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 14); } void addOneTrack() { auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); auto rootDirectory = QDir::root(); rootDirectory.mkpath(configDirectory.path()); auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); QFile dbFile(fileName); auto dbExists = dbFile.exists(); if (dbExists) { QCOMPARE(dbFile.remove(), true); } DatabaseInterface musicDb; AllTracksModel tracksModel; connect(&musicDb, &DatabaseInterface::tracksAdded, &tracksModel, &AllTracksModel::tracksAdded); connect(&musicDb, &DatabaseInterface::trackModified, &tracksModel, &AllTracksModel::trackModified); connect(&musicDb, &DatabaseInterface::trackRemoved, &tracksModel, &AllTracksModel::trackRemoved); musicDb.init(QStringLiteral("testDb")); QSignalSpy beginInsertRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&tracksModel, &AllTracksModel::rowsInserted); QSignalSpy beginRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeRemoved); QSignalSpy endRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsRemoved); QSignalSpy dataChangedSpy(&tracksModel, &AllTracksModel::dataChanged); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); musicDb.insertTracksList(mNewTracks, mNewCovers, QStringLiteral("autoTest")); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 18); auto newTrack = MusicAudioTrack{true, QStringLiteral("$19"), QStringLiteral("0"), QStringLiteral("track6"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 6, 1, QTime::fromMSecsSinceStartOfDay(19), {QUrl::fromLocalFile(QStringLiteral("/$19"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$19"))}, 5}; auto newTracks = QList(); newTracks.push_back(newTrack); musicDb.insertTracksList(newTracks, mNewCovers, QStringLiteral("autoTest")); QCOMPARE(beginInsertRowsSpy.count(), 2); QCOMPARE(endInsertRowsSpy.count(), 2); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 19); } void addOneAlbum() { auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); auto rootDirectory = QDir::root(); rootDirectory.mkpath(configDirectory.path()); auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); QFile dbFile(fileName); auto dbExists = dbFile.exists(); if (dbExists) { QCOMPARE(dbFile.remove(), true); } DatabaseInterface musicDb; AllTracksModel tracksModel; connect(&musicDb, &DatabaseInterface::tracksAdded, &tracksModel, &AllTracksModel::tracksAdded); connect(&musicDb, &DatabaseInterface::trackModified, &tracksModel, &AllTracksModel::trackModified); connect(&musicDb, &DatabaseInterface::trackRemoved, &tracksModel, &AllTracksModel::trackRemoved); musicDb.init(QStringLiteral("testDb")); QSignalSpy beginInsertRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&tracksModel, &AllTracksModel::rowsInserted); QSignalSpy beginRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeRemoved); QSignalSpy endRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsRemoved); QSignalSpy dataChangedSpy(&tracksModel, &AllTracksModel::dataChanged); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); auto newFiles = QList(); const auto &constNewTracks = mNewTracks; for (const auto &oneTrack : constNewTracks) { newFiles.push_back(oneTrack.resourceURI()); } musicDb.insertTracksList(mNewTracks, mNewCovers, QStringLiteral("autoTest")); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 18); auto newTrack = MusicAudioTrack{true, QStringLiteral("$19"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist2"), QStringLiteral("album5"), QStringLiteral("artist2"), 1, 1, QTime::fromMSecsSinceStartOfDay(19), {QUrl::fromLocalFile(QStringLiteral("/$19"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$19"))}, 5}; auto newTracks = QList(); newTracks.push_back(newTrack); auto newCover = QUrl::fromLocalFile(QStringLiteral("album5")); auto newCovers = QHash(); newCovers[QStringLiteral("album5")] = newCover; auto newFiles2 = QList(); for (const auto &oneTrack : newTracks) { newFiles2.push_back(oneTrack.resourceURI()); } musicDb.insertTracksList(newTracks, newCovers, QStringLiteral("autoTest")); QCOMPARE(beginInsertRowsSpy.count(), 2); QCOMPARE(endInsertRowsSpy.count(), 2); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(tracksModel.rowCount(), 19); } + + void addDuplicateTracks() + { + AllTracksModel tracksModel; + + auto newTracks = QList(); + newTracks.push_back({true, QStringLiteral("$19"), QStringLiteral("0"), QStringLiteral("track6"), + QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 6, 1, QTime::fromMSecsSinceStartOfDay(19), {QUrl::fromLocalFile(QStringLiteral("/$19"))}, + {QUrl::fromLocalFile(QStringLiteral("file://image$19"))}, 5}); + newTracks.push_back({true, QStringLiteral("$19"), QStringLiteral("0"), QStringLiteral("track6"), + QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 6, 1, QTime::fromMSecsSinceStartOfDay(19), {QUrl::fromLocalFile(QStringLiteral("/$19"))}, + {QUrl::fromLocalFile(QStringLiteral("file://image$19"))}, 5}); + + QSignalSpy beginInsertRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeInserted); + QSignalSpy endInsertRowsSpy(&tracksModel, &AllTracksModel::rowsInserted); + QSignalSpy beginRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsAboutToBeRemoved); + QSignalSpy endRemoveRowsSpy(&tracksModel, &AllTracksModel::rowsRemoved); + QSignalSpy dataChangedSpy(&tracksModel, &AllTracksModel::dataChanged); + + QCOMPARE(beginInsertRowsSpy.count(), 0); + QCOMPARE(endInsertRowsSpy.count(), 0); + QCOMPARE(beginRemoveRowsSpy.count(), 0); + QCOMPARE(endRemoveRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + + tracksModel.tracksAdded(newTracks); + + QCOMPARE(beginInsertRowsSpy.count(), 1); + QCOMPARE(endInsertRowsSpy.count(), 1); + QCOMPARE(beginRemoveRowsSpy.count(), 0); + QCOMPARE(endRemoveRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + + QCOMPARE(tracksModel.rowCount(), 1); + } }; QTEST_MAIN(AllTracksModelTests) #include "alltracksmodeltest.moc" diff --git a/src/abstractfile/abstractfilelisting.cpp b/src/abstractfile/abstractfilelisting.cpp index 83cbda87..e0ee14f9 100644 --- a/src/abstractfile/abstractfilelisting.cpp +++ b/src/abstractfile/abstractfilelisting.cpp @@ -1,416 +1,432 @@ /* * Copyright 2016-2017 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 "abstractfilelisting.h" #include "musicaudiotrack.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class AbstractFileListingPrivate { public: explicit AbstractFileListingPrivate(const QString &sourceName) : mSourceName(sourceName) { } QFileSystemWatcher mFileSystemWatcher; QHash mAllAlbumCover; QHash>> mDiscoveredFiles; QString mSourceName; bool mHandleNewFiles = true; KFileMetaData::ExtractorCollection mExtractors; QAtomicInt mStopRequest = 0; QMimeDatabase mMimeDb; }; AbstractFileListing::AbstractFileListing(const QString &sourceName, QObject *parent) : QObject(parent), d(new AbstractFileListingPrivate(sourceName)) { connect(&d->mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &AbstractFileListing::directoryChanged); connect(&d->mFileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &AbstractFileListing::fileChanged); } AbstractFileListing::~AbstractFileListing() { } void AbstractFileListing::init() { executeInit(); } void AbstractFileListing::databaseIsReady() { refreshContent(); } void AbstractFileListing::newTrackFile(const MusicAudioTrack &partialTrack) { const auto &newTrack = scanOneFile(partialTrack.resourceURI()); if (newTrack.isValid() && newTrack != partialTrack) { Q_EMIT modifyTracksList({newTrack}, d->mAllAlbumCover); } } void AbstractFileListing::applicationAboutToQuit() { d->mStopRequest = 1; } void AbstractFileListing::scanDirectory(QList &newFiles, const QUrl &path) { QDir rootDirectory(path.toLocalFile()); rootDirectory.refresh(); if (rootDirectory.exists()) { watchPath(path.toLocalFile()); } auto ¤tDirectoryListingFiles = d->mDiscoveredFiles[path]; auto currentFilesList = QSet(); rootDirectory.refresh(); const auto entryList = rootDirectory.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (const auto &oneEntry : entryList) { auto newFilePath = QUrl::fromLocalFile(oneEntry.canonicalFilePath()); if (oneEntry.isDir() || oneEntry.isFile()) { currentFilesList.insert(newFilePath); } } auto removedTracks = QVector>(); for (const auto &removedFilePath : currentDirectoryListingFiles) { auto itFilePath = std::find(currentFilesList.begin(), currentFilesList.end(), removedFilePath.first); if (itFilePath != currentFilesList.end()) { continue; } removedTracks.push_back(removedFilePath); } auto allRemovedTracks = QList(); for (const auto &oneRemovedTrack : removedTracks) { if (oneRemovedTrack.second) { allRemovedTracks.push_back(oneRemovedTrack.first); } else { removeFile(oneRemovedTrack.first, allRemovedTracks); } } for (const auto &oneRemovedTrack : removedTracks) { currentDirectoryListingFiles.remove(oneRemovedTrack); currentDirectoryListingFiles.remove(oneRemovedTrack); } if (!allRemovedTracks.isEmpty()) { Q_EMIT removedTracksList(allRemovedTracks); } if (!d->mHandleNewFiles) { return; } for (const auto &newFilePath : currentFilesList) { QFileInfo oneEntry(newFilePath.toLocalFile()); auto itFilePath = std::find(currentDirectoryListingFiles.begin(), currentDirectoryListingFiles.end(), QPair{newFilePath, oneEntry.isFile()}); if (itFilePath != currentDirectoryListingFiles.end()) { continue; } if (oneEntry.isDir()) { addFileInDirectory(newFilePath, path); scanDirectory(newFiles, newFilePath); continue; } if (!oneEntry.isFile()) { continue; } auto newTrack = scanOneFile(newFilePath); if (newTrack.isValid()) { addCover(newTrack); addFileInDirectory(newTrack.resourceURI(), path); newFiles.push_back(newTrack); } if (d->mStopRequest == 1) { break; } } } const QString &AbstractFileListing::sourceName() const { return d->mSourceName; } void AbstractFileListing::directoryChanged(const QString &path) { const auto directoryEntry = d->mDiscoveredFiles.find(QUrl::fromLocalFile(path)); if (directoryEntry == d->mDiscoveredFiles.end()) { return; } scanDirectoryTree(path); } void AbstractFileListing::fileChanged(const QString &modifiedFileName) { auto modifiedFile = QUrl::fromLocalFile(modifiedFileName); auto modifiedTrack = scanOneFile(modifiedFile); if (modifiedTrack.isValid()) { Q_EMIT modifyTracksList({modifiedTrack}, d->mAllAlbumCover); } } void AbstractFileListing::executeInit() { } void AbstractFileListing::triggerRefreshOfContent() { } void AbstractFileListing::refreshContent() { triggerRefreshOfContent(); } MusicAudioTrack AbstractFileListing::scanOneFile(const QUrl &scanFile) { MusicAudioTrack newTrack; const auto &fileMimeType = d->mMimeDb.mimeTypeForFile(scanFile.toLocalFile()); if (!fileMimeType.name().startsWith(QStringLiteral("audio/"))) { return newTrack; } QString mimetype = fileMimeType.name(); QList exList = d->mExtractors.fetchExtractors(mimetype); if (exList.isEmpty()) { return newTrack; } QFileInfo scanFileInfo(scanFile.toLocalFile()); if (scanFileInfo.exists()) { watchPath(scanFile.toLocalFile()); } KFileMetaData::Extractor* ex = exList.first(); KFileMetaData::SimpleExtractionResult result(scanFile.toLocalFile(), mimetype, KFileMetaData::ExtractionResult::ExtractMetaData); ex->extract(&result); const auto &allProperties = result.properties(); auto titleProperty = allProperties.find(KFileMetaData::Property::Title); auto durationProperty = allProperties.find(KFileMetaData::Property::Duration); auto artistProperty = allProperties.find(KFileMetaData::Property::Artist); auto albumProperty = allProperties.find(KFileMetaData::Property::Album); auto albumArtistProperty = allProperties.find(KFileMetaData::Property::AlbumArtist); auto trackNumberProperty = allProperties.find(KFileMetaData::Property::TrackNumber); auto discNumberProperty = allProperties.find(KFileMetaData::Property::DiscNumber); #if defined Q_OS_LINUX && !defined Q_OS_ANDROID auto fileData = KFileMetaData::UserMetaData(scanFile.toLocalFile()); #endif if (albumProperty != allProperties.end()) { auto albumValue = albumProperty->toString(); newTrack.setAlbumName(albumValue); if (artistProperty != allProperties.end()) { newTrack.setArtist(artistProperty->toString()); } if (durationProperty != allProperties.end()) { newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(1000 * durationProperty->toDouble())); } if (titleProperty != allProperties.end()) { newTrack.setTitle(titleProperty->toString()); } if (trackNumberProperty != allProperties.end()) { newTrack.setTrackNumber(trackNumberProperty->toInt()); } if (discNumberProperty != allProperties.end()) { newTrack.setDiscNumber(discNumberProperty->toInt()); } if (albumArtistProperty != allProperties.end()) { newTrack.setAlbumArtist(albumArtistProperty->toString()); } if (newTrack.artist().isEmpty()) { newTrack.setArtist(newTrack.albumArtist()); } newTrack.setResourceURI(scanFile); #if defined Q_OS_LINUX && !defined Q_OS_ANDROID newTrack.setRating(fileData.rating()); #endif + if (newTrack.title().isEmpty()) { + return newTrack; + } + + if (newTrack.artist().isEmpty()) { + return newTrack; + } + + if (newTrack.albumName().isEmpty()) { + return newTrack; + } + + if (!newTrack.duration().isValid()) { + return newTrack; + } + newTrack.setValid(true); } return newTrack; } void AbstractFileListing::watchPath(const QString &pathName) { d->mFileSystemWatcher.addPath(pathName); } void AbstractFileListing::addFileInDirectory(const QUrl &newFile, const QUrl &directoryName) { const auto directoryEntry = d->mDiscoveredFiles.find(directoryName); if (directoryEntry == d->mDiscoveredFiles.end()) { watchPath(directoryName.toLocalFile()); QDir currentDirectory(directoryName.toLocalFile()); if (currentDirectory.cdUp()) { const auto parentDirectoryName = currentDirectory.absolutePath(); const auto parentDirectory = QUrl::fromLocalFile(parentDirectoryName); const auto parentDirectoryEntry = d->mDiscoveredFiles.find(parentDirectory); if (parentDirectoryEntry == d->mDiscoveredFiles.end()) { watchPath(parentDirectoryName); } auto &parentCurrentDirectoryListingFiles = d->mDiscoveredFiles[parentDirectory]; parentCurrentDirectoryListingFiles.insert({directoryName, false}); } } auto ¤tDirectoryListingFiles = d->mDiscoveredFiles[directoryName]; QFileInfo isAFile(newFile.toLocalFile()); currentDirectoryListingFiles.insert({newFile, isAFile.isFile()}); } void AbstractFileListing::scanDirectoryTree(const QString &path) { auto newFiles = QList(); scanDirectory(newFiles, QUrl::fromLocalFile(path)); if (!newFiles.isEmpty() && d->mStopRequest == 0) { emitNewFiles(newFiles); } } void AbstractFileListing::setHandleNewFiles(bool handleThem) { d->mHandleNewFiles = handleThem; } void AbstractFileListing::emitNewFiles(const QList &tracks) { Q_EMIT tracksList(tracks, d->mAllAlbumCover, d->mSourceName); } void AbstractFileListing::addCover(const MusicAudioTrack &newTrack) { auto itCover = d->mAllAlbumCover.find(newTrack.albumName()); if (itCover != d->mAllAlbumCover.end()) { return; } QFileInfo trackFilePath(newTrack.resourceURI().toLocalFile()); QFileInfo coverFilePath(trackFilePath.dir().filePath(QStringLiteral("cover.jpg"))); if (coverFilePath.exists()) { d->mAllAlbumCover[newTrack.albumName()] = QUrl::fromLocalFile(coverFilePath.absoluteFilePath()); } } void AbstractFileListing::removeDirectory(const QUrl &removedDirectory, QList &allRemovedFiles) { const auto itRemovedDirectory = d->mDiscoveredFiles.find(removedDirectory); if (itRemovedDirectory == d->mDiscoveredFiles.end()) { return; } const auto ¤tRemovedDirectory = *itRemovedDirectory; for (const auto &itFile : currentRemovedDirectory) { if (itFile.first.isValid() && !itFile.first.isEmpty()) { removeFile(itFile.first, allRemovedFiles); if (itFile.second) { allRemovedFiles.push_back(itFile.first); } } } d->mDiscoveredFiles.erase(itRemovedDirectory); } void AbstractFileListing::removeFile(const QUrl &oneRemovedTrack, QList &allRemovedFiles) { auto itRemovedDirectory = d->mDiscoveredFiles.find(oneRemovedTrack); if (itRemovedDirectory != d->mDiscoveredFiles.end()) { removeDirectory(oneRemovedTrack, allRemovedFiles); } } #include "moc_abstractfilelisting.cpp" diff --git a/src/alltracksmodel.cpp b/src/alltracksmodel.cpp index 28290359..6f0f42d2 100644 --- a/src/alltracksmodel.cpp +++ b/src/alltracksmodel.cpp @@ -1,254 +1,266 @@ /* * Copyright 2017 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 "alltracksmodel.h" #include #include class AllTracksModelPrivate { public: QHash mAllTracks; QList mIds; }; AllTracksModel::AllTracksModel(QObject *parent) : QAbstractItemModel(parent), d(new AllTracksModelPrivate) { } AllTracksModel::~AllTracksModel() { delete d; } int AllTracksModel::rowCount(const QModelIndex &parent) const { - auto albumCount = 0; + auto tracksCount = 0; if (parent.isValid()) { - return albumCount; + return tracksCount; } - albumCount = d->mAllTracks.size(); + tracksCount = d->mAllTracks.size(); - return albumCount; + return tracksCount; } QHash AllTracksModel::roleNames() const { QHash roles; roles[static_cast(ColumnsRoles::TitleRole)] = "title"; roles[static_cast(ColumnsRoles::DurationRole)] = "duration"; roles[static_cast(ColumnsRoles::ArtistRole)] = "artist"; roles[static_cast(ColumnsRoles::AlbumRole)] = "album"; roles[static_cast(ColumnsRoles::TrackNumberRole)] = "trackNumber"; roles[static_cast(ColumnsRoles::DiscNumberRole)] = "discNumber"; roles[static_cast(ColumnsRoles::RatingRole)] = "rating"; roles[static_cast(ColumnsRoles::ImageRole)] = "image"; roles[static_cast(ColumnsRoles::DatabaseIdRole)] = "databaseId"; roles[static_cast(ColumnsRoles::TrackDataRole)] = "trackData"; return roles; } Qt::ItemFlags AllTracksModel::flags(const QModelIndex &index) const { if (!index.isValid()) { return Qt::NoItemFlags; } return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } QVariant AllTracksModel::data(const QModelIndex &index, int role) const { auto result = QVariant(); const auto tracksCount = d->mAllTracks.size(); if (!index.isValid()) { return result; } if (index.column() != 0) { return result; } if (index.row() < 0) { return result; } if (index.parent().isValid()) { return result; } if (index.internalId() != 0) { return result; } if (index.row() < 0 || index.row() >= tracksCount) { return result; } ColumnsRoles convertedRole = static_cast(role); switch(convertedRole) { case ColumnsRoles::TitleRole: - result = d->mAllTracks[index.row()].title(); + if (d->mAllTracks[d->mIds[index.row()]].title().isEmpty()) { + result = {}; + } + result = d->mAllTracks[d->mIds[index.row()]].title(); break; case ColumnsRoles::MilliSecondsDurationRole: - result = d->mAllTracks[index.row()].duration().msecsSinceStartOfDay(); + result = d->mAllTracks[d->mIds[index.row()]].duration().msecsSinceStartOfDay(); break; case ColumnsRoles::DurationRole: { - QTime trackDuration = d->mAllTracks[index.row()].duration(); + QTime trackDuration = d->mAllTracks[d->mIds[index.row()]].duration(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(); } break; } case ColumnsRoles::CreatorRole: - result = d->mAllTracks[index.row()].artist(); + result = d->mAllTracks[d->mIds[index.row()]].artist(); break; case ColumnsRoles::ArtistRole: - result = d->mAllTracks[index.row()].artist(); + result = d->mAllTracks[d->mIds[index.row()]].artist(); break; case ColumnsRoles::AlbumRole: - result = d->mAllTracks[index.row()].albumName(); + result = d->mAllTracks[d->mIds[index.row()]].albumName(); break; case ColumnsRoles::TrackNumberRole: - result = d->mAllTracks[index.row()].trackNumber(); + result = d->mAllTracks[d->mIds[index.row()]].trackNumber(); break; case ColumnsRoles::DiscNumberRole: { - const auto discNumber = d->mAllTracks[index.row()].discNumber(); + const auto discNumber = d->mAllTracks[d->mIds[index.row()]].discNumber(); if (discNumber > 0) { result = discNumber; } break; } case ColumnsRoles::RatingRole: - result = d->mAllTracks[index.row()].rating(); + result = d->mAllTracks[d->mIds[index.row()]].rating(); break; case ColumnsRoles::ImageRole: { - const auto &imageUrl = d->mAllTracks[index.row()].albumCover(); + const auto &imageUrl = d->mAllTracks[d->mIds[index.row()]].albumCover(); if (imageUrl.isValid()) { result = imageUrl; } break; } case ColumnsRoles::ResourceRole: - result = d->mAllTracks[index.row()].resourceURI(); + result = d->mAllTracks[d->mIds[index.row()]].resourceURI(); break; case ColumnsRoles::IdRole: - result = d->mAllTracks[index.row()].title(); + result = d->mAllTracks[d->mIds[index.row()]].title(); break; case ColumnsRoles::DatabaseIdRole: - result = d->mAllTracks[index.row()].databaseId(); + result = d->mAllTracks[d->mIds[index.row()]].databaseId(); break; case ColumnsRoles::TrackDataRole: - result = QVariant::fromValue(d->mAllTracks[index.row()]); + result = QVariant::fromValue(d->mAllTracks[d->mIds[index.row()]]); break; } return result; } QModelIndex AllTracksModel::index(int row, int column, const QModelIndex &parent) const { auto result = QModelIndex(); if (column != 0) { return result; } if (parent.isValid()) { return result; } if (row > d->mAllTracks.size() - 1) { return result; } result = createIndex(row, column); return result; } QModelIndex AllTracksModel::parent(const QModelIndex &child) const { Q_UNUSED(child) auto result = QModelIndex(); return result; } int AllTracksModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } void AllTracksModel::tracksAdded(const QList &allTracks) { - beginInsertRows({}, 0, allTracks.size() - 1); + auto newAllTracks = d->mAllTracks; + auto newTracksIds = QList(); + + int countNewTracks = 0; for (const auto &oneTrack : allTracks) { - d->mAllTracks[oneTrack.databaseId()] = oneTrack; + if (newAllTracks.find(oneTrack.databaseId()) == newAllTracks.end()) { + newAllTracks[oneTrack.databaseId()] = oneTrack; + newTracksIds.push_back(oneTrack.databaseId()); + ++countNewTracks; + } } - d->mIds = d->mAllTracks.keys(); - std::sort(d->mIds.begin(), d->mIds.end()); + beginInsertRows({}, d->mAllTracks.size(), d->mAllTracks.size() + countNewTracks - 1); + + d->mAllTracks = newAllTracks; + d->mIds.append(newTracksIds); endInsertRows(); } void AllTracksModel::trackRemoved(qulonglong removedTrackId) { auto itTrack = std::find(d->mIds.begin(), d->mIds.end(), removedTrackId); if (itTrack == d->mIds.end()) { return; } auto position = itTrack - d->mIds.begin(); beginRemoveRows({}, position, position); d->mIds.erase(itTrack); d->mAllTracks.remove(removedTrackId); endRemoveRows(); } void AllTracksModel::trackModified(const MusicAudioTrack &modifiedTrack) { } #include "moc_alltracksmodel.cpp" diff --git a/src/baloo/localbaloofilelisting.cpp b/src/baloo/localbaloofilelisting.cpp index b67e6159..92882388 100644 --- a/src/baloo/localbaloofilelisting.cpp +++ b/src/baloo/localbaloofilelisting.cpp @@ -1,226 +1,242 @@ /* * Copyright 2016-2017 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 "localbaloofilelisting.h" #include "musicaudiotrack.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class LocalBalooFileListingPrivate { public: Baloo::Query mQuery; QHash> mAllAlbums; QHash> mNewAlbums; QList mNewTracks; QHash mAllAlbumCover; QAtomicInt mStopRequest = 0; }; LocalBalooFileListing::LocalBalooFileListing(QObject *parent) : AbstractFileListing(QStringLiteral("baloo"), parent), d(new LocalBalooFileListingPrivate) { d->mQuery.addType(QStringLiteral("Audio")); setHandleNewFiles(false); } LocalBalooFileListing::~LocalBalooFileListing() { } void LocalBalooFileListing::applicationAboutToQuit() { d->mStopRequest = 1; } void LocalBalooFileListing::newBalooFile(const QString &fileName) { auto newFile = QUrl::fromLocalFile(fileName); auto newTrack = scanOneFile(newFile); if (newTrack.isValid()) { QFileInfo newFileInfo(fileName); addFileInDirectory(newFile, QUrl::fromLocalFile(newFileInfo.absoluteDir().absolutePath())); emitNewFiles({newTrack}); } } void LocalBalooFileListing::executeInit() { auto sessionBus = QDBusConnection::sessionBus(); auto methodCall = QDBusMessage::createMethodCall(QStringLiteral("org.kde.baloo"), QStringLiteral("/fileindexer"), QStringLiteral("org.kde.baloo.fileindexer"), QStringLiteral("registerMonitor")); auto answer = sessionBus.call(methodCall); if (answer.type() != QDBusMessage::ReplyMessage) { qDebug() << "LocalBalooFileListing::executeInit" << answer.errorName() << answer.errorMessage(); } sessionBus.connect(QStringLiteral("org.kde.baloo"), QStringLiteral("/fileindexer"), QStringLiteral("org.kde.baloo.fileindexer"), QStringLiteral("finishedIndexingFile"), this, SLOT(newBalooFile(QString))); } void LocalBalooFileListing::triggerRefreshOfContent() { auto resultIterator = d->mQuery.exec(); auto newFiles = QList(); while(resultIterator.next() && d->mStopRequest == 0) { const auto &newFileUrl = QUrl::fromLocalFile(resultIterator.filePath()); auto scanFileInfo = QFileInfo(resultIterator.filePath()); const auto currentDirectory = QUrl::fromLocalFile(scanFileInfo.absoluteDir().absolutePath()); addFileInDirectory(newFileUrl, currentDirectory); const auto &newTrack = scanOneFile(newFileUrl); if (newTrack.isValid()) { newFiles.push_back(newTrack); } } if (!newFiles.isEmpty() && d->mStopRequest == 0) { emitNewFiles(newFiles); } } MusicAudioTrack LocalBalooFileListing::scanOneFile(const QUrl &scanFile) { auto newTrack = MusicAudioTrack(); auto fileName = scanFile.toLocalFile(); auto scanFileInfo = QFileInfo(fileName); if (scanFileInfo.exists()) { watchPath(fileName); } Baloo::File match(fileName); match.load(); const auto &allProperties = match.properties(); auto titleProperty = allProperties.find(KFileMetaData::Property::Title); auto durationProperty = allProperties.find(KFileMetaData::Property::Duration); auto artistProperty = allProperties.find(KFileMetaData::Property::Artist); auto albumProperty = allProperties.find(KFileMetaData::Property::Album); auto albumArtistProperty = allProperties.find(KFileMetaData::Property::AlbumArtist); auto trackNumberProperty = allProperties.find(KFileMetaData::Property::TrackNumber); auto discNumberProperty = allProperties.find(KFileMetaData::Property::DiscNumber); auto fileData = KFileMetaData::UserMetaData(fileName); if (albumProperty != allProperties.end()) { auto albumValue = albumProperty->toString(); auto &allTracks = d->mAllAlbums[albumValue]; newTrack.setAlbumName(albumValue); if (artistProperty != allProperties.end()) { newTrack.setArtist(artistProperty->toString()); } if (durationProperty != allProperties.end()) { newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(1000 * durationProperty->toDouble())); } if (titleProperty != allProperties.end()) { newTrack.setTitle(titleProperty->toString()); } if (trackNumberProperty != allProperties.end()) { newTrack.setTrackNumber(trackNumberProperty->toInt()); } if (discNumberProperty != allProperties.end()) { newTrack.setDiscNumber(discNumberProperty->toInt()); } if (albumArtistProperty != allProperties.end()) { newTrack.setAlbumArtist(albumArtistProperty->toString()); } if (newTrack.artist().isEmpty()) { newTrack.setArtist(newTrack.albumArtist()); } newTrack.setRating(fileData.rating()); newTrack.setResourceURI(scanFile); QFileInfo coverFilePath(scanFileInfo.dir().filePath(QStringLiteral("cover.jpg"))); if (coverFilePath.exists()) { d->mAllAlbumCover[albumValue] = QUrl::fromLocalFile(coverFilePath.absoluteFilePath()); } auto itTrack = std::find(allTracks.begin(), allTracks.end(), newTrack); if (itTrack == allTracks.end()) { allTracks.push_back(newTrack); d->mNewTracks.push_back(newTrack); d->mNewAlbums[newTrack.albumName()].push_back(newTrack); auto &newTracks = d->mAllAlbums[newTrack.albumName()]; std::sort(allTracks.begin(), allTracks.end()); std::sort(newTracks.begin(), newTracks.end()); } + if (newTrack.title().isEmpty()) { + return newTrack; + } + + if (newTrack.artist().isEmpty()) { + return newTrack; + } + + if (newTrack.albumName().isEmpty()) { + return newTrack; + } + + if (!newTrack.duration().isValid()) { + return newTrack; + } + newTrack.setValid(true); } if (!newTrack.isValid()) { newTrack = AbstractFileListing::scanOneFile(scanFile); } if (newTrack.isValid()) { addCover(newTrack); } return newTrack; } #include "moc_localbaloofilelisting.cpp"