diff --git a/autotests/databaseinterfacetest.cpp b/autotests/databaseinterfacetest.cpp --- a/autotests/databaseinterfacetest.cpp +++ b/autotests/databaseinterfacetest.cpp @@ -4958,6 +4958,155 @@ QCOMPARE(musicDbTrackModifiedSpy.count(), 0); QCOMPARE(musicDbDatabaseErrorSpy.count(), 0); } + + void enqueueTracksWithMissingMetadata() + { + QTemporaryFile databaseFile; + databaseFile.open(); + + qDebug() << "enqueueTracksWithMissingMetadata" << databaseFile.fileName(); + + DatabaseInterface musicDb; + + musicDb.init(QStringLiteral("enqueueTracksWithMissingMetadata"), databaseFile.fileName()); + + QSignalSpy musicDbArtistAddedSpy(&musicDb, &DatabaseInterface::artistsAdded); + QSignalSpy musicDbAlbumAddedSpy(&musicDb, &DatabaseInterface::albumsAdded); + QSignalSpy musicDbTrackAddedSpy(&musicDb, &DatabaseInterface::tracksAdded); + QSignalSpy musicDbArtistRemovedSpy(&musicDb, &DatabaseInterface::artistRemoved); + QSignalSpy musicDbAlbumRemovedSpy(&musicDb, &DatabaseInterface::albumRemoved); + QSignalSpy musicDbTrackRemovedSpy(&musicDb, &DatabaseInterface::trackRemoved); + QSignalSpy musicDbAlbumModifiedSpy(&musicDb, &DatabaseInterface::albumModified); + QSignalSpy musicDbTrackModifiedSpy(&musicDb, &DatabaseInterface::trackModified); + QSignalSpy musicDbDatabaseErrorSpy(&musicDb, &DatabaseInterface::databaseError); + + QCOMPARE(musicDb.allAlbumsData().count(), 0); + QCOMPARE(musicDb.allArtistsData().count(), 0); + QCOMPARE(musicDb.allTracksData().count(), 0); + QCOMPARE(musicDbArtistAddedSpy.count(), 0); + QCOMPARE(musicDbAlbumAddedSpy.count(), 0); + QCOMPARE(musicDbTrackAddedSpy.count(), 0); + QCOMPARE(musicDbArtistRemovedSpy.count(), 0); + QCOMPARE(musicDbAlbumRemovedSpy.count(), 0); + QCOMPARE(musicDbTrackRemovedSpy.count(), 0); + QCOMPARE(musicDbAlbumModifiedSpy.count(), 0); + QCOMPARE(musicDbTrackModifiedSpy.count(), 0); + QCOMPARE(musicDbDatabaseErrorSpy.count(), 0); + + auto fullTrack = MusicAudioTrack{true, QStringLiteral("$23"), QStringLiteral("0"), QStringLiteral("track6"), + QStringLiteral("artist2"), QStringLiteral("album3"), QStringLiteral("artist2"), + 6, 1, QTime::fromMSecsSinceStartOfDay(23), {QUrl::fromLocalFile(QStringLiteral("/test/$23"))}, + QDateTime::fromMSecsSinceEpoch(23), + QUrl::fromLocalFile(QStringLiteral("album3")), 5, true, + QStringLiteral("genre1"), QStringLiteral("composer1"), QStringLiteral("lyricist1"), false}; + + fullTrack.setBitRate(154); + fullTrack.setChannels(2); + fullTrack.setSampleRate(48000); + + auto newTrack = MusicAudioTrack{}; + + newTrack.setValid(true); + newTrack.setId(QStringLiteral("$29")); + newTrack.setParentId(QStringLiteral("0")); + newTrack.setTitle(QStringLiteral("track19")); + newTrack.setArtist(QStringLiteral("artist2")); + newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(29)); + newTrack.setResourceURI(QUrl::fromLocalFile(QStringLiteral("/$29"))); + newTrack.setFileModificationTime(QDateTime::fromMSecsSinceEpoch(29)); + newTrack.setAlbumCover(QUrl::fromLocalFile(QStringLiteral("/withoutAlbum"))); + newTrack.setRating(9); + newTrack.setIsSingleDiscAlbum(true); + newTrack.setGenre(QStringLiteral("genre1")); + newTrack.setComposer(QStringLiteral("composer1")); + newTrack.setLyricist(QStringLiteral("lyricist1")); + newTrack.setHasEmbeddedCover(false); + + musicDb.insertTracksList({fullTrack, newTrack}, mNewCovers); + + QCOMPARE(musicDb.allAlbumsData().count(), 1); + QCOMPARE(musicDb.allArtistsData().count(), 1); + QCOMPARE(musicDb.allTracksData().count(), 2); + QCOMPARE(musicDbArtistAddedSpy.count(), 1); + QCOMPARE(musicDbAlbumAddedSpy.count(), 1); + QCOMPARE(musicDbTrackAddedSpy.count(), 1); + QCOMPARE(musicDbArtistRemovedSpy.count(), 0); + QCOMPARE(musicDbAlbumRemovedSpy.count(), 0); + QCOMPARE(musicDbTrackRemovedSpy.count(), 0); + QCOMPARE(musicDbAlbumModifiedSpy.count(), 0); + QCOMPARE(musicDbTrackModifiedSpy.count(), 0); + QCOMPARE(musicDbDatabaseErrorSpy.count(), 0); + + auto firstTrackId = musicDb.trackIdFromTitleAlbumTrackDiscNumber(QStringLiteral("track6"), QStringLiteral("artist2"), + QStringLiteral("album3"), 6, 1); + auto firstTrack = musicDb.trackDataFromDatabaseId(firstTrackId); + + auto firstTrackTitle = firstTrack.title(); + auto firstTrackArtist = firstTrack.artist(); + auto firstTrackAlbumArtist = firstTrack.albumArtist(); + auto firstTrackAlbum = firstTrack.album(); + auto firstTrackImage = firstTrack.albumCover(); + auto firstTrackDuration = firstTrack.duration(); + auto firstTrackMilliSecondsDuration = firstTrack.duration().msecsSinceStartOfDay(); + auto firstTrackTrackNumber = firstTrack.trackNumber(); + auto firstTrackDiscNumber = firstTrack.discNumber(); + const auto &firstTrackResource = firstTrack.resourceURI(); + auto firstTrackRating = firstTrack.rating(); + auto firstIsSingleDiscAlbum = firstTrack.isSingleDiscAlbum(); + + QCOMPARE(firstTrack.isValid(), true); + QCOMPARE(firstTrackTitle, QStringLiteral("track6")); + QCOMPARE(firstTrackArtist, QStringLiteral("artist2")); + QCOMPARE(firstTrackAlbumArtist, QStringLiteral("artist2")); + QCOMPARE(firstTrackAlbum, QStringLiteral("album3")); + QCOMPARE(firstTrackImage.isValid(), true); + QCOMPARE(firstTrackImage, QUrl::fromLocalFile(QStringLiteral("album3"))); + QCOMPARE(firstTrackDuration, QTime::fromMSecsSinceStartOfDay(23)); + QCOMPARE(firstTrackMilliSecondsDuration, 23); + QCOMPARE(firstTrackTrackNumber, 6); + QCOMPARE(firstTrackDiscNumber, 1); + QCOMPARE(firstTrackResource.isValid(), true); + QCOMPARE(firstTrackResource, QUrl::fromLocalFile(QStringLiteral("/test/$23"))); + QCOMPARE(firstTrackRating, 5); + QCOMPARE(firstIsSingleDiscAlbum, true); + + auto secondTrackId = musicDb.trackIdFromTitleAlbumTrackDiscNumber(QStringLiteral("track19"), QStringLiteral("artist2"), {}, {}, {}); + auto secondTrack = musicDb.trackDataFromDatabaseId(secondTrackId); + + auto secondTrackTitle = secondTrack.title(); + auto secondTrackArtist = secondTrack.artist(); + auto secondTrackAlbumArtist = secondTrack.albumArtist(); + auto secondTrackAlbum = secondTrack.album(); + auto secondTrackImage = secondTrack.albumCover(); + auto secondTrackDuration = secondTrack.duration(); + auto secondTrackMilliSecondsDuration = secondTrack.duration().msecsSinceStartOfDay(); + auto secondTrackTrackNumber = secondTrack.trackNumber(); + auto secondTrackDiscNumber = secondTrack.discNumber(); + auto secondTrackChannels = secondTrack.channels(); + auto secondTrackBitRate = secondTrack.bitRate(); + auto secondTrackSampleRate = secondTrack.sampleRate(); + const auto &secondTrackResource = secondTrack.resourceURI(); + auto secondTrackRating = secondTrack.rating(); + auto secondIsSingleDiscAlbum = secondTrack.isSingleDiscAlbum(); + + QCOMPARE(secondTrack.isValid(), true); + QCOMPARE(secondTrackTitle, QStringLiteral("track19")); + QCOMPARE(secondTrackArtist, QStringLiteral("artist2")); + QCOMPARE(secondTrackAlbumArtist, QString()); + QCOMPARE(secondTrackAlbum, QString()); + QCOMPARE(secondTrackImage.isValid(), false); + QCOMPARE(secondTrackDuration, QTime::fromMSecsSinceStartOfDay(29)); + QCOMPARE(secondTrackMilliSecondsDuration, 29); + QCOMPARE(secondTrackTrackNumber, 0); + QCOMPARE(secondTrackDiscNumber, 0); + QCOMPARE(secondTrackChannels, 0); + QCOMPARE(secondTrackBitRate, 0); + QCOMPARE(secondTrackSampleRate, 0); + QCOMPARE(secondTrackResource.isValid(), true); + QCOMPARE(secondTrackResource, QUrl::fromLocalFile(QStringLiteral("/$29"))); + QCOMPARE(secondTrackRating, 9); + QCOMPARE(secondIsSingleDiscAlbum, true); + } }; QTEST_GUILESS_MAIN(DatabaseInterfaceTests) diff --git a/autotests/mediaplaylisttest.h b/autotests/mediaplaylisttest.h --- a/autotests/mediaplaylisttest.h +++ b/autotests/mediaplaylisttest.h @@ -73,6 +73,8 @@ void restoreMultipleIdenticalTracks(); + void restoreTrackWithoutAlbum(); + void testHasHeaderAlbumWithSameTitle(); void testSavePersistentState(); diff --git a/autotests/mediaplaylisttest.cpp b/autotests/mediaplaylisttest.cpp --- a/autotests/mediaplaylisttest.cpp +++ b/autotests/mediaplaylisttest.cpp @@ -2918,6 +2918,140 @@ QCOMPARE(myPlayList.data(myPlayList.index(3, 0), MediaPlayList::MilliSecondsDurationRole).toInt(), 3); } +void MediaPlayListTest::restoreTrackWithoutAlbum() +{ + QTemporaryFile databaseFile; + databaseFile.open(); + + qDebug() << "restoreTrackWithoutAlbum" << databaseFile.fileName(); + + MediaPlayList myPlayList; + QAbstractItemModelTester testModel(&myPlayList); + DatabaseInterface myDatabaseContent; + TracksListener myListener(&myDatabaseContent); + + QSignalSpy rowsAboutToBeMovedSpy(&myPlayList, &MediaPlayList::rowsAboutToBeMoved); + QSignalSpy rowsAboutToBeRemovedSpy(&myPlayList, &MediaPlayList::rowsAboutToBeRemoved); + QSignalSpy rowsAboutToBeInsertedSpy(&myPlayList, &MediaPlayList::rowsAboutToBeInserted); + QSignalSpy rowsMovedSpy(&myPlayList, &MediaPlayList::rowsMoved); + QSignalSpy rowsRemovedSpy(&myPlayList, &MediaPlayList::rowsRemoved); + QSignalSpy rowsInsertedSpy(&myPlayList, &MediaPlayList::rowsInserted); + QSignalSpy persistentStateChangedSpy(&myPlayList, &MediaPlayList::persistentStateChanged); + QSignalSpy dataChangedSpy(&myPlayList, &MediaPlayList::dataChanged); + QSignalSpy newTrackByNameInListSpy(&myPlayList, &MediaPlayList::newTrackByNameInList); + QSignalSpy newEntryInListSpy(&myPlayList, &MediaPlayList::newEntryInList); + + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeMovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(rowsRemovedSpy.count(), 0); + QCOMPARE(rowsMovedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 0); + QCOMPARE(persistentStateChangedSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(newTrackByNameInListSpy.count(), 0); + QCOMPARE(newEntryInListSpy.count(), 0); + + myDatabaseContent.init(QStringLiteral("restoreTrackWithoutAlbum"), databaseFile.fileName()); + + connect(&myListener, &TracksListener::trackHasChanged, + &myPlayList, &MediaPlayList::trackChanged, + Qt::QueuedConnection); + connect(&myListener, &TracksListener::tracksListAdded, + &myPlayList, &MediaPlayList::tracksListAdded, + Qt::QueuedConnection); + connect(&myPlayList, &MediaPlayList::newTrackByNameInList, + &myListener, &TracksListener::trackByNameInList, + Qt::QueuedConnection); + connect(&myPlayList, &MediaPlayList::newEntryInList, + &myListener, &TracksListener::newEntryInList, + Qt::QueuedConnection); + connect(&myDatabaseContent, &DatabaseInterface::tracksAdded, + &myListener, &TracksListener::tracksAdded); + + myDatabaseContent.insertTracksList(mNewTracks, mNewCovers); + + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeMovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(rowsRemovedSpy.count(), 0); + QCOMPARE(rowsMovedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 0); + QCOMPARE(persistentStateChangedSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(newTrackByNameInListSpy.count(), 0); + QCOMPARE(newEntryInListSpy.count(), 0); + + auto newTrack = MusicAudioTrack{}; + + newTrack.setValid(true); + newTrack.setId(QStringLiteral("$29")); + newTrack.setParentId(QStringLiteral("0")); + newTrack.setTitle(QStringLiteral("track19")); + newTrack.setArtist(QStringLiteral("artist2")); + newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(29)); + newTrack.setResourceURI(QUrl::fromLocalFile(QStringLiteral("/$29"))); + newTrack.setFileModificationTime(QDateTime::fromMSecsSinceEpoch(29)); + newTrack.setAlbumCover(QUrl::fromLocalFile(QStringLiteral("withoutAlbum"))); + newTrack.setRating(9); + newTrack.setIsSingleDiscAlbum(true); + newTrack.setGenre(QStringLiteral("genre1")); + newTrack.setComposer(QStringLiteral("composer1")); + newTrack.setLyricist(QStringLiteral("lyricist1")); + newTrack.setHasEmbeddedCover(false); + + myDatabaseContent.insertTracksList({newTrack}, mNewCovers); + + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeMovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(rowsRemovedSpy.count(), 0); + QCOMPARE(rowsMovedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 0); + QCOMPARE(persistentStateChangedSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(newTrackByNameInListSpy.count(), 0); + QCOMPARE(newEntryInListSpy.count(), 0); + + auto newEntry = MediaPlayListEntry{}; + + newEntry.mTitle = QStringLiteral("track19"); + newEntry.mArtist = QStringLiteral("artist2"); + + myPlayList.enqueueRestoredEntry(newEntry); + + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeMovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1); + QCOMPARE(rowsRemovedSpy.count(), 0); + QCOMPARE(rowsMovedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 1); + QCOMPARE(persistentStateChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(newTrackByNameInListSpy.count(), 1); + QCOMPARE(newEntryInListSpy.count(), 0); + + QCOMPARE(dataChangedSpy.wait(), true); + + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeMovedSpy.count(), 0); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1); + QCOMPARE(rowsRemovedSpy.count(), 0); + QCOMPARE(rowsMovedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 1); + QCOMPARE(persistentStateChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(newTrackByNameInListSpy.count(), 1); + QCOMPARE(newEntryInListSpy.count(), 0); + + QCOMPARE(myPlayList.data(myPlayList.index(0, 0), MediaPlayList::TitleRole).toString(), QStringLiteral("track19")); + QCOMPARE(myPlayList.data(myPlayList.index(0, 0), MediaPlayList::AlbumRole).toString(), {}); + QCOMPARE(myPlayList.data(myPlayList.index(0, 0), MediaPlayList::ArtistRole).toString(), QStringLiteral("artist2")); + QCOMPARE(myPlayList.data(myPlayList.index(0, 0), MediaPlayList::TrackNumberRole).toInt(), 0); + QCOMPARE(myPlayList.data(myPlayList.index(0, 0), MediaPlayList::DiscNumberRole).toInt(), 0); + QCOMPARE(myPlayList.data(myPlayList.index(0, 0), MediaPlayList::MilliSecondsDurationRole).toInt(), 29); +} + void MediaPlayListTest::testHasHeaderAlbumWithSameTitle() { MediaPlayList myPlayList; diff --git a/src/databaseinterface.h b/src/databaseinterface.h --- a/src/databaseinterface.h +++ b/src/databaseinterface.h @@ -33,6 +33,7 @@ #include #include +#include class DatabaseInterfacePrivate; class QMutex; @@ -127,6 +128,11 @@ return operator[](key_type::AlbumIdRole).toULongLong(); } + bool hasAlbum() const + { + return find(key_type::AlbumRole) != end(); + } + QString album() const { return operator[](key_type::AlbumRole).toString(); @@ -137,11 +143,21 @@ return operator[](key_type::AlbumArtistRole).toString(); } + bool hasTrackNumber() const + { + return find(key_type::TrackNumberRole) != end(); + } + int trackNumber() const { return operator[](key_type::TrackNumberRole).toInt(); } + bool hasDiscNumber() const + { + return find(key_type::DiscNumberRole) != end(); + } + int discNumber() const { return operator[](key_type::DiscNumberRole).toInt(); @@ -367,8 +383,8 @@ TrackDataType trackDataFromDatabaseId(qulonglong id); - qulonglong trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const QString &album, - int trackNumber, int discNumber); + qulonglong trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const std::optional &album, + std::optional trackNumber, std::optional discNumber); qulonglong trackIdFromFileName(const QUrl &fileName); @@ -447,8 +463,8 @@ MusicAudioTrack internalTrackFromDatabaseId(qulonglong id); - qulonglong internalTrackIdFromTitleAlbumTracDiscNumber(const QString &title, const QString &artist, const QString &album, - int trackNumber, int discNumber); + qulonglong internalTrackIdFromTitleAlbumTracDiscNumber(const QString &title, const QString &artist, const std::optional &album, + std::optional trackNumber, std::optional discNumber); qulonglong getDuplicateTrackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &trackArtist, const QString &album, const QString &albumArtist, const QString &trackPath, int trackNumber, diff --git a/src/databaseinterface.cpp b/src/databaseinterface.cpp --- a/src/databaseinterface.cpp +++ b/src/databaseinterface.cpp @@ -702,8 +702,8 @@ return result; } -qulonglong DatabaseInterface::trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const QString &album, - int trackNumber, int discNumber) +qulonglong DatabaseInterface::trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const std::optional &album, + std::optional trackNumber, std::optional discNumber) { auto result = qulonglong(0); @@ -4770,9 +4770,9 @@ "`Tracks` tracks " "WHERE " "tracks.`Title` = :title AND " - "tracks.`AlbumTitle` = :album AND " - "tracks.`TrackNumber` = :trackNumber AND " - "tracks.`DiscNumber` = :discNumber AND " + "(tracks.`AlbumTitle` = :album OR (:album IS NULL AND tracks.`AlbumTitle` IS NULL)) AND " + "(tracks.`TrackNumber` = :trackNumber OR (:trackNumber IS NULL AND tracks.`TrackNumber` IS NULL)) AND " + "(tracks.`DiscNumber` = :discNumber OR (:discNumber IS NULL AND tracks.`DiscNumber` IS NULL)) AND " "tracks.`ArtistName` = :artist"); auto result = prepareQuery(d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery, selectTrackQueryText); @@ -5632,9 +5632,13 @@ d->mInsertTrackQuery.bindValue(QStringLiteral(":albumPath"), trackPath); if (oneTrack.trackNumberIsValid()) { d->mInsertTrackQuery.bindValue(QStringLiteral(":trackNumber"), oneTrack.trackNumber()); + } else { + d->mInsertTrackQuery.bindValue(QStringLiteral(":trackNumber"), {}); } if (oneTrack.discNumberIsValid()) { d->mInsertTrackQuery.bindValue(QStringLiteral(":discNumber"), oneTrack.discNumber()); + } else { + d->mInsertTrackQuery.bindValue(QStringLiteral(":discNumber"), {}); } d->mInsertTrackQuery.bindValue(QStringLiteral(":trackDuration"), QVariant::fromValue(oneTrack.duration().msecsSinceStartOfDay())); d->mInsertTrackQuery.bindValue(QStringLiteral(":trackRating"), oneTrack.rating()); @@ -5657,12 +5661,18 @@ d->mInsertTrackQuery.bindValue(QStringLiteral(":year"), oneTrack.year()); if (oneTrack.channelsIsValid()) { d->mInsertTrackQuery.bindValue(QStringLiteral(":channels"), oneTrack.channels()); + } else { + d->mInsertTrackQuery.bindValue(QStringLiteral(":channels"), {}); } if (oneTrack.bitRateIsValid()) { d->mInsertTrackQuery.bindValue(QStringLiteral(":bitRate"), oneTrack.bitRate()); + } else { + d->mInsertTrackQuery.bindValue(QStringLiteral(":bitRate"), {}); } if (oneTrack.sampleRateIsValid()) { d->mInsertTrackQuery.bindValue(QStringLiteral(":sampleRate"), oneTrack.sampleRate()); + } else { + d->mInsertTrackQuery.bindValue(QStringLiteral(":sampleRate"), {}); } d->mInsertTrackQuery.bindValue(QStringLiteral(":hasEmbeddedCover"), oneTrack.hasEmbeddedCover()); @@ -6455,8 +6465,8 @@ return result; } -qulonglong DatabaseInterface::internalTrackIdFromTitleAlbumTracDiscNumber(const QString &title, const QString &artist, const QString &album, - int trackNumber, int discNumber) +qulonglong DatabaseInterface::internalTrackIdFromTitleAlbumTracDiscNumber(const QString &title, const QString &artist, const std::optional &album, + std::optional trackNumber, std::optional discNumber) { auto result = qulonglong(0); @@ -6466,9 +6476,21 @@ d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":title"), title); d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":artist"), artist); - d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":album"), album); - d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":trackNumber"), trackNumber); - d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":discNumber"), discNumber); + if (album.has_value()) { + d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":album"), album.value()); + } else { + d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":album"), {}); + } + if (trackNumber.has_value()) { + d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":trackNumber"), trackNumber.value()); + } else { + d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":trackNumber"), {}); + } + if (discNumber.has_value()) { + d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":discNumber"), discNumber.value()); + } else { + d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":discNumber"), {}); + } auto queryResult = execQuery(d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery); diff --git a/src/manageaudioplayer.cpp b/src/manageaudioplayer.cpp --- a/src/manageaudioplayer.cpp +++ b/src/manageaudioplayer.cpp @@ -557,8 +557,9 @@ return; } - if (*itTitle != mCurrentTrack.data(mTitleRole) || *itArtistName != mCurrentTrack.data(mArtistNameRole) || - *itAlbumName != mCurrentTrack.data(mAlbumNameRole)) { + if (*itTitle != mCurrentTrack.data(mTitleRole) || + (itArtistName->isValid() && *itArtistName != mCurrentTrack.data(mArtistNameRole)) || + (itAlbumName->isValid() && *itAlbumName != mCurrentTrack.data(mAlbumNameRole))) { if (mCurrentTrack.isValid() && mCurrentTrack.data(mTitleRole).isValid() && mCurrentTrack.data(mArtistNameRole).isValid() && mCurrentTrack.data(mAlbumNameRole).isValid()) { mPersistentState.clear(); diff --git a/src/mediaplaylist.h b/src/mediaplaylist.h --- a/src/mediaplaylist.h +++ b/src/mediaplaylist.h @@ -175,7 +175,7 @@ void hideUndoInline(); - void newTrackByNameInList(const QString &title, const QString &artist, const QString &album, int trackNumber, int discNumber); + void newTrackByNameInList(const QVariant &title, const QVariant &artist, const QVariant &album, const QVariant &trackNumber, const QVariant &discNumber); void newEntryInList(qulonglong newDatabaseId, const QString &entryTitle, @@ -319,6 +319,12 @@ mTrackNumber(trackNumber), mDiscNumber(discNumber), mEntryType(entryType) { } + MediaPlayListEntry(QVariant title, QVariant artist, QVariant album, QVariant trackNumber, + QVariant discNumber, ElisaUtils::PlayListEntryType entryType = ElisaUtils::Unknown) + : mTitle(std::move(title)), mAlbum(std::move(album)), mArtist(std::move(artist)), + mTrackNumber(std::move(trackNumber)), mDiscNumber(std::move(discNumber)), mEntryType(entryType) { + } + explicit MediaPlayListEntry(const MusicAudioTrack &track) : mTitle(track.title()), mAlbum(track.albumName()), mTrackNumber(track.trackNumber()), mDiscNumber(track.discNumber()), mId(track.databaseId()), mIsValid(true) { @@ -353,9 +359,9 @@ QVariant mTrackUrl; - QVariant mTrackNumber = -1; + QVariant mTrackNumber; - QVariant mDiscNumber = -1; + QVariant mDiscNumber; qulonglong mId = 0; diff --git a/src/mediaplaylist.cpp b/src/mediaplaylist.cpp --- a/src/mediaplaylist.cpp +++ b/src/mediaplaylist.cpp @@ -146,7 +146,14 @@ d->mTrackData[index.row()][TrackDataType::key_type::ImageUrlRole].toUrl().toString()}}.toJson(); break; default: - result = d->mTrackData[index.row()][static_cast(role)]; + const auto &trackData = d->mTrackData[index.row()]; + auto roleEnum = static_cast(role); + auto itData = trackData.find(roleEnum); + if (itData != trackData.end()) { + result = itData.value(); + } else { + result = {}; + } } } else { switch(role) @@ -338,11 +345,11 @@ Q_EMIT newEntryInList(0, entryString, ElisaUtils::FileName); } } else { - Q_EMIT newTrackByNameInList(newEntry.mTitle.toString(), - newEntry.mArtist.toString(), - newEntry.mAlbum.toString(), - newEntry.mTrackNumber.toInt(), - newEntry.mDiscNumber.toInt()); + Q_EMIT newTrackByNameInList(newEntry.mTitle, + newEntry.mArtist, + newEntry.mAlbum, + newEntry.mTrackNumber, + newEntry.mDiscNumber); } } else { Q_EMIT newEntryInList(newEntry.mId, {}, ElisaUtils::Track); @@ -710,9 +717,21 @@ oneData.push_back(oneTrack.title()); oneData.push_back(oneTrack.artist()); - oneData.push_back(oneTrack.album()); - oneData.push_back(QString::number(oneTrack.trackNumber())); - oneData.push_back(QString::number(oneTrack.discNumber())); + if (oneTrack.hasAlbum()) { + oneData.push_back(oneTrack.album()); + } else { + oneData.push_back({}); + } + if (oneTrack.hasTrackNumber()) { + oneData.push_back(QString::number(oneTrack.trackNumber())); + } else { + oneData.push_back({}); + } + if (oneTrack.hasDiscNumber()) { + oneData.push_back(QString::number(oneTrack.discNumber())); + } else { + oneData.push_back({}); + } oneData.push_back(QString::number(oneEntry.mEntryType)); result.push_back(QVariant(oneData)); @@ -778,10 +797,10 @@ auto restoredTitle = trackData[0]; auto restoredArtist = trackData[1]; auto restoredAlbum = trackData[2]; - auto restoredTrackNumber = trackData[3].toInt(); - auto restoredDiscNumber = trackData[4].toInt(); + auto restoredTrackNumber = trackData[3]; + auto restoredDiscNumber = trackData[4]; - ElisaUtils::PlayListEntryType mEntryType = static_cast(trackData[5].toInt()); + auto mEntryType = static_cast(trackData[5].toInt()); enqueueRestoredEntry({restoredTitle, restoredArtist, restoredAlbum, restoredTrackNumber, restoredDiscNumber, mEntryType}); } @@ -893,19 +912,23 @@ } continue; } else if (oneEntry.mEntryType != ElisaUtils::Artist && !oneEntry.mIsValid && !oneEntry.mTrackUrl.isValid()) { - if (track.title() != oneEntry.mTitle) { + if (track.find(TrackDataType::key_type::TitleRole) != track.end() && + track.title() != oneEntry.mTitle) { continue; } - if (track.album() != oneEntry.mAlbum) { + if (track.find(TrackDataType::key_type::AlbumRole) != track.end() && + track.album() != oneEntry.mAlbum) { continue; } - if (track.trackNumber() != oneEntry.mTrackNumber) { + if (track.find(TrackDataType::key_type::TrackNumberRole) != track.end() && + track.trackNumber() != oneEntry.mTrackNumber) { continue; } - if (track.discNumber() != oneEntry.mDiscNumber) { + if (track.find(TrackDataType::key_type::DiscNumberRole) != track.end() && + track.discNumber() != oneEntry.mDiscNumber) { continue; } diff --git a/src/trackslistener.h b/src/trackslistener.h --- a/src/trackslistener.h +++ b/src/trackslistener.h @@ -63,7 +63,7 @@ void trackModified(const TracksListener::TrackDataType &modifiedTrack); - void trackByNameInList(const QString &title, const QString &artist, const QString &album, int trackNumber, int discNumber); + void trackByNameInList(const QVariant &title, const QVariant &artist, const QVariant &album, const QVariant &trackNumber, const QVariant &discNumber); void newEntryInList(qulonglong newDatabaseId, const QString &entryTitle, diff --git a/src/trackslistener.cpp b/src/trackslistener.cpp --- a/src/trackslistener.cpp +++ b/src/trackslistener.cpp @@ -66,17 +66,17 @@ } for (auto itTrack = d->mTracksByNameSet.begin(); itTrack != d->mTracksByNameSet.end(); ) { - if (std::get<0>(*itTrack) != oneTrack.title()) { + if (!std::get<0>(*itTrack).isEmpty() && std::get<0>(*itTrack) != oneTrack.title()) { ++itTrack; continue; } - if (std::get<1>(*itTrack) != oneTrack.artist()) { + if (!std::get<1>(*itTrack).isEmpty() && std::get<1>(*itTrack) != oneTrack.artist()) { ++itTrack; continue; } - if (std::get<2>(*itTrack) != oneTrack.album()) { + if (!std::get<2>(*itTrack).isEmpty() && std::get<2>(*itTrack) != oneTrack.album()) { ++itTrack; continue; } @@ -113,11 +113,33 @@ } } -void TracksListener::trackByNameInList(const QString &title, const QString &artist, const QString &album, int trackNumber, int discNumber) +void TracksListener::trackByNameInList(const QVariant &title, const QVariant &artist, const QVariant &album, + const QVariant &trackNumber, const QVariant &discNumber) { - auto newTrackId = d->mDatabase->trackIdFromTitleAlbumTrackDiscNumber(title, artist, album, trackNumber, discNumber); + const auto realTitle = title.toString(); + const auto realArtist = artist.toString(); + const auto albumIsValid = !album.isNull() && album.isValid() && !album.toString().isEmpty(); + auto realAlbum = std::optional{}; + if (albumIsValid) { + realAlbum = album.toString(); + } + auto trackNumberIsValid = bool{}; + const auto trackNumberValue = trackNumber.toInt(&trackNumberIsValid); + auto realTrackNumber = std::optional{}; + if (trackNumberIsValid) { + realTrackNumber = trackNumberValue; + } + auto discNumberIsValid = bool{}; + const auto discNumberValue = discNumber.toInt(&discNumberIsValid); + auto realDiscNumber = std::optional{}; + if (discNumberIsValid) { + realDiscNumber = discNumberValue; + } + + auto newTrackId = d->mDatabase->trackIdFromTitleAlbumTrackDiscNumber(realTitle, realArtist, realAlbum, + realTrackNumber, realDiscNumber); if (newTrackId == 0) { - auto newTrack = std::tuple(title, artist, album, trackNumber, discNumber); + auto newTrack = std::tuple(realTitle, realArtist, album.toString(), trackNumber.toInt(), discNumber.toInt()); d->mTracksByNameSet.push_back(newTrack); return;