diff --git a/autotests/trackmetadatamodeltest.cpp b/autotests/trackmetadatamodeltest.cpp index 0589a408..e595f60c 100644 --- a/autotests/trackmetadatamodeltest.cpp +++ b/autotests/trackmetadatamodeltest.cpp @@ -1,148 +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 "databasetestdata.h" #include #include #include 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); + QCOMPARE(myModel.rowCount(), 2); } 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); + myModel.initializeById(ElisaUtils::Track, 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); + QCOMPARE(myModel.rowCount(), 12); 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); + QCOMPARE(myModel.rowCount(), 12); 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); + QCOMPARE(myModel.rowCount(), 13); } }; QTEST_GUILESS_MAIN(TrackMetadataModelTests) #include "trackmetadatamodeltest.moc" diff --git a/src/databaseinterface.cpp b/src/databaseinterface.cpp index 7a906635..aecd9608 100644 --- a/src/databaseinterface.cpp +++ b/src/databaseinterface.cpp @@ -1,7737 +1,7733 @@ /* * 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 "databaseinterface.h" #include "databaseLogging.h" #include "musicaudiotrack.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include class DatabaseInterfacePrivate { public: DatabaseInterfacePrivate(const QSqlDatabase &tracksDatabase) : mTracksDatabase(tracksDatabase), mSelectAlbumQuery(mTracksDatabase), mSelectTrackQuery(mTracksDatabase), mSelectAlbumIdFromTitleQuery(mTracksDatabase), mInsertAlbumQuery(mTracksDatabase), mSelectTrackIdFromTitleAlbumIdArtistQuery(mTracksDatabase), mInsertTrackQuery(mTracksDatabase), mSelectTracksFromArtist(mTracksDatabase), mSelectTrackFromIdQuery(mTracksDatabase), mSelectRadioFromIdQuery(mTracksDatabase), mSelectCountAlbumsForArtistQuery(mTracksDatabase), mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery(mTracksDatabase), mSelectAllAlbumsFromArtistQuery(mTracksDatabase), mSelectAllArtistsQuery(mTracksDatabase), mInsertArtistsQuery(mTracksDatabase), mSelectArtistByNameQuery(mTracksDatabase), mSelectArtistQuery(mTracksDatabase), mUpdateTrackStatistics(mTracksDatabase), mRemoveTrackQuery(mTracksDatabase), mRemoveAlbumQuery(mTracksDatabase), mRemoveArtistQuery(mTracksDatabase), mSelectAllTracksQuery(mTracksDatabase), mSelectAllRadiosQuery(mTracksDatabase), mInsertTrackMapping(mTracksDatabase), mUpdateTrackFirstPlayStatistics(mTracksDatabase), mInsertMusicSource(mTracksDatabase), mSelectMusicSource(mTracksDatabase), mUpdateTrackPriority(mTracksDatabase), mUpdateTrackFileModifiedTime(mTracksDatabase), mSelectTracksMapping(mTracksDatabase), mSelectTracksMappingPriority(mTracksDatabase), mSelectRadioIdFromHttpAddress(mTracksDatabase), mUpdateAlbumArtUriFromAlbumIdQuery(mTracksDatabase), mSelectTracksMappingPriorityByTrackId(mTracksDatabase), mSelectAlbumIdsFromArtist(mTracksDatabase), mSelectAllTrackFilesQuery(mTracksDatabase), mRemoveTracksMappingFromSource(mTracksDatabase), mRemoveTracksMapping(mTracksDatabase), mSelectTracksWithoutMappingQuery(mTracksDatabase), mSelectAlbumIdFromTitleAndArtistQuery(mTracksDatabase), mSelectAlbumIdFromTitleWithoutArtistQuery(mTracksDatabase), mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery(mTracksDatabase), mSelectAlbumArtUriFromAlbumIdQuery(mTracksDatabase), mInsertComposerQuery(mTracksDatabase), mSelectComposerByNameQuery(mTracksDatabase), mSelectComposerQuery(mTracksDatabase), mInsertLyricistQuery(mTracksDatabase), mSelectLyricistByNameQuery(mTracksDatabase), mSelectLyricistQuery(mTracksDatabase), mInsertGenreQuery(mTracksDatabase), mSelectGenreByNameQuery(mTracksDatabase), mSelectGenreQuery(mTracksDatabase), mSelectAllTracksShortQuery(mTracksDatabase), mSelectAllAlbumsShortQuery(mTracksDatabase), mSelectAllComposersQuery(mTracksDatabase), mSelectAllLyricistsQuery(mTracksDatabase), mSelectCountAlbumsForComposerQuery(mTracksDatabase), mSelectCountAlbumsForLyricistQuery(mTracksDatabase), mSelectAllGenresQuery(mTracksDatabase), mSelectGenreForArtistQuery(mTracksDatabase), mSelectGenreForAlbumQuery(mTracksDatabase), mUpdateTrackQuery(mTracksDatabase), mUpdateAlbumArtistQuery(mTracksDatabase), mUpdateRadioQuery(mTracksDatabase), mUpdateAlbumArtistInTracksQuery(mTracksDatabase), mQueryMaximumTrackIdQuery(mTracksDatabase), mQueryMaximumAlbumIdQuery(mTracksDatabase), mQueryMaximumArtistIdQuery(mTracksDatabase), mQueryMaximumLyricistIdQuery(mTracksDatabase), mQueryMaximumComposerIdQuery(mTracksDatabase), mQueryMaximumGenreIdQuery(mTracksDatabase), mSelectAllArtistsWithGenreFilterQuery(mTracksDatabase), mSelectAllAlbumsShortWithGenreArtistFilterQuery(mTracksDatabase), mSelectAllAlbumsShortWithArtistFilterQuery(mTracksDatabase), mSelectAllRecentlyPlayedTracksQuery(mTracksDatabase), mSelectAllFrequentlyPlayedTracksQuery(mTracksDatabase), mClearTracksTable(mTracksDatabase), mClearAlbumsTable(mTracksDatabase), mClearArtistsTable(mTracksDatabase), mClearComposerTable(mTracksDatabase), mClearGenreTable(mTracksDatabase), mClearLyricistTable(mTracksDatabase), mArtistMatchGenreQuery(mTracksDatabase), mSelectTrackIdQuery(mTracksDatabase), mInsertRadioQuery(mTracksDatabase), mDeleteRadioQuery(mTracksDatabase) { } QSqlDatabase mTracksDatabase; QSqlQuery mSelectAlbumQuery; QSqlQuery mSelectTrackQuery; QSqlQuery mSelectAlbumIdFromTitleQuery; QSqlQuery mInsertAlbumQuery; QSqlQuery mSelectTrackIdFromTitleAlbumIdArtistQuery; QSqlQuery mInsertTrackQuery; QSqlQuery mSelectTracksFromArtist; QSqlQuery mSelectTrackFromIdQuery; QSqlQuery mSelectRadioFromIdQuery; QSqlQuery mSelectCountAlbumsForArtistQuery; QSqlQuery mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery; QSqlQuery mSelectAllAlbumsFromArtistQuery; QSqlQuery mSelectAllArtistsQuery; QSqlQuery mInsertArtistsQuery; QSqlQuery mSelectArtistByNameQuery; QSqlQuery mSelectArtistQuery; QSqlQuery mUpdateTrackStatistics; QSqlQuery mRemoveTrackQuery; QSqlQuery mRemoveAlbumQuery; QSqlQuery mRemoveArtistQuery; QSqlQuery mSelectAllTracksQuery; QSqlQuery mSelectAllRadiosQuery; QSqlQuery mInsertTrackMapping; QSqlQuery mUpdateTrackFirstPlayStatistics; QSqlQuery mInsertMusicSource; QSqlQuery mSelectMusicSource; QSqlQuery mUpdateTrackPriority; QSqlQuery mUpdateTrackFileModifiedTime; QSqlQuery mSelectTracksMapping; QSqlQuery mSelectTracksMappingPriority; QSqlQuery mSelectRadioIdFromHttpAddress; QSqlQuery mUpdateAlbumArtUriFromAlbumIdQuery; QSqlQuery mSelectTracksMappingPriorityByTrackId; QSqlQuery mSelectAlbumIdsFromArtist; QSqlQuery mSelectAllTrackFilesQuery; QSqlQuery mRemoveTracksMappingFromSource; QSqlQuery mRemoveTracksMapping; QSqlQuery mSelectTracksWithoutMappingQuery; QSqlQuery mSelectAlbumIdFromTitleAndArtistQuery; QSqlQuery mSelectAlbumIdFromTitleWithoutArtistQuery; QSqlQuery mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery; QSqlQuery mSelectAlbumArtUriFromAlbumIdQuery; QSqlQuery mInsertComposerQuery; QSqlQuery mSelectComposerByNameQuery; QSqlQuery mSelectComposerQuery; QSqlQuery mInsertLyricistQuery; QSqlQuery mSelectLyricistByNameQuery; QSqlQuery mSelectLyricistQuery; QSqlQuery mInsertGenreQuery; QSqlQuery mSelectGenreByNameQuery; QSqlQuery mSelectGenreQuery; QSqlQuery mSelectAllTracksShortQuery; QSqlQuery mSelectAllAlbumsShortQuery; QSqlQuery mSelectAllComposersQuery; QSqlQuery mSelectAllLyricistsQuery; QSqlQuery mSelectCountAlbumsForComposerQuery; QSqlQuery mSelectCountAlbumsForLyricistQuery; QSqlQuery mSelectAllGenresQuery; QSqlQuery mSelectGenreForArtistQuery; QSqlQuery mSelectGenreForAlbumQuery; QSqlQuery mUpdateTrackQuery; QSqlQuery mUpdateAlbumArtistQuery; QSqlQuery mUpdateRadioQuery; QSqlQuery mUpdateAlbumArtistInTracksQuery; QSqlQuery mQueryMaximumTrackIdQuery; QSqlQuery mQueryMaximumAlbumIdQuery; QSqlQuery mQueryMaximumArtistIdQuery; QSqlQuery mQueryMaximumLyricistIdQuery; QSqlQuery mQueryMaximumComposerIdQuery; QSqlQuery mQueryMaximumGenreIdQuery; QSqlQuery mSelectAllArtistsWithGenreFilterQuery; QSqlQuery mSelectAllAlbumsShortWithGenreArtistFilterQuery; QSqlQuery mSelectAllAlbumsShortWithArtistFilterQuery; QSqlQuery mSelectAllRecentlyPlayedTracksQuery; QSqlQuery mSelectAllFrequentlyPlayedTracksQuery; QSqlQuery mClearTracksTable; QSqlQuery mClearAlbumsTable; QSqlQuery mClearArtistsTable; QSqlQuery mClearComposerTable; QSqlQuery mClearGenreTable; QSqlQuery mClearLyricistTable; QSqlQuery mArtistMatchGenreQuery; QSqlQuery mSelectTrackIdQuery; QSqlQuery mInsertRadioQuery; QSqlQuery mDeleteRadioQuery; QSet mModifiedTrackIds; QSet mModifiedAlbumIds; QSet mModifiedArtistIds; QSet mInsertedTracks; QSet mInsertedAlbums; QSet mInsertedArtists; qulonglong mAlbumId = 1; qulonglong mArtistId = 1; qulonglong mComposerId = 1; qulonglong mLyricistId = 1; qulonglong mGenreId = 1; qulonglong mTrackId = 1; QAtomicInt mStopRequest = 0; bool mInitFinished = false; bool mIsInBadState = false; }; DatabaseInterface::DatabaseInterface(QObject *parent) : QObject(parent), d(nullptr) { } DatabaseInterface::~DatabaseInterface() { if (d) { d->mTracksDatabase.close(); } } void DatabaseInterface::init(const QString &dbName, const QString &databaseFileName) { QSqlDatabase tracksDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName); if (!databaseFileName.isEmpty()) { tracksDatabase.setDatabaseName(QStringLiteral("file:") + databaseFileName); } else { tracksDatabase.setDatabaseName(QStringLiteral("file:memdb1?mode=memory")); } tracksDatabase.setConnectOptions(QStringLiteral("foreign_keys = ON;locking_mode = EXCLUSIVE;QSQLITE_OPEN_URI;QSQLITE_BUSY_TIMEOUT=500000")); auto result = tracksDatabase.open(); if (result) { qCDebug(orgKdeElisaDatabase) << "database open"; } else { qCDebug(orgKdeElisaDatabase) << "database not open"; } qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::init" << (tracksDatabase.driver()->hasFeature(QSqlDriver::Transactions) ? "yes" : "no"); tracksDatabase.exec(QStringLiteral("PRAGMA foreign_keys = ON;")); d = std::make_unique(tracksDatabase); initDatabase(); initRequest(); if (!databaseFileName.isEmpty()) { reloadExistingDatabase(); } } qulonglong DatabaseInterface::albumIdFromTitleAndArtist(const QString &title, const QString &artist, const QString &albumPath) { auto result = qulonglong{0}; auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalAlbumIdFromTitleAndArtist(title, artist, albumPath); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::allTracksData() { auto result = ListTrackDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalAllTracksPartialData(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListRadioDataType DatabaseInterface::allRadiosData() { auto result = ListRadioDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalAllRadiosPartialData(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::recentlyPlayedTracksData(int count) { auto result = ListTrackDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalRecentlyPlayedTracksData(count); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::frequentlyPlayedTracksData(int count) { auto result = ListTrackDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalFrequentlyPlayedTracksData(count); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListAlbumDataType DatabaseInterface::allAlbumsData() { auto result = ListAlbumDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalAllAlbumsPartialData(d->mSelectAllAlbumsShortQuery); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListAlbumDataType DatabaseInterface::allAlbumsDataByGenreAndArtist(const QString &genre, const QString &artist) { auto result = ListAlbumDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } d->mSelectAllAlbumsShortWithGenreArtistFilterQuery.bindValue(QStringLiteral(":artistFilter"), artist); d->mSelectAllAlbumsShortWithGenreArtistFilterQuery.bindValue(QStringLiteral(":genreFilter"), genre); result = internalAllAlbumsPartialData(d->mSelectAllAlbumsShortWithGenreArtistFilterQuery); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListAlbumDataType DatabaseInterface::allAlbumsDataByArtist(const QString &artist) { auto result = ListAlbumDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } d->mSelectAllAlbumsShortWithArtistFilterQuery.bindValue(QStringLiteral(":artistFilter"), artist); result = internalAllAlbumsPartialData(d->mSelectAllAlbumsShortWithArtistFilterQuery); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::AlbumDataType DatabaseInterface::albumDataFromDatabaseId(qulonglong id) { auto result = DatabaseInterface::AlbumDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalOneAlbumPartialData(id); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::albumData(qulonglong databaseId) { auto result = ListTrackDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } d->mSelectTrackQuery.bindValue(QStringLiteral(":albumId"), databaseId); auto queryResult = execQuery(d->mSelectTrackQuery); if (!queryResult || !d->mSelectTrackQuery.isSelect() || !d->mSelectTrackQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::albumData" << d->mSelectTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::albumData" << d->mSelectTrackQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::albumData" << d->mSelectTrackQuery.lastError(); } while (d->mSelectTrackQuery.next()) { const auto ¤tRecord = d->mSelectTrackQuery.record(); result.push_back(buildTrackDataFromDatabaseRecord(currentRecord)); } d->mSelectTrackQuery.finish(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListArtistDataType DatabaseInterface::allArtistsData() { auto result = ListArtistDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalAllArtistsPartialData(d->mSelectAllArtistsQuery); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListArtistDataType DatabaseInterface::allArtistsDataByGenre(const QString &genre) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::allArtistsDataByGenre" << genre; auto result = ListArtistDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } d->mSelectAllArtistsWithGenreFilterQuery.bindValue(QStringLiteral(":genreFilter"), genre); result = internalAllArtistsPartialData(d->mSelectAllArtistsWithGenreFilterQuery); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::ListGenreDataType DatabaseInterface::allGenresData() { auto result = ListGenreDataType{}; if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalAllGenresPartialData(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } bool DatabaseInterface::internalArtistMatchGenre(qulonglong databaseId, const QString &genre) { auto result = true; if (!d) { return result; } d->mArtistMatchGenreQuery.bindValue(QStringLiteral(":databaseId"), databaseId); d->mArtistMatchGenreQuery.bindValue(QStringLiteral(":genreFilter"), genre); auto queryResult = execQuery(d->mArtistMatchGenreQuery); if (!queryResult || !d->mArtistMatchGenreQuery.isSelect() || !d->mArtistMatchGenreQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::artistMatchGenre" << d->mArtistMatchGenreQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::artistMatchGenre" << d->mArtistMatchGenreQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::artistMatchGenre" << d->mArtistMatchGenreQuery.lastError(); d->mArtistMatchGenreQuery.finish(); auto transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } result = d->mArtistMatchGenreQuery.next(); d->mArtistMatchGenreQuery.finish(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalArtistMatchGenre" << databaseId << (result ? "match" : "does not match"); return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::tracksDataFromAuthor(const QString &ArtistName) { auto allTracks = ListTrackDataType{}; auto transactionResult = startTransaction(); if (!transactionResult) { return allTracks; } allTracks = internalTracksFromAuthor(ArtistName); transactionResult = finishTransaction(); if (!transactionResult) { return allTracks; } return allTracks; } DatabaseInterface::TrackDataType DatabaseInterface::trackDataFromDatabaseId(qulonglong id) { auto result = TrackDataType(); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalOneTrackPartialData(id); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } DatabaseInterface::TrackDataType DatabaseInterface::radioDataFromDatabaseId(qulonglong id) { auto result = TrackDataType(); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalOneRadioPartialData(id); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } qulonglong DatabaseInterface::trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const std::optional &album, std::optional trackNumber, std::optional discNumber) { auto result = qulonglong(0); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalTrackIdFromTitleAlbumTracDiscNumber(title, artist, album, trackNumber, discNumber); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } qulonglong DatabaseInterface::trackIdFromFileName(const QUrl &fileName) { auto result = qulonglong(0); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalTrackIdFromFileName(fileName); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } void DatabaseInterface::applicationAboutToQuit() { d->mStopRequest = 1; } void DatabaseInterface::askRestoredTracks() { auto transactionResult = startTransaction(); if (!transactionResult) { return; } auto result = internalAllFileName(); Q_EMIT restoredTracks(result); transactionResult = finishTransaction(); if (!transactionResult) { return; } } void DatabaseInterface::trackHasStartedPlaying(const QUrl &fileName, const QDateTime &time) { auto transactionResult = startTransaction(); if (!transactionResult) { return; } updateTrackStatistics(fileName, time); auto trackId = internalTrackIdFromFileName(fileName); if (trackId != 0) { Q_EMIT trackModified(internalOneTrackPartialData(trackId)); } transactionResult = finishTransaction(); if (!transactionResult) { return; } } void DatabaseInterface::clearData() { auto transactionResult = startTransaction(); if (!transactionResult) { return; } auto queryResult = execQuery(d->mClearTracksTable); if (!queryResult || !d->mClearTracksTable.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksTable.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksTable.lastError(); } d->mClearTracksTable.finish(); queryResult = execQuery(d->mClearAlbumsTable); if (!queryResult || !d->mClearAlbumsTable.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearAlbumsTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearAlbumsTable.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearAlbumsTable.lastError(); } d->mClearAlbumsTable.finish(); queryResult = execQuery(d->mClearComposerTable); if (!queryResult || !d->mClearComposerTable.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearComposerTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearComposerTable.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearComposerTable.lastError(); } d->mClearComposerTable.finish(); queryResult = execQuery(d->mClearLyricistTable); if (!queryResult || !d->mClearLyricistTable.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearLyricistTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearLyricistTable.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearLyricistTable.lastError(); } d->mClearLyricistTable.finish(); queryResult = execQuery(d->mClearGenreTable); if (!queryResult || !d->mClearGenreTable.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearGenreTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearGenreTable.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearGenreTable.lastError(); } d->mClearGenreTable.finish(); queryResult = execQuery(d->mClearArtistsTable); if (!queryResult || !d->mClearArtistsTable.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearArtistsTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearArtistsTable.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearArtistsTable.lastError(); } d->mClearArtistsTable.finish(); transactionResult = finishTransaction(); if (!transactionResult) { return; } Q_EMIT cleanedDatabase(); } void DatabaseInterface::initChangesTrackers() { d->mModifiedTrackIds.clear(); d->mModifiedAlbumIds.clear(); d->mModifiedArtistIds.clear(); d->mInsertedTracks.clear(); d->mInsertedAlbums.clear(); d->mInsertedArtists.clear(); } void DatabaseInterface::recordModifiedTrack(qulonglong trackId) { d->mModifiedTrackIds.insert(trackId); } void DatabaseInterface::recordModifiedAlbum(qulonglong albumId) { d->mModifiedAlbumIds.insert(albumId); } void DatabaseInterface::insertTracksList(const QList &tracks, const QHash &covers) { qCDebug(orgKdeElisaDatabase()) << "DatabaseInterface::insertTracksList" << tracks.count(); if (d->mStopRequest == 1) { Q_EMIT finishInsertingTracksList(); return; } auto transactionResult = startTransaction(); if (!transactionResult) { Q_EMIT finishInsertingTracksList(); return; } initChangesTrackers(); for(const auto &oneTrack : tracks) { d->mSelectTracksMapping.bindValue(QStringLiteral(":fileName"), oneTrack.resourceURI()); auto result = execQuery(d->mSelectTracksMapping); if (!result || !d->mSelectTracksMapping.isSelect() || !d->mSelectTracksMapping.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertTracksList" << d->mSelectTracksMapping.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertTracksList" << d->mSelectTracksMapping.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertTracksList" << d->mSelectTracksMapping.lastError(); d->mSelectTracksMapping.finish(); rollBackTransaction(); Q_EMIT finishInsertingTracksList(); return; } bool isNewTrack = !d->mSelectTracksMapping.next(); if (isNewTrack) { insertTrackOrigin(oneTrack.resourceURI(), oneTrack.fileModificationTime(), QDateTime::currentDateTime()); } else if (!d->mSelectTracksMapping.record().value(0).isNull() && d->mSelectTracksMapping.record().value(0).toULongLong() != 0) { updateTrackOrigin(oneTrack.resourceURI(), oneTrack.fileModificationTime()); } d->mSelectTracksMapping.finish(); bool isInserted = false; const auto insertedTrackId = internalInsertTrack(oneTrack, covers, isInserted); if (isInserted && insertedTrackId != 0) { d->mInsertedTracks.insert(insertedTrackId); } if (d->mStopRequest == 1) { transactionResult = finishTransaction(); if (!transactionResult) { Q_EMIT finishInsertingTracksList(); return; } Q_EMIT finishInsertingTracksList(); return; } } if (!d->mInsertedArtists.isEmpty()) { ListArtistDataType newArtists; for (auto artistId : qAsConst(d->mInsertedArtists)) { newArtists.push_back({{DatabaseIdRole, artistId}}); } qCInfo(orgKdeElisaDatabase) << "artistsAdded" << newArtists.size(); Q_EMIT artistsAdded(newArtists); } if (!d->mInsertedAlbums.isEmpty()) { ListAlbumDataType newAlbums; for (auto albumId : qAsConst(d->mInsertedAlbums)) { d->mModifiedAlbumIds.remove(albumId); newAlbums.push_back(internalOneAlbumPartialData(albumId)); } qCInfo(orgKdeElisaDatabase) << "albumsAdded" << newAlbums.size(); Q_EMIT albumsAdded(newAlbums); } for (auto albumId : qAsConst(d->mModifiedAlbumIds)) { Q_EMIT albumModified({{DatabaseIdRole, albumId}}, albumId); } if (!d->mInsertedTracks.isEmpty()) { ListTrackDataType newTracks; for (auto trackId : qAsConst(d->mInsertedTracks)) { newTracks.push_back(internalOneTrackPartialData(trackId)); d->mModifiedTrackIds.remove(trackId); } qCInfo(orgKdeElisaDatabase) << "tracksAdded" << newTracks.size(); Q_EMIT tracksAdded(newTracks); } for (auto trackId : qAsConst(d->mModifiedTrackIds)) { Q_EMIT trackModified(internalOneTrackPartialData(trackId)); } transactionResult = finishTransaction(); if (!transactionResult) { Q_EMIT finishInsertingTracksList(); return; } Q_EMIT finishInsertingTracksList(); } void DatabaseInterface::removeTracksList(const QList &removedTracks) { auto transactionResult = startTransaction(); if (!transactionResult) { Q_EMIT finishRemovingTracksList(); return; } initChangesTrackers(); internalRemoveTracksList(removedTracks); if (!d->mInsertedArtists.isEmpty()) { ListArtistDataType newArtists; for (auto artistId : qAsConst(d->mInsertedArtists)) { newArtists.push_back({{DatabaseIdRole, artistId}}); } Q_EMIT artistsAdded(newArtists); } transactionResult = finishTransaction(); if (!transactionResult) { Q_EMIT finishRemovingTracksList(); return; } Q_EMIT finishRemovingTracksList(); } bool DatabaseInterface::startTransaction() const { auto result = false; auto transactionResult = d->mTracksDatabase.transaction(); if (!transactionResult) { qCDebug(orgKdeElisaDatabase) << "transaction failed" << d->mTracksDatabase.lastError() << d->mTracksDatabase.lastError().driverText(); return result; } result = true; return result; } bool DatabaseInterface::finishTransaction() const { auto result = false; auto transactionResult = d->mTracksDatabase.commit(); if (!transactionResult) { qCDebug(orgKdeElisaDatabase) << "commit failed" << d->mTracksDatabase.lastError() << d->mTracksDatabase.lastError().nativeErrorCode(); return result; } result = true; return result; } bool DatabaseInterface::rollBackTransaction() const { auto result = false; auto transactionResult = d->mTracksDatabase.rollback(); if (!transactionResult) { qCDebug(orgKdeElisaDatabase) << "commit failed" << d->mTracksDatabase.lastError() << d->mTracksDatabase.lastError().nativeErrorCode(); return result; } result = true; return result; } void DatabaseInterface::initDatabase() { auto listTables = d->mTracksDatabase.tables(); if (listTables.contains(QStringLiteral("DatabaseVersionV2")) || listTables.contains(QStringLiteral("DatabaseVersionV3")) || listTables.contains(QStringLiteral("DatabaseVersionV4")) || listTables.contains(QStringLiteral("DatabaseVersionV6")) || listTables.contains(QStringLiteral("DatabaseVersionV7")) || listTables.contains(QStringLiteral("DatabaseVersionV8")) || listTables.contains(QStringLiteral("DatabaseVersionV10"))) { qCDebug(orgKdeElisaDatabase()) << "Old database schema unsupported: delete and start from scratch"; qCDebug(orgKdeElisaDatabase()) << "list of old tables" << d->mTracksDatabase.tables(); auto oldTables = QStringList{ QStringLiteral("DatabaseVersionV2"), QStringLiteral("DatabaseVersionV3"), QStringLiteral("DatabaseVersionV4"), QStringLiteral("DatabaseVersionV5"), QStringLiteral("DatabaseVersionV6"), QStringLiteral("DatabaseVersionV7"), QStringLiteral("DatabaseVersionV8"), QStringLiteral("DatabaseVersionV10"), QStringLiteral("AlbumsArtists"), QStringLiteral("TracksArtists"), QStringLiteral("TracksMapping"), QStringLiteral("Tracks"), QStringLiteral("Composer"), QStringLiteral("Genre"), QStringLiteral("Lyricist"), QStringLiteral("Albums"), QStringLiteral("DiscoverSource"), QStringLiteral("Artists"),}; for (const auto &oneTable : oldTables) { if (listTables.indexOf(oneTable) == -1) { continue; } QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE ") + oneTable); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initDatabase" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initDatabase" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } listTables = d->mTracksDatabase.tables(); } listTables = d->mTracksDatabase.tables(); if (listTables.contains(QStringLiteral("DatabaseVersionV5")) && !listTables.contains(QStringLiteral("DatabaseVersionV9"))) { upgradeDatabaseV9(); upgradeDatabaseV11(); upgradeDatabaseV12(); upgradeDatabaseV13(); upgradeDatabaseV14(); checkDatabaseSchema(); } else if (listTables.contains(QStringLiteral("DatabaseVersionV9"))) { if (!listTables.contains(QStringLiteral("DatabaseVersionV11"))) { upgradeDatabaseV11(); } if (!listTables.contains(QStringLiteral("DatabaseVersionV12"))) { upgradeDatabaseV12(); } if (!listTables.contains(QStringLiteral("DatabaseVersionV13"))) { upgradeDatabaseV13(); } if (!listTables.contains(QStringLiteral("DatabaseVersionV14"))) { upgradeDatabaseV14(); } checkDatabaseSchema(); } else { createDatabaseV9(); upgradeDatabaseV11(); upgradeDatabaseV12(); upgradeDatabaseV13(); upgradeDatabaseV14(); } } void DatabaseInterface::createDatabaseV9() { qCInfo(orgKdeElisaDatabase) << "begin creation of v9 database schema"; { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DatabaseVersionV9` (`Version` INTEGER PRIMARY KEY NOT NULL)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DiscoverSource` (`ID` INTEGER PRIMARY KEY NOT NULL, " "`Name` VARCHAR(55) NOT NULL, " "UNIQUE (`Name`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Artists` (`ID` INTEGER PRIMARY KEY NOT NULL, " "`Name` VARCHAR(55) NOT NULL, " "UNIQUE (`Name`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Composer` (`ID` INTEGER PRIMARY KEY NOT NULL, " "`Name` VARCHAR(55) NOT NULL, " "UNIQUE (`Name`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Genre` (`ID` INTEGER PRIMARY KEY NOT NULL, " "`Name` VARCHAR(85) NOT NULL, " "UNIQUE (`Name`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Lyricist` (`ID` INTEGER PRIMARY KEY NOT NULL, " "`Name` VARCHAR(55) NOT NULL, " "UNIQUE (`Name`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Albums` (" "`ID` INTEGER PRIMARY KEY NOT NULL, " "`Title` VARCHAR(55) NOT NULL, " "`ArtistName` VARCHAR(55), " "`AlbumPath` VARCHAR(255) NOT NULL, " "`CoverFileName` VARCHAR(255) NOT NULL, " "UNIQUE (`Title`, `ArtistName`, `AlbumPath`), " "CONSTRAINT fk_artists FOREIGN KEY (`ArtistName`) REFERENCES `Artists`(`Name`) " "ON DELETE CASCADE)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Tracks` (" "`ID` INTEGER PRIMARY KEY NOT NULL, " "`Title` VARCHAR(85) NOT NULL, " "`ArtistName` VARCHAR(55), " "`AlbumTitle` VARCHAR(55), " "`AlbumArtistName` VARCHAR(55), " "`AlbumPath` VARCHAR(255), " "`TrackNumber` INTEGER DEFAULT -1, " "`DiscNumber` INTEGER DEFAULT -1, " "`Duration` INTEGER NOT NULL, " "`Rating` INTEGER NOT NULL DEFAULT 0, " "`Genre` VARCHAR(55), " "`Composer` VARCHAR(55), " "`Lyricist` VARCHAR(55), " "`Comment` VARCHAR(255) DEFAULT '', " "`Year` INTEGER DEFAULT 0, " "`Channels` INTEGER DEFAULT -1, " "`BitRate` INTEGER DEFAULT -1, " "`SampleRate` INTEGER DEFAULT -1, " "`HasEmbeddedCover` BOOLEAN NOT NULL, " "`ImportDate` INTEGER NOT NULL, " "`FirstPlayDate` INTEGER, " "`LastPlayDate` INTEGER, " "`PlayCounter` INTEGER NOT NULL, " "UNIQUE (" "`Title`, `AlbumTitle`, `AlbumArtistName`, " "`AlbumPath`, `TrackNumber`, `DiscNumber`" "), " "CONSTRAINT fk_artist FOREIGN KEY (`ArtistName`) REFERENCES `Artists`(`Name`), " "CONSTRAINT fk_tracks_composer FOREIGN KEY (`Composer`) REFERENCES `Composer`(`Name`), " "CONSTRAINT fk_tracks_lyricist FOREIGN KEY (`Lyricist`) REFERENCES `Lyricist`(`Name`), " "CONSTRAINT fk_tracks_genre FOREIGN KEY (`Genre`) REFERENCES `Genre`(`Name`), " "CONSTRAINT fk_tracks_album FOREIGN KEY (" "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)" "REFERENCES `Albums`(`Title`, `ArtistName`, `AlbumPath`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `TracksMapping` (" "`TrackID` INTEGER NULL, " "`DiscoverID` INTEGER NOT NULL, " "`FileName` VARCHAR(255) NOT NULL, " "`Priority` INTEGER NOT NULL, " "`FileModifiedTime` DATETIME NOT NULL, " "PRIMARY KEY (`FileName`), " "CONSTRAINT TracksUnique UNIQUE (`TrackID`, `Priority`), " "CONSTRAINT fk_tracksmapping_trackID FOREIGN KEY (`TrackID`) REFERENCES `Tracks`(`ID`) ON DELETE CASCADE, " "CONSTRAINT fk_tracksmapping_discoverID FOREIGN KEY (`DiscoverID`) REFERENCES `DiscoverSource`(`ID`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createSchemaQuery.lastError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TitleAlbumsIndex` ON `Albums` " "(`Title`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`ArtistNameAlbumsIndex` ON `Albums` " "(`ArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksAlbumIndex` ON `Tracks` " "(`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`ArtistNameIndex` ON `Tracks` " "(`ArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`AlbumArtistNameIndex` ON `Tracks` " "(`AlbumArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksFileNameIndex` ON `TracksMapping` " "(`FileName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::createDatabaseV9" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } qCInfo(orgKdeElisaDatabase) << "end creation of v9 database schema"; } void DatabaseInterface::upgradeDatabaseV9() { qCInfo(orgKdeElisaDatabase) << "begin update to v9 of database schema"; { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DatabaseVersionV9` (`Version` INTEGER PRIMARY KEY NOT NULL)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery disableForeignKeys(d->mTracksDatabase); auto result = disableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=OFF")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << disableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << disableForeignKeys.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.transaction(); { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `NewAlbums` (" "`ID` INTEGER PRIMARY KEY NOT NULL, " "`Title` VARCHAR(55) NOT NULL, " "`ArtistName` VARCHAR(55), " "`AlbumPath` VARCHAR(255) NOT NULL, " "`CoverFileName` VARCHAR(255) NOT NULL, " "UNIQUE (`Title`, `ArtistName`, `AlbumPath`), " "CONSTRAINT fk_artists FOREIGN KEY (`ArtistName`) REFERENCES `Artists`(`Name`) " "ON DELETE CASCADE)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); } } { QSqlQuery copyDataQuery(d->mTracksDatabase); auto result = copyDataQuery.exec(QStringLiteral("INSERT INTO `NewAlbums` " "SELECT " "album.`ID`, " "album.`Title`, " "artist.`Name`, " "album.`AlbumPath`, " "album.`CoverFileName` " "FROM " "`Albums` album, " "`AlbumsArtists` albumArtistMapping, " "`Artists` artist " "WHERE " "album.`ID` = albumArtistMapping.`AlbumID` AND " "albumArtistMapping.`ArtistID` = artist.`ID`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << copyDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << copyDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `Albums`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `AlbumsArtists`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("ALTER TABLE `NewAlbums` RENAME TO `Albums`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `NewTracks` (" "`ID` INTEGER PRIMARY KEY NOT NULL, " "`Title` VARCHAR(85) NOT NULL, " "`ArtistName` VARCHAR(55), " "`AlbumTitle` VARCHAR(55), " "`AlbumArtistName` VARCHAR(55), " "`AlbumPath` VARCHAR(255), " "`TrackNumber` INTEGER DEFAULT -1, " "`DiscNumber` INTEGER DEFAULT -1, " "`Duration` INTEGER NOT NULL, " "`Rating` INTEGER NOT NULL DEFAULT 0, " "`Genre` VARCHAR(55), " "`Composer` VARCHAR(55), " "`Lyricist` VARCHAR(55), " "`Comment` VARCHAR(255) DEFAULT '', " "`Year` INTEGER DEFAULT 0, " "`Channels` INTEGER DEFAULT -1, " "`BitRate` INTEGER DEFAULT -1, " "`SampleRate` INTEGER DEFAULT -1, " "`HasEmbeddedCover` BOOLEAN NOT NULL, " "`ImportDate` INTEGER NOT NULL, " "`FirstPlayDate` INTEGER, " "`LastPlayDate` INTEGER, " "`PlayCounter` INTEGER NOT NULL, " "UNIQUE (" "`Title`, `AlbumTitle`, `AlbumArtistName`, " "`AlbumPath`, `TrackNumber`, `DiscNumber`" "), " "CONSTRAINT fk_artist FOREIGN KEY (`ArtistName`) REFERENCES `Artists`(`Name`), " "CONSTRAINT fk_tracks_composer FOREIGN KEY (`Composer`) REFERENCES `Composer`(`Name`), " "CONSTRAINT fk_tracks_lyricist FOREIGN KEY (`Lyricist`) REFERENCES `Lyricist`(`Name`), " "CONSTRAINT fk_tracks_genre FOREIGN KEY (`Genre`) REFERENCES `Genre`(`Name`), " "CONSTRAINT fk_tracks_album FOREIGN KEY (" "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)" "REFERENCES `Albums`(`Title`, `ArtistName`, `AlbumPath`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery copyDataQuery(d->mTracksDatabase); auto result = copyDataQuery.exec(QStringLiteral("INSERT INTO `NewTracks` " "(`ID`, `Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`, " "`TrackNumber`, `DiscNumber`, `Duration`, " "`Rating`, `Genre`, `Composer`, " "`Lyricist`, `Comment`, `Year`, " "`Channels`, `BitRate`, `SampleRate`, " "`HasEmbeddedCover`, `ImportDate`, `PlayCounter`) " "SELECT " "track.`ID`, " "track.`Title`, " "artist.`Name`, " "album.`Title`, " "album.`ArtistName`, " "album.`AlbumPath`, " "track.`TrackNumber`, " "track.`DiscNumber`, " "track.`Duration`, " "track.`Rating`, " "genre.`Name`, " "composer.`Name`, " "lyricist.`Name`, " "track.`Comment`, " "track.`Year`, " "track.`Channels`, " "track.`BitRate`, " "track.`SampleRate`, " "FALSE, " "strftime('%s', 'now'), " "0 " "FROM " "`Tracks` track, " "`TracksArtists` trackArtistMapping, " "`Artists` artist, " "`Albums` album " "left join " "`Genre` genre " "on track.`GenreID` = genre.`ID` " "left join " "`Composer` composer " "on track.`ComposerID` = composer.`ID` " "left join " "`Lyricist` lyricist " "on track.`LyricistID` = lyricist.`ID` " "WHERE " "track.`ID` = trackArtistMapping.`TrackID` AND " "trackArtistMapping.`ArtistID` = artist.`ID` AND " "track.`AlbumID` = album.`ID`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << copyDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << copyDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `TracksArtists`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("ALTER TABLE `NewTracks` RENAME TO `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.commit(); { QSqlQuery enableForeignKeys(d->mTracksDatabase); auto result = enableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=ON")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << enableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV9" << enableForeignKeys.lastError(); Q_EMIT databaseError(); } } qCInfo(orgKdeElisaDatabase) << "finished update to v9 of database schema"; } void DatabaseInterface::upgradeDatabaseV11() { qCInfo(orgKdeElisaDatabase) << "begin update to v11 of database schema"; { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DatabaseVersionV11` (`Version` INTEGER PRIMARY KEY NOT NULL)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery disableForeignKeys(d->mTracksDatabase); auto result = disableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=OFF")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << disableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << disableForeignKeys.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.transaction(); { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `TracksData` (" "`DiscoverID` INTEGER NOT NULL, " "`FileName` VARCHAR(255) NOT NULL, " "`FileModifiedTime` DATETIME NOT NULL, " "`ImportDate` INTEGER NOT NULL, " "`FirstPlayDate` INTEGER, " "`LastPlayDate` INTEGER, " "`PlayCounter` INTEGER NOT NULL, " "PRIMARY KEY (`FileName`, `DiscoverID`), " "CONSTRAINT fk_tracksmapping_discoverID FOREIGN KEY (`DiscoverID`) REFERENCES `DiscoverSource`(`ID`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastError(); } } { QSqlQuery copyDataQuery(d->mTracksDatabase); auto result = copyDataQuery.exec(QStringLiteral("INSERT INTO `TracksData` " "SELECT " "m.`DiscoverID`, " "m.`FileName`, " "m.`FileModifiedTime`, " "t.`ImportDate`, " "t.`FirstPlayDate`, " "t.`LastPlayDate`, " "t.`PlayCounter` " "FROM " "`Tracks` t, " "`TracksMapping` m " "WHERE " "t.`ID` = m.`TrackID`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << copyDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << copyDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `NewTracks` (" "`ID` INTEGER PRIMARY KEY AUTOINCREMENT, " "`DiscoverID` INTEGER NOT NULL, " "`FileName` VARCHAR(255) NOT NULL, " "`Priority` INTEGER NOT NULL, " "`Title` VARCHAR(85) NOT NULL, " "`ArtistName` VARCHAR(55), " "`AlbumTitle` VARCHAR(55), " "`AlbumArtistName` VARCHAR(55), " "`AlbumPath` VARCHAR(255), " "`TrackNumber` INTEGER, " "`DiscNumber` INTEGER, " "`Duration` INTEGER NOT NULL, " "`Rating` INTEGER NOT NULL DEFAULT 0, " "`Genre` VARCHAR(55), " "`Composer` VARCHAR(55), " "`Lyricist` VARCHAR(55), " "`Comment` VARCHAR(255), " "`Year` INTEGER, " "`Channels` INTEGER, " "`BitRate` INTEGER, " "`SampleRate` INTEGER, " "`HasEmbeddedCover` BOOLEAN NOT NULL, " "UNIQUE (" "`FileName`" "), " "UNIQUE (" "`Priority`, `Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`" "), " "CONSTRAINT fk_fileName FOREIGN KEY (`FileName`, `DiscoverID`) " "REFERENCES `TracksData`(`FileName`, `DiscoverID`) ON DELETE CASCADE, " "CONSTRAINT fk_artist FOREIGN KEY (`ArtistName`) REFERENCES `Artists`(`Name`), " "CONSTRAINT fk_tracks_composer FOREIGN KEY (`Composer`) REFERENCES `Composer`(`Name`), " "CONSTRAINT fk_tracks_lyricist FOREIGN KEY (`Lyricist`) REFERENCES `Lyricist`(`Name`), " "CONSTRAINT fk_tracks_genre FOREIGN KEY (`Genre`) REFERENCES `Genre`(`Name`), " "CONSTRAINT fk_tracks_discoverID FOREIGN KEY (`DiscoverID`) REFERENCES `DiscoverSource`(`ID`)" "CONSTRAINT fk_tracks_album FOREIGN KEY (" "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)" "REFERENCES `Albums`(`Title`, `ArtistName`, `AlbumPath`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery copyDataQuery(d->mTracksDatabase); auto result = copyDataQuery.exec(QStringLiteral("INSERT OR IGNORE INTO `NewTracks` " "(" "`DiscoverID`, " "`FileName`, " "`Priority`, " "`Title`, " "`ArtistName`, " "`AlbumTitle`, " "`AlbumArtistName`, " "`AlbumPath`, " "`TrackNumber`, " "`DiscNumber`, " "`Duration`, " "`Rating`, " "`Genre`, " "`Composer`, " "`Lyricist`, " "`Comment`, " "`Year`, " "`Channels`, " "`BitRate`, " "`SampleRate`, " "`HasEmbeddedCover`" ") " "SELECT " "m.`DiscoverID`, " "m.`FileName`, " "m.`Priority`, " "t.`Title`, " "t.`ArtistName`, " "t.`AlbumTitle`, " "t.`AlbumArtistName`, " "t.`AlbumPath`, " "t.`TrackNumber`, " "t.`DiscNumber`, " "t.`Duration`, " "t.`Rating`, " "t.`Genre`, " "t.`Composer`, " "t.`Lyricist`, " "t.`Comment`, " "t.`Year`, " "t.`Channels`, " "t.`BitRate`, " "t.`SampleRate`, " "t.`HasEmbeddedCover` " "FROM " "`Tracks` t, " "`TracksMapping` m " "WHERE " "t.`ID` = m.`TrackID`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << copyDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << copyDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery updateDataQuery(d->mTracksDatabase); auto result = updateDataQuery.exec(QStringLiteral("UPDATE `NewTracks` " "SET " "`TrackNumber` = NULL " "WHERE " "`TrackNumber` = -1")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery updateDataQuery(d->mTracksDatabase); auto result = updateDataQuery.exec(QStringLiteral("UPDATE `NewTracks` " "SET " "`Channels` = NULL " "WHERE " "`Channels` = -1")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery updateDataQuery(d->mTracksDatabase); auto result = updateDataQuery.exec(QStringLiteral("UPDATE `NewTracks` " "SET " "`BitRate` = NULL " "WHERE " "`BitRate` = -1")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery updateDataQuery(d->mTracksDatabase); auto result = updateDataQuery.exec(QStringLiteral("UPDATE `NewTracks` " "SET " "`SampleRate` = NULL " "WHERE " "`SampleRate` = -1")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << updateDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `TracksMapping`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("ALTER TABLE `NewTracks` RENAME TO `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.commit(); { QSqlQuery enableForeignKeys(d->mTracksDatabase); auto result = enableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=ON")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << enableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << enableForeignKeys.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksAlbumIndex` ON `Tracks` " "(`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`ArtistNameIndex` ON `Tracks` " "(`ArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`AlbumArtistNameIndex` ON `Tracks` " "(`AlbumArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksUniqueData` ON `Tracks` " "(`Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksUniqueDataPriority` ON `Tracks` " "(`Priority`, `Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksFileNameIndex` ON `Tracks` " "(`FileName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV11" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } qCInfo(orgKdeElisaDatabase) << "finished update to v11 of database schema"; } void DatabaseInterface::upgradeDatabaseV12() { qCInfo(orgKdeElisaDatabase) << "begin update to v12 of database schema"; { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DatabaseVersionV12` (`Version` INTEGER PRIMARY KEY NOT NULL)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery disableForeignKeys(d->mTracksDatabase); auto result = disableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=OFF")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << disableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << disableForeignKeys.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.transaction(); { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `NewTracks` (" "`ID` INTEGER PRIMARY KEY AUTOINCREMENT, " "`FileName` VARCHAR(255) NOT NULL, " "`Priority` INTEGER NOT NULL, " "`Title` VARCHAR(85) NOT NULL, " "`ArtistName` VARCHAR(55), " "`AlbumTitle` VARCHAR(55), " "`AlbumArtistName` VARCHAR(55), " "`AlbumPath` VARCHAR(255), " "`TrackNumber` INTEGER, " "`DiscNumber` INTEGER, " "`Duration` INTEGER NOT NULL, " "`Rating` INTEGER NOT NULL DEFAULT 0, " "`Genre` VARCHAR(55), " "`Composer` VARCHAR(55), " "`Lyricist` VARCHAR(55), " "`Comment` VARCHAR(255), " "`Year` INTEGER, " "`Channels` INTEGER, " "`BitRate` INTEGER, " "`SampleRate` INTEGER, " "`HasEmbeddedCover` BOOLEAN NOT NULL, " "UNIQUE (" "`FileName`" "), " "UNIQUE (" "`Priority`, `Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`" "), " "CONSTRAINT fk_fileName FOREIGN KEY (`FileName`) " "REFERENCES `TracksData`(`FileName`) ON DELETE CASCADE, " "CONSTRAINT fk_artist FOREIGN KEY (`ArtistName`) REFERENCES `Artists`(`Name`), " "CONSTRAINT fk_tracks_composer FOREIGN KEY (`Composer`) REFERENCES `Composer`(`Name`), " "CONSTRAINT fk_tracks_lyricist FOREIGN KEY (`Lyricist`) REFERENCES `Lyricist`(`Name`), " "CONSTRAINT fk_tracks_genre FOREIGN KEY (`Genre`) REFERENCES `Genre`(`Name`), " "CONSTRAINT fk_tracks_album FOREIGN KEY (" "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)" "REFERENCES `Albums`(`Title`, `ArtistName`, `AlbumPath`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `NewTracksData` (" "`FileName` VARCHAR(255) NOT NULL, " "`FileModifiedTime` DATETIME NOT NULL, " "`ImportDate` INTEGER NOT NULL, " "`FirstPlayDate` INTEGER, " "`LastPlayDate` INTEGER, " "`PlayCounter` INTEGER NOT NULL, " "PRIMARY KEY (`FileName`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastError(); } } { QSqlQuery copyDataQuery(d->mTracksDatabase); auto result = copyDataQuery.exec(QStringLiteral("INSERT INTO `NewTracksData` " "SELECT " "td.`FileName`, " "td.`FileModifiedTime`, " "td.`ImportDate`, " "td.`FirstPlayDate`, " "td.`LastPlayDate`, " "td.`PlayCounter` " "FROM " "`TracksData` td")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << copyDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << copyDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery copyDataQuery(d->mTracksDatabase); auto result = copyDataQuery.exec(QStringLiteral("INSERT INTO `NewTracks` " "SELECT " "t.`ID`, " "t.`FileName`, " "t.`Priority`, " "t.`Title`, " "t.`ArtistName`, " "t.`AlbumTitle`, " "t.`AlbumArtistName`, " "t.`AlbumPath`, " "t.`TrackNumber`, " "t.`DiscNumber`, " "t.`Duration`, " "t.`Rating`, " "t.`Genre`, " "t.`Composer`, " "t.`Lyricist`, " "t.`Comment`, " "t.`Year`, " "t.`Channels`, " "t.`BitRate`, " "t.`SampleRate`, " "t.`HasEmbeddedCover` " "FROM " "`Tracks` t")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << copyDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << copyDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `TracksData`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("ALTER TABLE `NewTracksData` RENAME TO `TracksData`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("ALTER TABLE `NewTracks` RENAME TO `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.commit(); { QSqlQuery enableForeignKeys(d->mTracksDatabase); auto result = enableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=ON")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << enableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << enableForeignKeys.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksAlbumIndex` ON `Tracks` " "(`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`ArtistNameIndex` ON `Tracks` " "(`ArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`AlbumArtistNameIndex` ON `Tracks` " "(`AlbumArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksUniqueData` ON `Tracks` " "(`Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksUniqueDataPriority` ON `Tracks` " "(`Priority`, `Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksFileNameIndex` ON `Tracks` " "(`FileName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV12" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } qCInfo(orgKdeElisaDatabase) << "finished update to v12 of database schema"; } void DatabaseInterface::upgradeDatabaseV13() { qCInfo(orgKdeElisaDatabase) << "begin update to v13 of database schema"; { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DatabaseVersionV13` (`Version` INTEGER PRIMARY KEY NOT NULL)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery disableForeignKeys(d->mTracksDatabase); auto result = disableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=OFF")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << disableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << disableForeignKeys.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.transaction(); { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `NewTracks` (" "`ID` INTEGER PRIMARY KEY AUTOINCREMENT, " "`FileName` VARCHAR(255) NOT NULL, " "`Priority` INTEGER NOT NULL, " "`Title` VARCHAR(85) NOT NULL, " "`ArtistName` VARCHAR(55), " "`AlbumTitle` VARCHAR(55), " "`AlbumArtistName` VARCHAR(55), " "`AlbumPath` VARCHAR(255), " "`TrackNumber` INTEGER, " "`DiscNumber` INTEGER, " "`Duration` INTEGER NOT NULL, " "`Rating` INTEGER NOT NULL DEFAULT 0, " "`Genre` VARCHAR(55), " "`Composer` VARCHAR(55), " "`Lyricist` VARCHAR(55), " "`Comment` VARCHAR(255), " "`Year` INTEGER, " "`Channels` INTEGER, " "`BitRate` INTEGER, " "`SampleRate` INTEGER, " "`HasEmbeddedCover` BOOLEAN NOT NULL, " "UNIQUE (" "`FileName`" "), " "UNIQUE (" "`Priority`, `Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`, " "`TrackNumber`, `DiscNumber`" "), " "CONSTRAINT fk_fileName FOREIGN KEY (`FileName`) " "REFERENCES `TracksData`(`FileName`) ON DELETE CASCADE, " "CONSTRAINT fk_artist FOREIGN KEY (`ArtistName`) REFERENCES `Artists`(`Name`), " "CONSTRAINT fk_tracks_composer FOREIGN KEY (`Composer`) REFERENCES `Composer`(`Name`), " "CONSTRAINT fk_tracks_lyricist FOREIGN KEY (`Lyricist`) REFERENCES `Lyricist`(`Name`), " "CONSTRAINT fk_tracks_genre FOREIGN KEY (`Genre`) REFERENCES `Genre`(`Name`), " "CONSTRAINT fk_tracks_album FOREIGN KEY (" "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)" "REFERENCES `Albums`(`Title`, `ArtistName`, `AlbumPath`))")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastError(); } } { QSqlQuery copyDataQuery(d->mTracksDatabase); auto result = copyDataQuery.exec(QStringLiteral("INSERT INTO `NewTracks` " "SELECT " "t.`ID`, " "t.`FileName`, " "t.`Priority`, " "t.`Title`, " "t.`ArtistName`, " "t.`AlbumTitle`, " "t.`AlbumArtistName`, " "t.`AlbumPath`, " "t.`TrackNumber`, " "t.`DiscNumber`, " "t.`Duration`, " "t.`Rating`, " "t.`Genre`, " "t.`Composer`, " "t.`Lyricist`, " "t.`Comment`, " "t.`Year`, " "t.`Channels`, " "t.`BitRate`, " "t.`SampleRate`, " "t.`HasEmbeddedCover` " "FROM " "`Tracks` t")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << copyDataQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << copyDataQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("DROP TABLE `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); auto result = createSchemaQuery.exec(QStringLiteral("ALTER TABLE `NewTracks` RENAME TO `Tracks`")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.commit(); { QSqlQuery enableForeignKeys(d->mTracksDatabase); auto result = enableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=ON")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << enableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << enableForeignKeys.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksAlbumIndex` ON `Tracks` " "(`AlbumTitle`, `AlbumArtistName`, `AlbumPath`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`ArtistNameIndex` ON `Tracks` " "(`ArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`AlbumArtistNameIndex` ON `Tracks` " "(`AlbumArtistName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksUniqueData` ON `Tracks` " "(`Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`, " "`TrackNumber`, `DiscNumber`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksUniqueDataPriority` ON `Tracks` " "(`Priority`, `Title`, `ArtistName`, " "`AlbumTitle`, `AlbumArtistName`, `AlbumPath`, " "`TrackNumber`, `DiscNumber`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksFileNameIndex` ON `Tracks` " "(`FileName`)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV13" << createTrackIndex.lastError(); Q_EMIT databaseError(); } } qCInfo(orgKdeElisaDatabase) << "finished update to v13 of database schema"; } void DatabaseInterface::upgradeDatabaseV14() { qCInfo(orgKdeElisaDatabase) << "begin update to v14 of database schema"; { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DatabaseVersionV14` (`Version` INTEGER PRIMARY KEY NOT NULL)")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery disableForeignKeys(d->mTracksDatabase); auto result = disableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=OFF")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << disableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << disableForeignKeys.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.transaction(); { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Radios` (" "`ID` INTEGER PRIMARY KEY AUTOINCREMENT, " "`HttpAddress` VARCHAR(255) NOT NULL, " "`Priority` INTEGER NOT NULL, " "`Title` VARCHAR(85) NOT NULL, " "`Rating` INTEGER NOT NULL DEFAULT 0, " "`Genre` VARCHAR(55), " "`Comment` VARCHAR(255), " "UNIQUE (" "`HttpAddress`" "), " "UNIQUE (" "`Priority`, `Title`, `HttpAddress`" ") " "CONSTRAINT fk_tracks_genre FOREIGN KEY (`Genre`) REFERENCES `Genre`(`Name`))" )); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } { QSqlQuery createSchemaQuery(d->mTracksDatabase); //Find webradios (french): https://doc.ubuntu-fr.org/liste_radio_france //English: https://www.radio.fr/language/english (to get the link play a radio and look for streamUrl in the html elements page). const auto &result = createSchemaQuery.exec(QStringLiteral("INSERT INTO `Radios` (`HttpAddress`, `Priority`, `Title`) " "SELECT 'http://classicrock.stream.ouifm.fr/ouifm3.mp3', 1, 'OuiFM_Classic_Rock' UNION ALL " "SELECT 'http://rock70s.stream.ouifm.fr/ouifmseventies.mp3', 1, 'OuiFM_70s' UNION ALL " "SELECT 'http://jazzradio.ice.infomaniak.ch/jazzradio-high.mp3', 2 , 'Jazz_Radio' UNION ALL " "SELECT 'http://cdn.nrjaudio.fm/audio1/fr/30601/mp3_128.mp3?origine=playerweb', 1, 'Nostalgie' UNION ALL " "SELECT 'https://scdn.nrjaudio.fm/audio1/fr/30713/aac_64.mp3?origine=playerweb', 1, 'Nostalgie Johnny' UNION ALL " "SELECT 'http://sc-classrock.1.fm:8200', 1, 'Classic rock replay' UNION ALL " "SELECT 'http://agnes.torontocast.com:8151/stream', 1, 'Instrumentals Forever' UNION ALL " "SELECT 'https://stream.laut.fm/jahfari', 1, 'Jahfari'" )); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << createSchemaQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << createSchemaQuery.lastError(); Q_EMIT databaseError(); } } d->mTracksDatabase.commit(); { QSqlQuery enableForeignKeys(d->mTracksDatabase); auto result = enableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=ON")); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << enableForeignKeys.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << enableForeignKeys.lastError(); Q_EMIT databaseError(); } } qCInfo(orgKdeElisaDatabase) << "finished update to v14 of database schema"; } void DatabaseInterface::checkDatabaseSchema() { checkAlbumsTableSchema(); if (d->mIsInBadState) { resetDatabase(); return; } checkArtistsTableSchema(); if (d->mIsInBadState) { resetDatabase(); return; } checkComposerTableSchema(); if (d->mIsInBadState) { resetDatabase(); return; } checkGenreTableSchema(); if (d->mIsInBadState) { resetDatabase(); return; } checkLyricistTableSchema(); if (d->mIsInBadState) { resetDatabase(); return; } checkTracksTableSchema(); if (d->mIsInBadState) { resetDatabase(); return; } checkTracksDataTableSchema(); if (d->mIsInBadState) { resetDatabase(); return; } } void DatabaseInterface::checkAlbumsTableSchema() { auto fieldsList = QStringList{QStringLiteral("ID"), QStringLiteral("Title"), QStringLiteral("ArtistName"), QStringLiteral("AlbumPath"), QStringLiteral("CoverFileName")}; genericCheckTable(QStringLiteral("Albums"), fieldsList); } void DatabaseInterface::checkArtistsTableSchema() { auto fieldsList = QStringList{QStringLiteral("ID"), QStringLiteral("Name")}; genericCheckTable(QStringLiteral("Artists"), fieldsList); } void DatabaseInterface::checkComposerTableSchema() { auto fieldsList = QStringList{QStringLiteral("ID"), QStringLiteral("Name")}; genericCheckTable(QStringLiteral("Composer"), fieldsList); } void DatabaseInterface::checkGenreTableSchema() { auto fieldsList = QStringList{QStringLiteral("ID"), QStringLiteral("Name")}; genericCheckTable(QStringLiteral("Genre"), fieldsList); } void DatabaseInterface::checkLyricistTableSchema() { auto fieldsList = QStringList{QStringLiteral("ID"), QStringLiteral("Name")}; genericCheckTable(QStringLiteral("Lyricist"), fieldsList); } void DatabaseInterface::checkTracksTableSchema() { auto fieldsList = QStringList{QStringLiteral("ID"), QStringLiteral("FileName"), QStringLiteral("Priority"), QStringLiteral("Title"), QStringLiteral("ArtistName"), QStringLiteral("AlbumTitle"), QStringLiteral("AlbumArtistName"), QStringLiteral("AlbumPath"), QStringLiteral("TrackNumber"), QStringLiteral("DiscNumber"), QStringLiteral("Duration"), QStringLiteral("Rating"), QStringLiteral("Genre"), QStringLiteral("Composer"), QStringLiteral("Lyricist"), QStringLiteral("Comment"), QStringLiteral("Year"), QStringLiteral("Channels"), QStringLiteral("BitRate"), QStringLiteral("SampleRate"), QStringLiteral("HasEmbeddedCover")}; genericCheckTable(QStringLiteral("Tracks"), fieldsList); } void DatabaseInterface::checkTracksDataTableSchema() { auto fieldsList = QStringList{QStringLiteral("FileName"), QStringLiteral("FileModifiedTime"), QStringLiteral("ImportDate"), QStringLiteral("FirstPlayDate"), QStringLiteral("LastPlayDate"), QStringLiteral("PlayCounter")}; genericCheckTable(QStringLiteral("TracksData"), fieldsList); } void DatabaseInterface::genericCheckTable(const QString &tableName, const QStringList &expectedColumns) { auto columnsList = d->mTracksDatabase.record(tableName); if (columnsList.count() != expectedColumns.count()) { qCInfo(orgKdeElisaDatabase()) << tableName << "table has wrong number of columns" << columnsList.count() << "expected" << expectedColumns.count(); d->mIsInBadState = true; return; } for (const auto &oneField : expectedColumns) { if (!columnsList.contains(oneField)) { qCInfo(orgKdeElisaDatabase()) << tableName << "table has missing column" << oneField; d->mIsInBadState = true; return; } } } void DatabaseInterface::resetDatabase() { qCInfo(orgKdeElisaDatabase()) << "Full reset of database due to corrupted database"; auto listTables = d->mTracksDatabase.tables(); while(!listTables.isEmpty()) { for (const auto &oneTable : listTables) { QSqlQuery createSchemaQuery(d->mTracksDatabase); qCDebug(orgKdeElisaDatabase()) << "dropping table" << oneTable; createSchemaQuery.exec(QStringLiteral("DROP TABLE ") + oneTable); } listTables = d->mTracksDatabase.tables(); if (listTables == QStringList{QStringLiteral("sqlite_sequence")}) { break; } } d->mIsInBadState = false; } void DatabaseInterface::initRequest() { auto transactionResult = startTransaction(); if (!transactionResult) { return; } { auto selectAlbumQueryText = QStringLiteral("SELECT " "album.`ID`, " "album.`Title`, " "album.`ArtistName`, " "album.`AlbumPath`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(*) " "FROM " "`Tracks` tracks3 " "WHERE " "tracks3.`AlbumTitle` = album.`Title` AND " "(tracks3.`AlbumArtistName` = album.`ArtistName` OR " "(tracks3.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks3.`AlbumPath` = album.`AlbumPath` " ") as `TracksCount`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "COUNT(DISTINCT tracks.`ArtistName`) as ArtistsCount, " "GROUP_CONCAT(tracks.`ArtistName`, ', ') as AllArtists, " "MAX(tracks.`Rating`) as HighestRating, " "GROUP_CONCAT(genres.`Name`, ', ') as AllGenres, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Albums` album LEFT JOIN " "`Tracks` tracks ON " "tracks.`AlbumTitle` = album.`Title` AND " "(" "tracks.`AlbumArtistName` = album.`ArtistName` OR " "(" "tracks.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks.`AlbumPath` = album.`AlbumPath`" "LEFT JOIN " "`Genre` genres ON tracks.`Genre` = genres.`Name` " "WHERE " "album.`ID` = :albumId " "GROUP BY album.`ID`"); auto result = prepareQuery(d->mSelectAlbumQuery, selectAlbumQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllGenresText = QStringLiteral("SELECT " "genre.`ID`, " "genre.`Name` " "FROM `Genre` genre " "ORDER BY genre.`Name` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllGenresQuery, selectAllGenresText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllGenresQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllGenresQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllAlbumsText = QStringLiteral("SELECT " "album.`ID`, " "album.`Title`, " "album.`ArtistName` as SecondaryText, " "album.`CoverFileName`, " "album.`ArtistName`, " "COUNT(DISTINCT tracks.`ArtistName`) as ArtistsCount, " "GROUP_CONCAT(tracks.`ArtistName`, ', ') as AllArtists, " "MAX(tracks.`Rating`) as HighestRating, " "GROUP_CONCAT(genres.`Name`, ', ') as AllGenres, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Albums` album, " "`Tracks` tracks LEFT JOIN " "`Genre` genres ON tracks.`Genre` = genres.`Name` " "WHERE " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR " "(tracks.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ") " ") AND " "tracks.`AlbumPath` = album.`AlbumPath` " "GROUP BY album.`ID`, album.`Title`, album.`AlbumPath` " "ORDER BY album.`Title` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllAlbumsShortQuery, selectAllAlbumsText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllAlbumsShortQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllAlbumsShortQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllAlbumsText = QStringLiteral("SELECT " "album.`ID`, " "album.`Title`, " "album.`ArtistName` as SecondaryText, " "album.`CoverFileName`, " "album.`ArtistName`, " "COUNT(DISTINCT tracks.`ArtistName`) as ArtistsCount, " "GROUP_CONCAT(tracks.`ArtistName`, ', ') as AllArtists, " "MAX(tracks.`Rating`) as HighestRating, " "GROUP_CONCAT(genres.`Name`, ', ') as AllGenres, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Albums` album, " "`Tracks` tracks LEFT JOIN " "`Genre` genres ON tracks.`Genre` = genres.`Name` " "WHERE " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR " "(tracks.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks.`AlbumPath` = album.`AlbumPath` AND " "EXISTS (" " SELECT tracks2.`Genre` " " FROM " " `Tracks` tracks2, " " `Genre` genre2 " " WHERE " " tracks2.`AlbumTitle` = album.`Title` AND " " (tracks2.`AlbumArtistName` = album.`ArtistName` OR " " (tracks2.`AlbumArtistName` IS NULL AND " " album.`ArtistName` IS NULL" " )" " ) AND " " tracks2.`Genre` = genre2.`Name` AND " " genre2.`Name` = :genreFilter AND " " (tracks2.`ArtistName` = :artistFilter OR tracks2.`AlbumArtistName` = :artistFilter) " ") " "GROUP BY album.`ID`, album.`Title`, album.`AlbumPath` " "ORDER BY album.`Title` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllAlbumsShortWithGenreArtistFilterQuery, selectAllAlbumsText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllAlbumsShortWithGenreArtistFilterQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllAlbumsShortWithGenreArtistFilterQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllAlbumsText = QStringLiteral("SELECT " "album.`ID`, " "album.`Title`, " "album.`ArtistName` as SecondaryText, " "album.`CoverFileName`, " "album.`ArtistName`, " "COUNT(DISTINCT tracks.`ArtistName`) as ArtistsCount, " "GROUP_CONCAT(tracks.`ArtistName`, ', ') as AllArtists, " "MAX(tracks.`Rating`) as HighestRating, " "GROUP_CONCAT(genres.`Name`, ', ') as AllGenres, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Albums` album, " "`Tracks` tracks LEFT JOIN " "`Genre` genres ON tracks.`Genre` = genres.`Name` " "WHERE " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR " "(tracks.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks.`AlbumPath` = album.`AlbumPath` AND " "EXISTS (" " SELECT tracks2.`Genre` " " FROM " " `Tracks` tracks2 " " WHERE " " tracks2.`AlbumTitle` = album.`Title` AND " " ( " " tracks2.`AlbumArtistName` = album.`ArtistName` OR " " ( " " tracks2.`AlbumArtistName` IS NULL AND " " album.`ArtistName` IS NULL " " )" " ) AND " " (tracks2.`ArtistName` = :artistFilter OR tracks2.`AlbumArtistName` = :artistFilter) " ") " "GROUP BY album.`ID`, album.`Title`, album.`AlbumPath` " "ORDER BY album.`Title` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllAlbumsShortWithArtistFilterQuery, selectAllAlbumsText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllAlbumsShortWithArtistFilterQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllAlbumsShortWithArtistFilterQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllArtistsWithFilterText = QStringLiteral("SELECT artists.`ID`, " "artists.`Name`, " "GROUP_CONCAT(genres.`Name`, ', ') as AllGenres " "FROM `Artists` artists LEFT JOIN " "`Tracks` tracks ON artists.`Name` = tracks.`ArtistName` LEFT JOIN " "`Genre` genres ON tracks.`Genre` = genres.`Name` " "GROUP BY artists.`ID` " "ORDER BY artists.`Name` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllArtistsQuery, selectAllArtistsWithFilterText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllArtistsQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllArtistsQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllArtistsWithGenreFilterText = QStringLiteral("SELECT artists.`ID`, " "artists.`Name`, " "GROUP_CONCAT(genres.`Name`, ', ') as AllGenres " "FROM `Artists` artists LEFT JOIN " "`Tracks` tracks ON (tracks.`ArtistName` = artists.`Name` OR tracks.`AlbumArtistName` = artists.`Name`) LEFT JOIN " "`Genre` genres ON tracks.`Genre` = genres.`Name` " "WHERE " "EXISTS (" " SELECT tracks2.`Genre` " " FROM " " `Tracks` tracks2, " " `Genre` genre2 " " WHERE " " (tracks2.`ArtistName` = artists.`Name` OR tracks2.`AlbumArtistName` = artists.`Name`) AND " " tracks2.`Genre` = genre2.`Name` AND " " genre2.`Name` = :genreFilter " ") " "GROUP BY artists.`ID` " "ORDER BY artists.`Name` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllArtistsWithGenreFilterQuery, selectAllArtistsWithGenreFilterText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllArtistsWithGenreFilterQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllArtistsWithGenreFilterQuery.lastError(); Q_EMIT databaseError(); } } { auto artistMatchGenreText = QStringLiteral("SELECT artists.`ID` " "FROM `Artists` artists LEFT JOIN " "`Tracks` tracks ON (tracks.`ArtistName` = artists.`Name` OR tracks.`AlbumArtistName` = artists.`Name`) LEFT JOIN " "`Genre` genres ON tracks.`Genre` = genres.`Name` " "WHERE " "EXISTS (" " SELECT tracks2.`Genre` " " FROM " " `Tracks` tracks2, " " `Genre` genre2 " " WHERE " " (tracks2.`ArtistName` = artists.`Name` OR tracks2.`AlbumArtistName` = artists.`Name`) AND " " tracks2.`Genre` = genre2.`Name` AND " " genre2.`Name` = :genreFilter " ") AND " "artists.`ID` = :databaseId"); auto result = prepareQuery(d->mArtistMatchGenreQuery, artistMatchGenreText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mArtistMatchGenreQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mArtistMatchGenreQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllComposersWithFilterText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Artists` " "ORDER BY `Name` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllComposersQuery, selectAllComposersWithFilterText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllComposersQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllComposersQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllLyricistsWithFilterText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Lyricist` " "ORDER BY `Name` COLLATE NOCASE"); auto result = prepareQuery(d->mSelectAllLyricistsQuery, selectAllLyricistsWithFilterText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllLyricistsQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllLyricistsQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllTracksText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "album.`ID`, " "tracks.`ArtistName`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "tracksMapping.`FileName`, " "tracksMapping.`FileModifiedTime`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "tracks.`AlbumTitle`, " "tracks.`Rating`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "trackGenre.`Name`, " "trackComposer.`Name`, " "trackLyricist.`Name`, " "tracks.`Comment`, " "tracks.`Year`, " "tracks.`Channels`, " "tracks.`BitRate`, " "tracks.`SampleRate`, " "tracks.`HasEmbeddedCover`, " "tracksMapping.`ImportDate`, " "tracksMapping.`FirstPlayDate`, " "tracksMapping.`LastPlayDate`, " "tracksMapping.`PlayCounter`, " "tracksMapping.`PlayCounter` / (strftime('%s', 'now') - tracksMapping.`FirstPlayDate`) as PlayFrequency, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "LEFT JOIN `Composer` trackComposer ON trackComposer.`Name` = tracks.`Composer` " "LEFT JOIN `Lyricist` trackLyricist ON trackLyricist.`Name` = tracks.`Lyricist` " "WHERE " "tracksMapping.`FileName` = tracks.`FileName` AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" ""); auto result = prepareQuery(d->mSelectAllTracksQuery, selectAllTracksText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllTracksQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllTracksQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllRadiosText = QStringLiteral("SELECT " "radios.`ID`, " "radios.`Title`, " "radios.`HttpAddress`, " "radios.`Rating`, " "trackGenre.`Name`, " "radios.`Comment` " "FROM " "`Radios` radios " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = radios.`Genre` " ""); auto result = prepareQuery(d->mSelectAllRadiosQuery, selectAllRadiosText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllRadiosQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllRadiosQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllTracksText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "album.`ID`, " "tracks.`ArtistName`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "tracksMapping.`FileName`, " "tracksMapping.`FileModifiedTime`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "tracks.`AlbumTitle`, " "tracks.`Rating`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "trackGenre.`Name`, " "trackComposer.`Name`, " "trackLyricist.`Name`, " "tracks.`Comment`, " "tracks.`Year`, " "tracks.`Channels`, " "tracks.`BitRate`, " "tracks.`SampleRate`, " "tracks.`HasEmbeddedCover`, " "tracksMapping.`ImportDate`, " "tracksMapping.`FirstPlayDate`, " "tracksMapping.`LastPlayDate`, " "tracksMapping.`PlayCounter`, " "tracksMapping.`PlayCounter` / (strftime('%s', 'now') - tracksMapping.`FirstPlayDate`) as PlayFrequency, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "LEFT JOIN `Composer` trackComposer ON trackComposer.`Name` = tracks.`Composer` " "LEFT JOIN `Lyricist` trackLyricist ON trackLyricist.`Name` = tracks.`Lyricist` " "WHERE " "tracksMapping.`FileName` = tracks.`FileName` AND " "tracksMapping.`PlayCounter` > 0 AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" "ORDER BY tracksMapping.`LastPlayDate` DESC " "LIMIT :maximumResults"); auto result = prepareQuery(d->mSelectAllRecentlyPlayedTracksQuery, selectAllTracksText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllRecentlyPlayedTracksQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllRecentlyPlayedTracksQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAllTracksText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "album.`ID`, " "tracks.`ArtistName`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "tracksMapping.`FileName`, " "tracksMapping.`FileModifiedTime`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "tracks.`AlbumTitle`, " "tracks.`Rating`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "trackGenre.`Name`, " "trackComposer.`Name`, " "trackLyricist.`Name`, " "tracks.`Comment`, " "tracks.`Year`, " "tracks.`Channels`, " "tracks.`BitRate`, " "tracks.`SampleRate`, " "tracks.`HasEmbeddedCover`, " "tracksMapping.`ImportDate`, " "tracksMapping.`FirstPlayDate`, " "tracksMapping.`LastPlayDate`, " "tracksMapping.`PlayCounter`, " "CAST(tracksMapping.`PlayCounter` AS REAL) / ((CAST(strftime('%s','now') as INTEGER) - CAST(tracksMapping.`FirstPlayDate` / 1000 as INTEGER)) / CAST(1000 AS REAL)) as PlayFrequency, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "LEFT JOIN `Composer` trackComposer ON trackComposer.`Name` = tracks.`Composer` " "LEFT JOIN `Lyricist` trackLyricist ON trackLyricist.`Name` = tracks.`Lyricist` " "WHERE " "tracksMapping.`FileName` = tracks.`FileName` AND " "tracksMapping.`PlayCounter` > 0 AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" "ORDER BY CAST(tracksMapping.`PlayCounter` AS REAL) / ((CAST(strftime('%s','now') as INTEGER) - CAST(tracksMapping.`FirstPlayDate` / 1000 as INTEGER)) / CAST(1000 AS REAL)) DESC " "LIMIT :maximumResults"); auto result = prepareQuery(d->mSelectAllFrequentlyPlayedTracksQuery, selectAllTracksText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllFrequentlyPlayedTracksQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllFrequentlyPlayedTracksQuery.lastError(); Q_EMIT databaseError(); } } { auto clearAlbumsTableText = QStringLiteral("DELETE FROM `Albums`"); auto result = prepareQuery(d->mClearAlbumsTable, clearAlbumsTableText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearAlbumsTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearAlbumsTable.lastError(); Q_EMIT databaseError(); } } { auto clearArtistsTableText = QStringLiteral("DELETE FROM `Artists`"); auto result = prepareQuery(d->mClearArtistsTable, clearArtistsTableText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearArtistsTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearArtistsTable.lastError(); Q_EMIT databaseError(); } } { auto clearComposerTableText = QStringLiteral("DELETE FROM `Composer`"); auto result = prepareQuery(d->mClearComposerTable, clearComposerTableText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearComposerTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearComposerTable.lastError(); Q_EMIT databaseError(); } } { auto clearGenreTableText = QStringLiteral("DELETE FROM `Genre`"); auto result = prepareQuery(d->mClearGenreTable, clearGenreTableText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearGenreTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearGenreTable.lastError(); Q_EMIT databaseError(); } } { auto clearLyricistTableText = QStringLiteral("DELETE FROM `Lyricist`"); auto result = prepareQuery(d->mClearLyricistTable, clearLyricistTableText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearLyricistTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearLyricistTable.lastError(); Q_EMIT databaseError(); } } { auto clearTracksTableText = QStringLiteral("DELETE FROM `Tracks`"); auto result = prepareQuery(d->mClearTracksTable, clearTracksTableText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearTracksTable.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mClearTracksTable.lastError(); Q_EMIT databaseError(); } } { auto selectAllTracksShortText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "tracks.`ArtistName`, " "tracks.`AlbumTitle`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "tracks.`Duration`, " "album.`CoverFileName`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Rating` " "FROM " "`Tracks` tracks " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " ""); auto result = prepareQuery(d->mSelectAllTracksShortQuery, selectAllTracksShortText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllTracksShortQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllTracksShortQuery.lastError(); Q_EMIT databaseError(); } } { auto selectArtistByNameText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Artists` " "WHERE " "`Name` = :name"); auto result = prepareQuery(d->mSelectArtistByNameQuery, selectArtistByNameText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectArtistByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectArtistByNameQuery.lastError(); Q_EMIT databaseError(); } } { auto selectComposerByNameText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Composer` " "WHERE " "`Name` = :name"); auto result = prepareQuery(d->mSelectComposerByNameQuery, selectComposerByNameText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectComposerByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectComposerByNameQuery.lastError(); } } { auto selectLyricistByNameText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Lyricist` " "WHERE " "`Name` = :name"); auto result = prepareQuery(d->mSelectLyricistByNameQuery, selectLyricistByNameText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectLyricistByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectLyricistByNameQuery.lastError(); } } { auto selectGenreByNameText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Genre` " "WHERE " "`Name` = :name"); auto result = prepareQuery(d->mSelectGenreByNameQuery, selectGenreByNameText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreByNameQuery.lastError(); Q_EMIT databaseError(); } } { auto insertArtistsText = QStringLiteral("INSERT INTO `Artists` (`ID`, `Name`) " "VALUES (:artistId, :name)"); auto result = prepareQuery(d->mInsertArtistsQuery, insertArtistsText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertArtistsQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertArtistsQuery.lastError(); Q_EMIT databaseError(); } } { auto insertGenreText = QStringLiteral("INSERT INTO `Genre` (`ID`, `Name`) " "VALUES (:genreId, :name)"); auto result = prepareQuery(d->mInsertGenreQuery, insertGenreText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertGenreQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertGenreQuery.lastError(); Q_EMIT databaseError(); } } { auto insertComposerText = QStringLiteral("INSERT INTO `Composer` (`ID`, `Name`) " "VALUES (:composerId, :name)"); auto result = prepareQuery(d->mInsertComposerQuery, insertComposerText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertComposerQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertComposerQuery.lastError(); } } { auto insertLyricistText = QStringLiteral("INSERT INTO `Lyricist` (`ID`, `Name`) " "VALUES (:lyricistId, :name)"); auto result = prepareQuery(d->mInsertLyricistQuery, insertLyricistText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertLyricistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertLyricistQuery.lastError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "album.`ID`, " "tracks.`ArtistName`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "tracksMapping.`FileName`, " "tracksMapping.`FileModifiedTime`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "tracks.`AlbumTitle`, " "tracks.`Rating`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "trackGenre.`Name`, " "trackComposer.`Name`, " "trackLyricist.`Name`, " "tracks.`Comment`, " "tracks.`Year`, " "tracks.`Channels`, " "tracks.`BitRate`, " "tracks.`SampleRate`, " "tracks.`HasEmbeddedCover`, " "tracksMapping.`ImportDate`, " "tracksMapping.`FirstPlayDate`, " "tracksMapping.`LastPlayDate`, " "tracksMapping.`PlayCounter`, " "tracksMapping.`PlayCounter` / (strftime('%s', 'now') - tracksMapping.`FirstPlayDate`) as PlayFrequency, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "album.`ID` = :albumId AND " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Composer` trackComposer ON trackComposer.`Name` = tracks.`Composer` " "LEFT JOIN `Lyricist` trackLyricist ON trackLyricist.`Name` = tracks.`Lyricist` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "WHERE " "tracksMapping.`FileName` = tracks.`FileName` AND " "album.`ID` = :albumId AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" "ORDER BY tracks.`DiscNumber` ASC, " "tracks.`TrackNumber` ASC"); auto result = prepareQuery(d->mSelectTrackQuery, selectTrackQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackQuery.lastError(); Q_EMIT databaseError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.`ID` " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "album.`ID` = :albumId AND " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "WHERE " "tracksMapping.`FileName` = tracks.`FileName` AND " "album.`ID` = :albumId AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" "ORDER BY tracks.`DiscNumber` ASC, " "tracks.`TrackNumber` ASC"); auto result = prepareQuery(d->mSelectTrackIdQuery, selectTrackQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdQuery.lastError(); Q_EMIT databaseError(); } } { auto selectTrackFromIdQueryText = QStringLiteral("SELECT " "tracks.`Id`, " "tracks.`Title`, " "album.`ID`, " "tracks.`ArtistName`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "tracksMapping.`FileName`, " "tracksMapping.`FileModifiedTime`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "tracks.`AlbumTitle`, " "tracks.`Rating`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "trackGenre.`Name`, " "trackComposer.`Name`, " "trackLyricist.`Name`, " "tracks.`Comment`, " "tracks.`Year`, " "tracks.`Channels`, " "tracks.`BitRate`, " "tracks.`SampleRate`, " "tracks.`HasEmbeddedCover`, " "tracksMapping.`ImportDate`, " "tracksMapping.`FirstPlayDate`, " "tracksMapping.`LastPlayDate`, " "tracksMapping.`PlayCounter`, " "tracksMapping.`PlayCounter` / (strftime('%s', 'now') - tracksMapping.`FirstPlayDate`) as PlayFrequency, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Composer` trackComposer ON trackComposer.`Name` = tracks.`Composer` " "LEFT JOIN `Lyricist` trackLyricist ON trackLyricist.`Name` = tracks.`Lyricist` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "WHERE " "tracks.`ID` = :trackId AND " "tracksMapping.`FileName` = tracks.`FileName` AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" ""); auto result = prepareQuery(d->mSelectTrackFromIdQuery, selectTrackFromIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackFromIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackFromIdQuery.lastError(); Q_EMIT databaseError(); } } { auto selectRadioFromIdQueryText = QStringLiteral("SELECT " "radios.`ID`, " "radios.`Title`, " "radios.`HttpAddress`, " "radios.`Rating`, " "trackGenre.`Name`, " "radios.`Comment` " "FROM " "`Radios` radios " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = radios.`Genre` " "WHERE " "radios.`ID` = :radioId " ""); auto result = prepareQuery(d->mSelectRadioFromIdQuery, selectRadioFromIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioFromIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioFromIdQuery.lastError(); Q_EMIT databaseError(); } } { auto selectCountAlbumsQueryText = QStringLiteral("SELECT count(*) " "FROM `Albums` album " "WHERE album.`ArtistName` = :artistName "); const auto result = prepareQuery(d->mSelectCountAlbumsForArtistQuery, selectCountAlbumsQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectCountAlbumsForArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectCountAlbumsForArtistQuery.lastError(); Q_EMIT databaseError(); } } { auto selectGenreForArtistQueryText = QStringLiteral("SELECT DISTINCT trackGenre.`Name` " "FROM " "`Tracks` tracks " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "WHERE " "album.`ArtistName` = :artistName"); const auto result = prepareQuery(d->mSelectGenreForArtistQuery, selectGenreForArtistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreForArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreForArtistQuery.lastError(); Q_EMIT databaseError(); } } { auto selectGenreForAlbumQueryText = QStringLiteral("SELECT DISTINCT trackGenre.`Name` " "FROM " "`Tracks` tracks " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "WHERE " "album.`ID` = :albumId"); const auto result = prepareQuery(d->mSelectGenreForAlbumQuery, selectGenreForAlbumQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreForAlbumQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreForAlbumQuery.lastError(); Q_EMIT databaseError(); } } { auto selectCountAlbumsQueryText = QStringLiteral("SELECT distinct count(album.`ID`) " "FROM " "`Tracks` tracks, " "`Albums` album " "LEFT JOIN `Composer` albumComposer ON albumComposer.`Name` = tracks.`Composer` " "WHERE " "(tracks.`AlbumTitle` = album.`Title` OR tracks.`AlbumTitle` IS NULL ) AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "(tracks.`AlbumPath` = album.`AlbumPath` OR tracks.`AlbumPath` IS NULL ) AND " "albumComposer.`Name` = :artistName"); const auto result = prepareQuery(d->mSelectCountAlbumsForComposerQuery, selectCountAlbumsQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectCountAlbumsForComposerQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectCountAlbumsForComposerQuery.lastError(); Q_EMIT databaseError(); } } { auto selectCountAlbumsQueryText = QStringLiteral("SELECT distinct count(album.`ID`) " "FROM " "`Tracks` tracks, " "`Albums` album " "LEFT JOIN `Lyricist` albumLyricist ON albumLyricist.`Name` = tracks.`Lyricist` " "WHERE " "(tracks.`AlbumTitle` = album.`Title` OR tracks.`AlbumTitle` IS NULL ) AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "(tracks.`AlbumPath` = album.`AlbumPath` OR tracks.`AlbumPath` IS NULL ) AND " "albumLyricist.`Name` = :artistName"); const auto result = prepareQuery(d->mSelectCountAlbumsForLyricistQuery, selectCountAlbumsQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectCountAlbumsForLyricistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectCountAlbumsForLyricistQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAlbumIdFromTitleQueryText = QStringLiteral("SELECT " "album.`ID` " "FROM " "`Albums` album " "WHERE " "album.`ArtistName` = :artistName AND " "album.`Title` = :title"); auto result = prepareQuery(d->mSelectAlbumIdFromTitleQuery, selectAlbumIdFromTitleQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdFromTitleQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdFromTitleQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAlbumIdFromTitleAndArtistQueryText = QStringLiteral("SELECT " "album.`ID` " "FROM " "`Albums` album " "WHERE " "(album.`ArtistName` = :artistName OR :artistName IS NULL OR album.`ArtistName` IS NULL) AND " "album.`Title` = :title AND " "album.`AlbumPath` = :albumPath"); auto result = prepareQuery(d->mSelectAlbumIdFromTitleAndArtistQuery, selectAlbumIdFromTitleAndArtistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdFromTitleAndArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdFromTitleAndArtistQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAlbumIdFromTitleWithoutArtistQueryText = QStringLiteral("SELECT " "album.`ID` " "FROM " "`Albums` album " "WHERE " "album.`AlbumPath` = :albumPath AND " "album.`Title` = :title AND " "album.`ArtistName` IS NULL"); auto result = prepareQuery(d->mSelectAlbumIdFromTitleWithoutArtistQuery, selectAlbumIdFromTitleWithoutArtistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdFromTitleWithoutArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdFromTitleWithoutArtistQuery.lastError(); Q_EMIT databaseError(); } } { auto insertAlbumQueryText = QStringLiteral("INSERT INTO `Albums` " "(`ID`, " "`Title`, " "`ArtistName`, " "`AlbumPath`, " "`CoverFileName`) " "VALUES " "(:albumId, " ":title, " ":albumArtist, " ":albumPath, " ":coverFileName)"); auto result = prepareQuery(d->mInsertAlbumQuery, insertAlbumQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertAlbumQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertAlbumQuery.lastError(); Q_EMIT databaseError(); } } { auto insertTrackMappingQueryText = QStringLiteral("INSERT INTO " "`TracksData` " "(`FileName`, " "`FileModifiedTime`, " "`ImportDate`, " "`PlayCounter`) " "VALUES (:fileName, :mtime, :importDate, 0)"); auto result = prepareQuery(d->mInsertTrackMapping, insertTrackMappingQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertTrackMapping.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertTrackMapping.lastError(); Q_EMIT databaseError(); } } { auto initialUpdateTracksValidityQueryText = QStringLiteral("UPDATE `TracksData` " "SET " "`FileModifiedTime` = :mtime " "WHERE `FileName` = :fileName"); auto result = prepareQuery(d->mUpdateTrackFileModifiedTime, initialUpdateTracksValidityQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackFileModifiedTime.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackFileModifiedTime.lastError(); Q_EMIT databaseError(); } } { auto initialUpdateTracksValidityQueryText = QStringLiteral("UPDATE `Tracks` " "SET " "`Priority` = :priority " "WHERE `FileName` = :fileName"); auto result = prepareQuery(d->mUpdateTrackPriority, initialUpdateTracksValidityQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackPriority.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackPriority.lastError(); Q_EMIT databaseError(); } } { auto removeTracksMappingFromSourceQueryText = QStringLiteral("DELETE FROM `TracksData` " "WHERE `FileName` = :fileName"); auto result = prepareQuery(d->mRemoveTracksMappingFromSource, removeTracksMappingFromSourceQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveTracksMappingFromSource.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveTracksMappingFromSource.lastError(); Q_EMIT databaseError(); } } { auto removeTracksMappingQueryText = QStringLiteral("DELETE FROM `TracksData` " "WHERE `FileName` = :fileName"); auto result = prepareQuery(d->mRemoveTracksMapping, removeTracksMappingQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveTracksMapping.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveTracksMapping.lastError(); Q_EMIT databaseError(); } } { auto selectTracksWithoutMappingQueryText = QStringLiteral("SELECT " "tracks.`Id`, " "tracks.`Title`, " "album.`ID`, " "tracks.`ArtistName`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "\"\" as FileName, " "NULL as FileModifiedTime, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "tracks.`AlbumTitle`, " "tracks.`Rating`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "trackGenre.`Name`, " "trackComposer.`Name`, " "trackLyricist.`Name`, " "tracks.`Comment`, " "tracks.`Year`, " "tracks.`Channels`, " "tracks.`BitRate`, " "tracks.`SampleRate`, " "tracks.`HasEmbeddedCover`, " "tracksMapping.`ImportDate`, " "tracksMapping.`FirstPlayDate`, " "tracksMapping.`LastPlayDate`, " "tracksMapping.`PlayCounter`, " "tracksMapping.`PlayCounter` / (strftime('%s', 'now') - tracksMapping.`FirstPlayDate`) as PlayFrequency, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Composer` trackComposer ON trackComposer.`Name` = tracks.`Composer` " "LEFT JOIN `Lyricist` trackLyricist ON trackLyricist.`Name` = tracks.`Lyricist` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "WHERE " "tracks.`FileName` = tracksMapping.`FileName` AND " "tracks.`FileName` NOT IN (SELECT tracksMapping2.`FileName` FROM `TracksData` tracksMapping2)"); auto result = prepareQuery(d->mSelectTracksWithoutMappingQuery, selectTracksWithoutMappingQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksWithoutMappingQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksWithoutMappingQuery.lastError(); Q_EMIT databaseError(); } } { auto selectTracksMappingQueryText = QStringLiteral("SELECT " "track.`ID`, " "trackData.`FileName`, " "track.`Priority`, " "trackData.`FileModifiedTime` " "FROM " "`TracksData` trackData " "LEFT JOIN " "`Tracks` track " "ON " "track.`FileName` = trackData.`FileName` " "WHERE " "trackData.`FileName` = :fileName"); auto result = prepareQuery(d->mSelectTracksMapping, selectTracksMappingQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksMapping.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksMapping.lastError(); Q_EMIT databaseError(); } } { auto selectRadioIdFromHttpAddress = QStringLiteral("SELECT " "`ID` " "FROM " "`Radios` " "WHERE " "`HttpAddress` = :httpAddress"); auto result = prepareQuery(d->mSelectRadioIdFromHttpAddress, selectRadioIdFromHttpAddress); if (!result) { qCInfo(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioIdFromHttpAddress.lastQuery(); qCInfo(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioIdFromHttpAddress.lastError(); Q_EMIT databaseError(); } } { auto selectTracksMappingPriorityQueryText = QStringLiteral("SELECT " "max(tracks.`Priority`) AS Priority " "FROM " "`Tracks` tracks, " "`Albums` albums " "WHERE " "tracks.`Title` = :title AND " "(tracks.`ArtistName` = :trackArtist OR tracks.`ArtistName` IS NULL) AND " "(tracks.`AlbumTitle` = :album OR tracks.`AlbumTitle` IS NULL) AND " "(tracks.`AlbumArtistName` = :albumArtist OR tracks.`AlbumArtistName` IS NULL) AND " "(tracks.`AlbumPath` = :albumPath OR tracks.`AlbumPath` IS NULL)"); auto result = prepareQuery(d->mSelectTracksMappingPriority, selectTracksMappingPriorityQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksMappingPriority.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksMappingPriority.lastError(); Q_EMIT databaseError(); } } { auto selectTracksMappingPriorityQueryByTrackIdText = QStringLiteral("SELECT " "MAX(track.`Priority`) " "FROM " "`TracksData` trackData, " "`Tracks` track " "WHERE " "track.`ID` = :trackId AND " "trackData.`FileName` = track.`FileName`"); auto result = prepareQuery(d->mSelectTracksMappingPriorityByTrackId, selectTracksMappingPriorityQueryByTrackIdText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksMappingPriorityByTrackId.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksMappingPriorityByTrackId.lastError(); Q_EMIT databaseError(); } } { auto selectAllTrackFilesFromSourceQueryText = QStringLiteral("SELECT " "tracksMapping.`FileName`, " "tracksMapping.`FileModifiedTime` " "FROM " "`TracksData` tracksMapping, " "`Tracks` tracks " "WHERE " "tracks.`FileName` = tracksMapping.`FileName`"); auto result = prepareQuery(d->mSelectAllTrackFilesQuery, selectAllTrackFilesFromSourceQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllTrackFilesQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllTrackFilesQuery.lastError(); Q_EMIT databaseError(); } } { auto insertMusicSourceQueryText = QStringLiteral("INSERT OR IGNORE INTO `DiscoverSource` (`ID`, `Name`) " "VALUES (:discoverId, :name)"); auto result = prepareQuery(d->mInsertMusicSource, insertMusicSourceQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertMusicSource.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertMusicSource.lastError(); Q_EMIT databaseError(); } } { auto selectMusicSourceQueryText = QStringLiteral("SELECT `ID` FROM `DiscoverSource` WHERE `Name` = :name"); auto result = prepareQuery(d->mSelectMusicSource, selectMusicSourceQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectMusicSource.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectMusicSource.lastError(); Q_EMIT databaseError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.`ID`, tracksMapping.`FileName` " "FROM " "`Tracks` tracks, " "`Albums` album, " "`TracksData` tracksMapping " "WHERE " "tracks.`Title` = :title AND " "album.`ID` = :album AND " "(tracks.`AlbumTitle` = album.`Title` OR tracks.`AlbumTitle` IS NULL ) AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "(tracks.`AlbumPath` = album.`AlbumPath` OR tracks.`AlbumPath` IS NULL ) AND " "tracks.`ArtistName` = :artist AND " "tracksMapping.`FileName` = tracks.`FileName` AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" ""); auto result = prepareQuery(d->mSelectTrackIdFromTitleAlbumIdArtistQuery, selectTrackQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleAlbumIdArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleAlbumIdArtistQuery.lastError(); Q_EMIT databaseError(); } } { auto insertTrackQueryText = QStringLiteral("INSERT INTO `Tracks` " "(" "`ID`, " "`FileName`, " "`Priority`, " "`Title`, " "`ArtistName`, " "`AlbumTitle`, " "`AlbumArtistName`, " "`AlbumPath`, " "`Genre`, " "`Composer`, " "`Lyricist`, " "`Comment`, " "`TrackNumber`, " "`DiscNumber`, " "`Channels`, " "`BitRate`, " "`SampleRate`, " "`Year`, " "`Duration`, " "`Rating`, " "`HasEmbeddedCover`) " "VALUES " "(" ":trackId, " ":fileName, " ":priority, " ":title, " ":artistName, " ":albumTitle, " ":albumArtistName, " ":albumPath, " ":genre, " ":composer, " ":lyricist, " ":comment, " ":trackNumber, " ":discNumber, " ":channels, " ":bitRate, " ":sampleRate, " ":year, " ":trackDuration, " ":trackRating, " ":hasEmbeddedCover)"); auto result = prepareQuery(d->mInsertTrackQuery, insertTrackQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertTrackQuery.lastError(); Q_EMIT databaseError(); } } { auto updateTrackQueryText = QStringLiteral("UPDATE `Tracks` " "SET " "`FileName` = :fileName, " "`Title` = :title, " "`ArtistName` = :artistName, " "`AlbumTitle` = :albumTitle, " "`AlbumArtistName` = :albumArtistName, " "`AlbumPath` = :albumPath, " "`Genre` = :genre, " "`Composer` = :composer, " "`Lyricist` = :lyricist, " "`Comment` = :comment, " "`TrackNumber` = :trackNumber, " "`DiscNumber` = :discNumber, " "`Channels` = :channels, " "`BitRate` = :bitRate, " "`SampleRate` = :sampleRate, " "`Year` = :year, " " `Duration` = :trackDuration, " "`Rating` = :trackRating " "WHERE " "`ID` = :trackId"); auto result = prepareQuery(d->mUpdateTrackQuery, updateTrackQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackQuery.lastError(); Q_EMIT databaseError(); } } { auto insertRadioQueryText = QStringLiteral("INSERT INTO `Radios` " "(" "`Title`, " "`httpAddress`, " "`Comment`, " "`Rating`, " "`Priority`) " "VALUES " "(" ":title, " ":httpAddress, " ":comment, " ":trackRating," "1)"); auto result = prepareQuery(d->mInsertRadioQuery, insertRadioQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertRadioQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertRadioQuery.lastError(); Q_EMIT databaseError(); } } { auto deleteRadioQueryText = QStringLiteral("DELETE FROM `Radios` " "WHERE `ID` = :radioId"); auto result = prepareQuery(d->mDeleteRadioQuery, deleteRadioQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mDeleteRadioQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mDeleteRadioQuery.lastError(); Q_EMIT databaseError(); } } { auto updateRadioQueryText = QStringLiteral("UPDATE `Radios` " "SET " "`HttpAddress` = :httpAddress, " "`Title` = :title, " "`Comment` = :comment, " "`Rating` = :trackRating " "WHERE " "`ID` = :radioId"); auto result = prepareQuery(d->mUpdateRadioQuery, updateRadioQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateRadioQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateRadioQuery.lastError(); Q_EMIT databaseError(); } } { auto updateAlbumArtistQueryText = QStringLiteral("UPDATE `Albums` " "SET " "`ArtistName` = :artistName " "WHERE " "`ID` = :albumId"); auto result = prepareQuery(d->mUpdateAlbumArtistQuery, updateAlbumArtistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateAlbumArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateAlbumArtistQuery.lastError(); Q_EMIT databaseError(); } } { auto updateAlbumArtistInTracksQueryText = QStringLiteral("UPDATE `Tracks` " "SET " "`AlbumArtistName` = :artistName " "WHERE " "`AlbumTitle` = :albumTitle AND " "`AlbumPath` = :albumPath AND " "`AlbumArtistName` IS NULL"); auto result = prepareQuery(d->mUpdateAlbumArtistInTracksQuery, updateAlbumArtistInTracksQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateAlbumArtistInTracksQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateAlbumArtistInTracksQuery.lastError(); Q_EMIT databaseError(); } } { auto queryMaximumTrackIdQueryText = QStringLiteral("SELECT MAX(tracks.`ID`)" "FROM " "`Tracks` tracks"); auto result = prepareQuery(d->mQueryMaximumTrackIdQuery, queryMaximumTrackIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumTrackIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumTrackIdQuery.lastError(); Q_EMIT databaseError(); } } { auto queryMaximumAlbumIdQueryText = QStringLiteral("SELECT MAX(albums.`ID`)" "FROM " "`Albums` albums"); auto result = prepareQuery(d->mQueryMaximumAlbumIdQuery, queryMaximumAlbumIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumAlbumIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumAlbumIdQuery.lastError(); Q_EMIT databaseError(); } } { auto queryMaximumArtistIdQueryText = QStringLiteral("SELECT MAX(artists.`ID`)" "FROM " "`Artists` artists"); auto result = prepareQuery(d->mQueryMaximumArtistIdQuery, queryMaximumArtistIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumArtistIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumArtistIdQuery.lastError(); Q_EMIT databaseError(); } } { auto queryMaximumLyricistIdQueryText = QStringLiteral("SELECT MAX(lyricists.`ID`)" "FROM " "`Lyricist` lyricists"); auto result = prepareQuery(d->mQueryMaximumLyricistIdQuery, queryMaximumLyricistIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumLyricistIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumLyricistIdQuery.lastError(); Q_EMIT databaseError(); } } { auto queryMaximumComposerIdQueryText = QStringLiteral("SELECT MAX(composers.`ID`)" "FROM " "`Composer` composers"); auto result = prepareQuery(d->mQueryMaximumComposerIdQuery, queryMaximumComposerIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumComposerIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumComposerIdQuery.lastError(); Q_EMIT databaseError(); } } { auto queryMaximumGenreIdQueryText = QStringLiteral("SELECT MAX(genres.`ID`)" "FROM " "`Genre` genres"); auto result = prepareQuery(d->mQueryMaximumGenreIdQuery, queryMaximumGenreIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumGenreIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mQueryMaximumGenreIdQuery.lastError(); Q_EMIT databaseError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.ID " "FROM " "`Tracks` tracks " "WHERE " "tracks.`Title` = :title 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); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.lastError(); Q_EMIT databaseError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.ID " "FROM " "`Tracks` tracks, " "`Albums` albums " "WHERE " "tracks.`Title` = :title AND " "tracks.`Priority` = :priority AND " "(tracks.`ArtistName` = :trackArtist OR tracks.`ArtistName` IS NULL) AND " "(tracks.`AlbumTitle` = :album OR tracks.`AlbumTitle` IS NULL) AND " "(tracks.`AlbumArtistName` = :albumArtist OR tracks.`AlbumArtistName` IS NULL) AND " "(tracks.`AlbumPath` = :albumPath OR tracks.`AlbumPath` IS NULL) AND " "(tracks.`TrackNumber` = :trackNumber OR tracks.`TrackNumber` IS NULL) AND " "(tracks.`DiscNumber` = :discNumber OR tracks.`DiscNumber` IS NULL) " ""); auto result = prepareQuery(d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery, selectTrackQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.lastError(); Q_EMIT databaseError(); } } { auto selectAlbumArtUriFromAlbumIdQueryText = QStringLiteral("SELECT `CoverFileName`" "FROM " "`Albums` " "WHERE " "`ID` = :albumId"); auto result = prepareQuery(d->mSelectAlbumArtUriFromAlbumIdQuery, selectAlbumArtUriFromAlbumIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumArtUriFromAlbumIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumArtUriFromAlbumIdQuery.lastError(); Q_EMIT databaseError(); } } { auto updateAlbumArtUriFromAlbumIdQueryText = QStringLiteral("UPDATE `Albums` " "SET `CoverFileName` = :coverFileName " "WHERE " "`ID` = :albumId"); auto result = prepareQuery(d->mUpdateAlbumArtUriFromAlbumIdQuery, updateAlbumArtUriFromAlbumIdQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateAlbumArtUriFromAlbumIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateAlbumArtUriFromAlbumIdQuery.lastError(); Q_EMIT databaseError(); } } { auto selectTracksFromArtistQueryText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "album.`ID`, " "tracks.`ArtistName`, " "( " "SELECT " "COUNT(DISTINCT tracksFromAlbum1.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum1 " "WHERE " "tracksFromAlbum1.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum1.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum1.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum1.`AlbumPath` = album.`AlbumPath` " ") AS ArtistsCount, " "( " "SELECT " "GROUP_CONCAT(tracksFromAlbum2.`ArtistName`) " "FROM " "`Tracks` tracksFromAlbum2 " "WHERE " "tracksFromAlbum2.`AlbumTitle` = album.`Title` AND " "(tracksFromAlbum2.`AlbumArtistName` = album.`ArtistName` OR " "(tracksFromAlbum2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksFromAlbum2.`AlbumPath` = album.`AlbumPath` " ") AS AllArtists, " "tracks.`AlbumArtistName`, " "tracksMapping.`FileName`, " "tracksMapping.`FileModifiedTime`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "tracks.`AlbumTitle`, " "tracks.`Rating`, " "album.`CoverFileName`, " "(" "SELECT " "COUNT(DISTINCT tracks2.DiscNumber) <= 1 " "FROM " "`Tracks` tracks2 " "WHERE " "tracks2.`AlbumTitle` = album.`Title` AND " "(tracks2.`AlbumArtistName` = album.`ArtistName` OR " "(tracks2.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL" ")" ") AND " "tracks2.`AlbumPath` = album.`AlbumPath` " ") as `IsSingleDiscAlbum`, " "trackGenre.`Name`, " "trackComposer.`Name`, " "trackLyricist.`Name`, " "tracks.`Comment`, " "tracks.`Year`, " "tracks.`Channels`, " "tracks.`BitRate`, " "tracks.`SampleRate`, " "tracks.`HasEmbeddedCover`, " "tracksMapping.`ImportDate`, " "tracksMapping.`FirstPlayDate`, " "tracksMapping.`LastPlayDate`, " "tracksMapping.`PlayCounter`, " "tracksMapping.`PlayCounter` / (strftime('%s', 'now') - tracksMapping.`FirstPlayDate`) as PlayFrequency, " "( " "SELECT tracksCover.`FileName` " "FROM " "`Tracks` tracksCover " "WHERE " "tracksCover.`HasEmbeddedCover` = 1 AND " "tracksCover.`AlbumTitle` = album.`Title` AND " "(tracksCover.`AlbumArtistName` = album.`ArtistName` OR " "(tracksCover.`AlbumArtistName` IS NULL AND " "album.`ArtistName` IS NULL " ") " ") AND " "tracksCover.`AlbumPath` = album.`AlbumPath` " ") as EmbeddedCover " "FROM " "`Tracks` tracks, " "`TracksData` tracksMapping " "LEFT JOIN " "`Albums` album " "ON " "tracks.`AlbumTitle` = album.`Title` AND " "(tracks.`AlbumArtistName` = album.`ArtistName` OR tracks.`AlbumArtistName` IS NULL ) AND " "tracks.`AlbumPath` = album.`AlbumPath` " "LEFT JOIN `Composer` trackComposer ON trackComposer.`Name` = tracks.`Composer` " "LEFT JOIN `Lyricist` trackLyricist ON trackLyricist.`Name` = tracks.`Lyricist` " "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = tracks.`Genre` " "WHERE " "tracks.`ArtistName` = :artistName AND " "tracksMapping.`FileName` = tracks.`FileName` AND " "tracks.`Priority` = (" " SELECT " " MIN(`Priority`) " " FROM " " `Tracks` tracks2 " " WHERE " " tracks.`Title` = tracks2.`Title` AND " " (tracks.`ArtistName` IS NULL OR tracks.`ArtistName` = tracks2.`ArtistName`) AND " " (tracks.`AlbumTitle` IS NULL OR tracks.`AlbumTitle` = tracks2.`AlbumTitle`) AND " " (tracks.`AlbumArtistName` IS NULL OR tracks.`AlbumArtistName` = tracks2.`AlbumArtistName`) AND " " (tracks.`AlbumPath` IS NULL OR tracks.`AlbumPath` = tracks2.`AlbumPath`)" ")" "ORDER BY tracks.`Title` ASC, " "album.`Title` ASC"); auto result = prepareQuery(d->mSelectTracksFromArtist, selectTracksFromArtistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksFromArtist.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectTracksFromArtist.lastError(); Q_EMIT databaseError(); } } { auto selectAlbumIdsFromArtistQueryText = QStringLiteral("SELECT " "album.`ID` " "FROM " "`Albums` album " "WHERE " "album.`ArtistName` = :artistName"); auto result = prepareQuery(d->mSelectAlbumIdsFromArtist, selectAlbumIdsFromArtistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdsFromArtist.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAlbumIdsFromArtist.lastError(); Q_EMIT databaseError(); } } { auto selectArtistQueryText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Artists` " "WHERE " "`ID` = :artistId"); auto result = prepareQuery(d->mSelectArtistQuery, selectArtistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectArtistQuery.lastError(); Q_EMIT databaseError(); } } { auto updateTrackStatisticsQueryText = QStringLiteral("UPDATE `TracksData` " "SET " "`LastPlayDate` = :playDate, " "`PlayCounter` = `PlayCounter` + 1 " "WHERE " "`FileName` = :fileName"); auto result = prepareQuery(d->mUpdateTrackStatistics, updateTrackStatisticsQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackStatistics.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackStatistics.lastError(); Q_EMIT databaseError(); } } { auto updateTrackFirstPlayStatisticsQueryText = QStringLiteral("UPDATE `TracksData` " "SET " "`FirstPlayDate` = :playDate " "WHERE " "`FileName` = :fileName AND " "`FirstPlayDate` IS NULL"); auto result = prepareQuery(d->mUpdateTrackFirstPlayStatistics, updateTrackFirstPlayStatisticsQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackFirstPlayStatistics.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateTrackFirstPlayStatistics.lastError(); Q_EMIT databaseError(); } } { auto selectGenreQueryText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Genre` " "WHERE " "`ID` = :genreId"); auto result = prepareQuery(d->mSelectGenreQuery, selectGenreQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectGenreQuery.lastError(); Q_EMIT databaseError(); } } { auto selectComposerQueryText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Composer` " "WHERE " "`ID` = :composerId"); auto result = prepareQuery(d->mSelectComposerQuery, selectComposerQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectComposerQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectComposerQuery.lastError(); } } { auto selectLyricistQueryText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Lyricist` " "WHERE " "`ID` = :lyricistId"); auto result = prepareQuery(d->mSelectLyricistQuery, selectLyricistQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectLyricistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectLyricistQuery.lastError(); } } { auto removeTrackQueryText = QStringLiteral("DELETE FROM `Tracks` " "WHERE " "`ID` = :trackId"); auto result = prepareQuery(d->mRemoveTrackQuery, removeTrackQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveTrackQuery.lastError(); Q_EMIT databaseError(); } } { auto removeAlbumQueryText = QStringLiteral("DELETE FROM `Albums` " "WHERE " "`ID` = :albumId"); auto result = prepareQuery(d->mRemoveAlbumQuery, removeAlbumQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveAlbumQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveAlbumQuery.lastError(); Q_EMIT databaseError(); } } { auto removeAlbumQueryText = QStringLiteral("DELETE FROM `Artists` " "WHERE " "`ID` = :artistId"); auto result = prepareQuery(d->mRemoveArtistQuery, removeAlbumQueryText); if (!result) { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mRemoveArtistQuery.lastError(); Q_EMIT databaseError(); } } finishTransaction(); d->mInitFinished = true; Q_EMIT requestsInitDone(); } qulonglong DatabaseInterface::insertAlbum(const QString &title, const QString &albumArtist, const QString &trackPath, const QUrl &albumArtURI) { auto result = qulonglong(0); if (title.isEmpty()) { return result; } d->mSelectAlbumIdFromTitleAndArtistQuery.bindValue(QStringLiteral(":title"), title); d->mSelectAlbumIdFromTitleAndArtistQuery.bindValue(QStringLiteral(":albumPath"), trackPath); d->mSelectAlbumIdFromTitleAndArtistQuery.bindValue(QStringLiteral(":artistName"), albumArtist); auto queryResult = execQuery(d->mSelectAlbumIdFromTitleAndArtistQuery); if (!queryResult || !d->mSelectAlbumIdFromTitleAndArtistQuery.isSelect() || !d->mSelectAlbumIdFromTitleAndArtistQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertAlbum" << d->mSelectAlbumIdFromTitleAndArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertAlbum" << d->mSelectAlbumIdFromTitleAndArtistQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertAlbum" << d->mSelectAlbumIdFromTitleAndArtistQuery.lastError(); d->mSelectAlbumIdFromTitleAndArtistQuery.finish(); return result; } if (d->mSelectAlbumIdFromTitleAndArtistQuery.next()) { result = d->mSelectAlbumIdFromTitleAndArtistQuery.record().value(0).toULongLong(); d->mSelectAlbumIdFromTitleAndArtistQuery.finish(); if (!albumArtist.isEmpty()) { const auto similarAlbum = internalOneAlbumPartialData(result); updateAlbumArtist(result, title, trackPath, albumArtist); } return result; } d->mSelectAlbumIdFromTitleAndArtistQuery.finish(); d->mInsertAlbumQuery.bindValue(QStringLiteral(":albumId"), d->mAlbumId); d->mInsertAlbumQuery.bindValue(QStringLiteral(":title"), title); if (!albumArtist.isEmpty()) { insertArtist(albumArtist); d->mInsertAlbumQuery.bindValue(QStringLiteral(":albumArtist"), albumArtist); } else { d->mInsertAlbumQuery.bindValue(QStringLiteral(":albumArtist"), {}); } d->mInsertAlbumQuery.bindValue(QStringLiteral(":albumPath"), trackPath); d->mInsertAlbumQuery.bindValue(QStringLiteral(":coverFileName"), albumArtURI); queryResult = execQuery(d->mInsertAlbumQuery); if (!queryResult || !d->mInsertAlbumQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertAlbum" << d->mInsertAlbumQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertAlbum" << d->mInsertAlbumQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertAlbum" << d->mInsertAlbumQuery.lastError(); d->mInsertAlbumQuery.finish(); return result; } result = d->mAlbumId; d->mInsertAlbumQuery.finish(); ++d->mAlbumId; d->mInsertedAlbums.insert(result); return result; } bool DatabaseInterface::updateAlbumFromId(qulonglong albumId, const QUrl &albumArtUri, const MusicAudioTrack ¤tTrack, const QString &albumPath) { auto modifiedAlbum = false; modifiedAlbum = true; if (!albumArtUri.isValid()) { return modifiedAlbum; } auto storedAlbumArtUri = internalAlbumArtUriFromAlbumId(albumId); if (!storedAlbumArtUri.isValid() || storedAlbumArtUri != albumArtUri) { d->mUpdateAlbumArtUriFromAlbumIdQuery.bindValue(QStringLiteral(":albumId"), albumId); d->mUpdateAlbumArtUriFromAlbumIdQuery.bindValue(QStringLiteral(":coverFileName"), albumArtUri); auto result = execQuery(d->mUpdateAlbumArtUriFromAlbumIdQuery); if (!result || !d->mUpdateAlbumArtUriFromAlbumIdQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumFromId" << d->mUpdateAlbumArtUriFromAlbumIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumFromId" << d->mUpdateAlbumArtUriFromAlbumIdQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumFromId" << d->mUpdateAlbumArtUriFromAlbumIdQuery.lastError(); d->mUpdateAlbumArtUriFromAlbumIdQuery.finish(); return modifiedAlbum; } d->mUpdateAlbumArtUriFromAlbumIdQuery.finish(); modifiedAlbum = true; } if (!isValidArtist(albumId) && currentTrack.isValidAlbumArtist()) { updateAlbumArtist(albumId, currentTrack.albumName(), albumPath, currentTrack.albumArtist()); modifiedAlbum = true; } return modifiedAlbum; } qulonglong DatabaseInterface::insertArtist(const QString &name) { auto result = qulonglong(0); if (name.isEmpty()) { return result; } d->mSelectArtistByNameQuery.bindValue(QStringLiteral(":name"), name); auto queryResult = execQuery(d->mSelectArtistByNameQuery); if (!queryResult || !d->mSelectArtistByNameQuery.isSelect() || !d->mSelectArtistByNameQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.lastError(); d->mSelectArtistByNameQuery.finish(); return result; } if (d->mSelectArtistByNameQuery.next()) { result = d->mSelectArtistByNameQuery.record().value(0).toULongLong(); d->mSelectArtistByNameQuery.finish(); return result; } d->mSelectArtistByNameQuery.finish(); d->mInsertArtistsQuery.bindValue(QStringLiteral(":artistId"), d->mArtistId); d->mInsertArtistsQuery.bindValue(QStringLiteral(":name"), name); queryResult = execQuery(d->mInsertArtistsQuery); if (!queryResult || !d->mInsertArtistsQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mInsertArtistsQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mInsertArtistsQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mInsertArtistsQuery.lastError(); d->mInsertArtistsQuery.finish(); return result; } result = d->mArtistId; ++d->mArtistId; d->mInsertedArtists.insert(result); d->mInsertArtistsQuery.finish(); return result; } qulonglong DatabaseInterface::insertComposer(const QString &name) { auto result = qulonglong(0); if (name.isEmpty()) { return result; } d->mSelectComposerByNameQuery.bindValue(QStringLiteral(":name"), name); auto queryResult = execQuery(d->mSelectComposerByNameQuery); if (!queryResult || !d->mSelectComposerByNameQuery.isSelect() || !d->mSelectComposerByNameQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertComposer" << d->mSelectComposerByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertComposer" << d->mSelectComposerByNameQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertComposer" << d->mSelectComposerByNameQuery.lastError(); d->mSelectComposerByNameQuery.finish(); return result; } if (d->mSelectComposerByNameQuery.next()) { result = d->mSelectComposerByNameQuery.record().value(0).toULongLong(); d->mSelectComposerByNameQuery.finish(); return result; } d->mSelectComposerByNameQuery.finish(); d->mInsertComposerQuery.bindValue(QStringLiteral(":composerId"), d->mComposerId); d->mInsertComposerQuery.bindValue(QStringLiteral(":name"), name); queryResult = execQuery(d->mInsertComposerQuery); if (!queryResult || !d->mInsertComposerQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertComposer" << d->mInsertComposerQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertComposer" << d->mInsertComposerQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertComposer" << d->mInsertComposerQuery.lastError(); d->mInsertComposerQuery.finish(); return result; } result = d->mComposerId; ++d->mComposerId; d->mInsertComposerQuery.finish(); Q_EMIT composersAdded(internalAllComposersPartialData()); return result; } qulonglong DatabaseInterface::insertGenre(const QString &name) { auto result = qulonglong(0); if (name.isEmpty()) { return result; } d->mSelectGenreByNameQuery.bindValue(QStringLiteral(":name"), name); auto queryResult = execQuery(d->mSelectGenreByNameQuery); if (!queryResult || !d->mSelectGenreByNameQuery.isSelect() || !d->mSelectGenreByNameQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertGenre" << d->mSelectGenreByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertGenre" << d->mSelectGenreByNameQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertGenre" << d->mSelectGenreByNameQuery.lastError(); d->mSelectGenreByNameQuery.finish(); return result; } if (d->mSelectGenreByNameQuery.next()) { result = d->mSelectGenreByNameQuery.record().value(0).toULongLong(); d->mSelectGenreByNameQuery.finish(); return result; } d->mSelectGenreByNameQuery.finish(); d->mInsertGenreQuery.bindValue(QStringLiteral(":genreId"), d->mGenreId); d->mInsertGenreQuery.bindValue(QStringLiteral(":name"), name); queryResult = execQuery(d->mInsertGenreQuery); if (!queryResult || !d->mInsertGenreQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertGenre" << d->mInsertGenreQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertGenre" << d->mInsertGenreQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertGenre" << d->mInsertGenreQuery.lastError(); d->mInsertGenreQuery.finish(); return result; } result = d->mGenreId; ++d->mGenreId; d->mInsertGenreQuery.finish(); Q_EMIT genresAdded({{{DatabaseIdRole, result}}}); return result; } void DatabaseInterface::insertTrackOrigin(const QUrl &fileNameURI, const QDateTime &fileModifiedTime, const QDateTime &importDate) { d->mInsertTrackMapping.bindValue(QStringLiteral(":fileName"), fileNameURI); d->mInsertTrackMapping.bindValue(QStringLiteral(":priority"), 1); d->mInsertTrackMapping.bindValue(QStringLiteral(":mtime"), fileModifiedTime); d->mInsertTrackMapping.bindValue(QStringLiteral(":importDate"), importDate.toMSecsSinceEpoch()); auto queryResult = execQuery(d->mInsertTrackMapping); if (!queryResult || !d->mInsertTrackMapping.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mInsertTrackMapping.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mInsertTrackMapping.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mInsertTrackMapping.lastError(); d->mInsertTrackMapping.finish(); return; } d->mInsertTrackMapping.finish(); } void DatabaseInterface::updateTrackOrigin(const QUrl &fileName, const QDateTime &fileModifiedTime) { d->mUpdateTrackFileModifiedTime.bindValue(QStringLiteral(":fileName"), fileName); d->mUpdateTrackFileModifiedTime.bindValue(QStringLiteral(":mtime"), fileModifiedTime); auto queryResult = execQuery(d->mUpdateTrackFileModifiedTime); if (!queryResult || !d->mUpdateTrackFileModifiedTime.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackOrigin" << d->mUpdateTrackFileModifiedTime.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackOrigin" << d->mUpdateTrackFileModifiedTime.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackOrigin" << d->mUpdateTrackFileModifiedTime.lastError(); d->mUpdateTrackFileModifiedTime.finish(); return; } d->mUpdateTrackFileModifiedTime.finish(); } qulonglong DatabaseInterface::internalInsertTrack(const MusicAudioTrack &oneTrack, const QHash &covers, bool &isInserted) { qulonglong resultId = 0; if (oneTrack.title().isEmpty()) { return resultId; } QUrl::FormattingOptions currentOptions = QUrl::PreferLocalFile | QUrl::RemoveAuthority | QUrl::RemoveFilename | QUrl::RemoveFragment | QUrl::RemovePassword | QUrl::RemovePort | QUrl::RemoveQuery | QUrl::RemoveScheme | QUrl::RemoveUserInfo; const auto &trackPath = oneTrack.resourceURI().toString(currentOptions); auto albumCover = covers[oneTrack.resourceURI().toString()]; if (albumCover.isEmpty() && !covers.contains(oneTrack.resourceURI().toString())) { albumCover = oneTrack.albumCover(); } auto albumId = insertAlbum(oneTrack.albumName(), (oneTrack.isValidAlbumArtist() ? oneTrack.albumArtist() : QString()), trackPath, albumCover); auto oldAlbumId = albumId; auto existingTrackId = internalTrackIdFromFileName(oneTrack.resourceURI()); bool isModifiedTrack = (existingTrackId != 0); if (isModifiedTrack) { resultId = existingTrackId; auto oldTrack = internalTrackFromDatabaseId(existingTrackId); oldAlbumId = oldTrack.albumId(); auto isSameTrack = true; isSameTrack = isSameTrack && (oldTrack.title() == oneTrack.title()); isSameTrack = isSameTrack && (oldTrack.albumName() == oneTrack.albumName()); isSameTrack = isSameTrack && (oldTrack.artist() == oneTrack.artist()); isSameTrack = isSameTrack && (oldTrack.albumArtist() == oneTrack.albumArtist()); isSameTrack = isSameTrack && (oldTrack.trackNumberIsValid() == oneTrack.trackNumberIsValid()); if (isSameTrack && oldTrack.trackNumberIsValid()) { isSameTrack = isSameTrack && (oldTrack.trackNumber() == oneTrack.trackNumber()); } isSameTrack = isSameTrack && (oldTrack.discNumberIsValid() == oneTrack.discNumberIsValid()); if (isSameTrack && oldTrack.discNumberIsValid()) { isSameTrack = isSameTrack && (oldTrack.discNumber() == oneTrack.discNumber()); } isSameTrack = isSameTrack && (oldTrack.duration() == oneTrack.duration()); isSameTrack = isSameTrack && (oldTrack.rating() == oneTrack.rating()); isSameTrack = isSameTrack && (oldTrack.resourceURI() == oneTrack.resourceURI()); isSameTrack = isSameTrack && (oldTrack.genre() == oneTrack.genre()); isSameTrack = isSameTrack && (oldTrack.composer() == oneTrack.composer()); isSameTrack = isSameTrack && (oldTrack.lyricist() == oneTrack.lyricist()); isSameTrack = isSameTrack && (oldTrack.comment() == oneTrack.comment()); isSameTrack = isSameTrack && (oldTrack.year() == oneTrack.year()); isSameTrack = isSameTrack && (oldTrack.channelsIsValid() == oneTrack.channelsIsValid()); if (isSameTrack && oldTrack.channelsIsValid()) { isSameTrack = isSameTrack && (oldTrack.channels() == oneTrack.channels()); } isSameTrack = isSameTrack && (oldTrack.bitRateIsValid() == oneTrack.bitRateIsValid()); if (isSameTrack && oldTrack.bitRateIsValid()) { isSameTrack = isSameTrack && (oldTrack.bitRate() == oneTrack.bitRate()); } isSameTrack = isSameTrack && (oldTrack.sampleRateIsValid() == oneTrack.sampleRateIsValid()); if (isSameTrack && oldTrack.sampleRateIsValid()) { isSameTrack = isSameTrack && (oldTrack.sampleRate() == oneTrack.sampleRate()); } if (isSameTrack) { return resultId; } auto newTrack = oneTrack; newTrack.setDatabaseId(resultId); updateTrackInDatabase(newTrack, trackPath); updateTrackOrigin(oneTrack.resourceURI(), oneTrack.fileModificationTime()); updateAlbumFromId(albumId, oneTrack.albumCover(), oneTrack, trackPath); recordModifiedTrack(existingTrackId); if (albumId != 0) { recordModifiedAlbum(albumId); } if (oldAlbumId != 0) { auto tracksCount = fetchTrackIds(oldAlbumId).count(); if (tracksCount) { recordModifiedAlbum(oldAlbumId); } else { removeAlbumInDatabase(oldAlbumId); Q_EMIT albumRemoved(oldAlbumId); } } isInserted = false; return resultId; } else { oldAlbumId = 0; existingTrackId = d->mTrackId; isInserted = true; } int priority = 1; while(true) { auto otherTrackId = getDuplicateTrackIdFromTitleAlbumTrackDiscNumber(oneTrack.title(), oneTrack.artist(), oneTrack.albumName(), oneTrack.albumArtist(), trackPath, oneTrack.trackNumber(), oneTrack.discNumber(), priority); if (otherTrackId) { ++priority; } else { break; } } resultId = existingTrackId; d->mInsertTrackQuery.bindValue(QStringLiteral(":trackId"), existingTrackId); d->mInsertTrackQuery.bindValue(QStringLiteral(":fileName"), oneTrack.resourceURI()); d->mInsertTrackQuery.bindValue(QStringLiteral(":priority"), priority); d->mInsertTrackQuery.bindValue(QStringLiteral(":title"), oneTrack.title()); insertArtist(oneTrack.artist()); d->mInsertTrackQuery.bindValue(QStringLiteral(":artistName"), oneTrack.artist()); d->mInsertTrackQuery.bindValue(QStringLiteral(":albumTitle"), oneTrack.albumName()); if (oneTrack.isValidAlbumArtist()) { d->mInsertTrackQuery.bindValue(QStringLiteral(":albumArtistName"), oneTrack.albumArtist()); } else { d->mInsertTrackQuery.bindValue(QStringLiteral(":albumArtistName"), {}); } 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()); if (insertGenre(oneTrack.genre()) != 0) { d->mInsertTrackQuery.bindValue(QStringLiteral(":genre"), oneTrack.genre()); } else { d->mInsertTrackQuery.bindValue(QStringLiteral(":genre"), {}); } if (insertComposer(oneTrack.composer()) != 0) { d->mInsertTrackQuery.bindValue(QStringLiteral(":composer"), oneTrack.composer()); } else { d->mInsertTrackQuery.bindValue(QStringLiteral(":composer"), {}); } if (insertLyricist(oneTrack.lyricist()) != 0) { d->mInsertTrackQuery.bindValue(QStringLiteral(":lyricist"), oneTrack.lyricist()); } else { d->mInsertTrackQuery.bindValue(QStringLiteral(":lyricist"), {}); } d->mInsertTrackQuery.bindValue(QStringLiteral(":comment"), oneTrack.comment()); 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()); auto result = execQuery(d->mInsertTrackQuery); if (result && d->mInsertTrackQuery.isActive()) { d->mInsertTrackQuery.finish(); if (!isModifiedTrack) { ++d->mTrackId; } updateTrackOrigin(oneTrack.resourceURI(), oneTrack.fileModificationTime()); if (isModifiedTrack) { recordModifiedTrack(existingTrackId); if (albumId != 0) { recordModifiedAlbum(albumId); } if (oldAlbumId != 0) { recordModifiedAlbum(oldAlbumId); } } if (albumId != 0) { if (updateAlbumFromId(albumId, covers[oneTrack.resourceURI().toString()], oneTrack, trackPath)) { auto modifiedTracks = fetchTrackIds(albumId); for (auto oneModifiedTrack : modifiedTracks) { if (oneModifiedTrack != resultId) { recordModifiedTrack(oneModifiedTrack); } } } recordModifiedAlbum(albumId); } } else { d->mInsertTrackQuery.finish(); Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalInsertTrack" << oneTrack << oneTrack.resourceURI(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalInsertTrack" << d->mInsertTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalInsertTrack" << d->mInsertTrackQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalInsertTrack" << d->mInsertTrackQuery.lastError(); } return resultId; } MusicAudioTrack DatabaseInterface::buildTrackFromDatabaseRecord(const QSqlRecord &trackRecord) const { auto id = trackRecord.value(0).toULongLong(); auto result = MusicAudioTrack{}; if (result.isValid()) { return result; } result.setDatabaseId(id); result.setTitle(trackRecord.value(1).toString()); result.setParentId(trackRecord.value(2).toString()); result.setArtist(trackRecord.value(3).toString()); if (trackRecord.value(6).isValid()) { result.setAlbumArtist(trackRecord.value(6).toString()); } else { if (trackRecord.value(5).toInt() == 1) { result.setAlbumArtist(trackRecord.value(6).toString()); } else if (trackRecord.value(5).toInt() > 1) { result.setAlbumArtist(QStringLiteral("Various Artists")); } } result.setResourceURI(trackRecord.value(7).toUrl()); result.setFileModificationTime(trackRecord.value(8).toDateTime()); if (trackRecord.value(9).isValid()) { result.setTrackNumber(trackRecord.value(9).toInt()); } if (trackRecord.value(10).isValid()) { result.setDiscNumber(trackRecord.value(10).toInt()); } result.setDuration(QTime::fromMSecsSinceStartOfDay(trackRecord.value(11).toInt())); result.setAlbumName(trackRecord.value(12).toString()); result.setRating(trackRecord.value(13).toInt()); result.setAlbumCover(trackRecord.value(14).toUrl()); result.setIsSingleDiscAlbum(trackRecord.value(15).toBool()); result.setGenre(trackRecord.value(16).toString()); result.setComposer(trackRecord.value(17).toString()); result.setLyricist(trackRecord.value(18).toString()); result.setComment(trackRecord.value(19).toString()); result.setYear(trackRecord.value(20).toInt()); if (trackRecord.value(21).isValid()) { bool isValid; auto value = trackRecord.value(21).toInt(&isValid); if (isValid) { result.setChannels(value); } } if (trackRecord.value(22).isValid()) { bool isValid; auto value = trackRecord.value(22).toInt(&isValid); if (isValid) { result.setBitRate(value); } } if (trackRecord.value(23).isValid()) { bool isValid; auto value = trackRecord.value(23).toInt(&isValid); if (isValid) { result.setSampleRate(value); } } result.setAlbumId(trackRecord.value(2).toULongLong()); result.setHasEmbeddedCover(trackRecord.value(24).toBool()); result.setValid(true); return result; } DatabaseInterface::TrackDataType DatabaseInterface::buildTrackDataFromDatabaseRecord(const QSqlRecord &trackRecord) const { TrackDataType result; result[TrackDataType::key_type::DatabaseIdRole] = trackRecord.value(0); result[TrackDataType::key_type::TitleRole] = trackRecord.value(1); if (!trackRecord.value(12).isNull()) { result[TrackDataType::key_type::AlbumRole] = trackRecord.value(12); result[TrackDataType::key_type::AlbumIdRole] = trackRecord.value(2); } if (!trackRecord.value(3).isNull()) { result[TrackDataType::key_type::ArtistRole] = trackRecord.value(3); } if (!trackRecord.value(6).isNull()) { result[TrackDataType::key_type::IsValidAlbumArtistRole] = true; result[TrackDataType::key_type::AlbumArtistRole] = trackRecord.value(6); } else { result[TrackDataType::key_type::IsValidAlbumArtistRole] = false; if (trackRecord.value(4).toInt() == 1) { result[TrackDataType::key_type::AlbumArtistRole] = trackRecord.value(3); } else if (trackRecord.value(4).toInt() > 1) { result[TrackDataType::key_type::AlbumArtistRole] = QStringLiteral("Various Artists"); } } result[TrackDataType::key_type::ResourceRole] = trackRecord.value(7); if (!trackRecord.value(9).isNull()) { result[TrackDataType::key_type::TrackNumberRole] = trackRecord.value(9); } if (!trackRecord.value(10).isNull()) { result[TrackDataType::key_type::DiscNumberRole] = trackRecord.value(10); } result[TrackDataType::key_type::DurationRole] = QTime::fromMSecsSinceStartOfDay(trackRecord.value(11).toInt()); result[TrackDataType::key_type::MilliSecondsDurationRole] = trackRecord.value(11).toInt(); result[TrackDataType::key_type::RatingRole] = trackRecord.value(13); if (!trackRecord.value(14).toString().isEmpty()) { result[TrackDataType::key_type::ImageUrlRole] = QUrl(trackRecord.value(14).toString()); } else if (!trackRecord.value(30).toString().isEmpty()) { result[TrackDataType::key_type::ImageUrlRole] = QVariant{QStringLiteral("image://cover/") + trackRecord.value(30).toUrl().toLocalFile()}; } result[TrackDataType::key_type::IsSingleDiscAlbumRole] = trackRecord.value(15); if (!trackRecord.value(16).isNull()) { result[TrackDataType::key_type::GenreRole] = trackRecord.value(16); } if (!trackRecord.value(17).isNull()) { result[TrackDataType::key_type::ComposerRole] = trackRecord.value(17); } if (!trackRecord.value(18).isNull()) { result[TrackDataType::key_type::LyricistRole] = trackRecord.value(18); } result[TrackDataType::key_type::HasEmbeddedCover] = trackRecord.value(24); result[TrackDataType::key_type::FileModificationTime] = trackRecord.value(8); if (!trackRecord.value(26).isNull()) { result[TrackDataType::key_type::FirstPlayDate] = trackRecord.value(26); } if (!trackRecord.value(27).isNull()) { result[TrackDataType::key_type::LastPlayDate] = trackRecord.value(27); } result[TrackDataType::key_type::PlayCounter] = trackRecord.value(28); result[TrackDataType::key_type::PlayFrequency] = trackRecord.value(29); - result[DataType::key_type::ElementTypeRole] = ElisaUtils::Track; + result[TrackDataType::key_type::ElementTypeRole] = ElisaUtils::Track; return result; } DatabaseInterface::TrackDataType DatabaseInterface::buildRadioDataFromDatabaseRecord(const QSqlRecord &trackRecord) const { TrackDataType result; result[TrackDataType::key_type::DatabaseIdRole] = trackRecord.value(0); result[TrackDataType::key_type::TitleRole] = trackRecord.value(1); result[TrackDataType::key_type::AlbumRole] = QStringLiteral("Radios"); result[TrackDataType::key_type::ArtistRole] = trackRecord.value(1); result[TrackDataType::key_type::ResourceRole] = trackRecord.value(2); result[TrackDataType::key_type::RatingRole] = trackRecord.value(3); if (!trackRecord.value(4).isNull()) { result[TrackDataType::key_type::GenreRole] = trackRecord.value(4); } result[TrackDataType::key_type::CommentRole] = trackRecord.value(5); - result[DataType::key_type::ElementTypeRole] = ElisaUtils::Radio; + result[TrackDataType::key_type::ElementTypeRole] = ElisaUtils::Radio; return result; } void DatabaseInterface::internalRemoveTracksList(const QList &removedTracks) { QSet modifiedAlbums; QUrl::FormattingOptions currentOptions = QUrl::PreferLocalFile | QUrl::RemoveAuthority | QUrl::RemoveFilename | QUrl::RemoveFragment | QUrl::RemovePassword | QUrl::RemovePort | QUrl::RemoveQuery | QUrl::RemoveScheme | QUrl::RemoveUserInfo; for (const auto &removedTrackFileName : removedTracks) { auto removedTrackId = internalTrackIdFromFileName(removedTrackFileName); Q_EMIT trackRemoved(removedTrackId); auto oneRemovedTrack = internalTrackFromDatabaseId(removedTrackId); removeTrackInDatabase(removedTrackId); const auto &trackPath = oneRemovedTrack.resourceURI().toString(currentOptions); const auto &modifiedAlbumId = internalAlbumIdFromTitleAndArtist(oneRemovedTrack.albumName(), oneRemovedTrack.albumArtist(), trackPath); const auto &allTracksFromArtist = internalTracksFromAuthor(oneRemovedTrack.artist()); const auto &allAlbumsFromArtist = internalAlbumIdsFromAuthor(oneRemovedTrack.artist()); const auto &removedArtistId = internalArtistIdFromName(oneRemovedTrack.artist()); if (modifiedAlbumId) { recordModifiedAlbum(modifiedAlbumId); modifiedAlbums.insert(modifiedAlbumId); updateAlbumFromId(modifiedAlbumId, oneRemovedTrack.albumCover(), oneRemovedTrack, trackPath); } if (removedArtistId != 0 && allTracksFromArtist.isEmpty() && allAlbumsFromArtist.isEmpty()) { removeArtistInDatabase(removedArtistId); Q_EMIT artistRemoved(removedArtistId); } d->mRemoveTracksMapping.bindValue(QStringLiteral(":fileName"), removedTrackFileName.toString()); auto result = execQuery(d->mRemoveTracksMapping); if (!result || !d->mRemoveTracksMapping.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalRemoveTracksList" << d->mRemoveTracksMapping.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalRemoveTracksList" << d->mRemoveTracksMapping.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalRemoveTracksList" << d->mRemoveTracksMapping.lastError(); continue; } d->mRemoveTracksMapping.finish(); } for (auto modifiedAlbumId : modifiedAlbums) { const auto &modifiedAlbumData = internalOneAlbumPartialData(modifiedAlbumId); auto tracksCount = fetchTrackIds(modifiedAlbumId).count(); if (!modifiedAlbumData.isEmpty() && tracksCount) { Q_EMIT albumModified({{DatabaseIdRole, modifiedAlbumId}}, modifiedAlbumId); } else { removeAlbumInDatabase(modifiedAlbumId); Q_EMIT albumRemoved(modifiedAlbumId); const auto &allTracksFromArtist = internalTracksFromAuthor(modifiedAlbumData[AlbumDataType::key_type::ArtistRole].toString()); const auto &allAlbumsFromArtist = internalAlbumIdsFromAuthor(modifiedAlbumData[AlbumDataType::key_type::ArtistRole].toString()); const auto &removedArtistId = internalArtistIdFromName(modifiedAlbumData[AlbumDataType::key_type::ArtistRole].toString()); if (removedArtistId != 0 && allTracksFromArtist.isEmpty() && allAlbumsFromArtist.isEmpty()) { removeArtistInDatabase(removedArtistId); Q_EMIT artistRemoved(removedArtistId); } } } } QUrl DatabaseInterface::internalAlbumArtUriFromAlbumId(qulonglong albumId) { auto result = QUrl(); d->mSelectAlbumArtUriFromAlbumIdQuery.bindValue(QStringLiteral(":albumId"), albumId); auto queryResult = execQuery(d->mSelectAlbumArtUriFromAlbumIdQuery); if (!queryResult || !d->mSelectAlbumArtUriFromAlbumIdQuery.isSelect() || !d->mSelectAlbumArtUriFromAlbumIdQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectAlbumArtUriFromAlbumIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectAlbumArtUriFromAlbumIdQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectAlbumArtUriFromAlbumIdQuery.lastError(); d->mSelectAlbumArtUriFromAlbumIdQuery.finish(); return result; } if (!d->mSelectAlbumArtUriFromAlbumIdQuery.next()) { d->mSelectAlbumArtUriFromAlbumIdQuery.finish(); return result; } result = d->mSelectAlbumArtUriFromAlbumIdQuery.record().value(0).toUrl(); d->mSelectAlbumArtUriFromAlbumIdQuery.finish(); return result; } bool DatabaseInterface::isValidArtist(qulonglong albumId) { auto result = false; d->mSelectAlbumQuery.bindValue(QStringLiteral(":albumId"), albumId); auto queryResult = execQuery(d->mSelectAlbumQuery); if (!queryResult || !d->mSelectAlbumQuery.isSelect() || !d->mSelectAlbumQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumFromId" << d->mSelectAlbumQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumFromId" << d->mSelectAlbumQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumFromId" << d->mSelectAlbumQuery.lastError(); d->mSelectAlbumQuery.finish(); return result; } if (!d->mSelectAlbumQuery.next()) { d->mSelectAlbumQuery.finish(); return result; } const auto ¤tRecord = d->mSelectAlbumQuery.record(); result = !currentRecord.value(2).toString().isEmpty(); return result; } bool DatabaseInterface::internalGenericPartialData(QSqlQuery &query) { auto result = false; auto queryResult = execQuery(query); if (!queryResult || !query.isSelect() || !query.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAllGenericPartialData" << query.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAllGenericPartialData" << query.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAllGenericPartialData" << query.lastError(); query.finish(); auto transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } result = true; return result; } qulonglong DatabaseInterface::insertLyricist(const QString &name) { auto result = qulonglong(0); if (name.isEmpty()) { return result; } d->mSelectLyricistByNameQuery.bindValue(QStringLiteral(":name"), name); auto queryResult = execQuery(d->mSelectLyricistByNameQuery); if (!queryResult || !d->mSelectLyricistByNameQuery.isSelect() || !d->mSelectLyricistByNameQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertLyricist" << d->mSelectLyricistByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertLyricist" << d->mSelectLyricistByNameQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertLyricist" << d->mSelectLyricistByNameQuery.lastError(); d->mSelectLyricistByNameQuery.finish(); return result; } if (d->mSelectLyricistByNameQuery.next()) { result = d->mSelectLyricistByNameQuery.record().value(0).toULongLong(); d->mSelectLyricistByNameQuery.finish(); return result; } d->mSelectLyricistByNameQuery.finish(); d->mInsertLyricistQuery.bindValue(QStringLiteral(":lyricistId"), d->mLyricistId); d->mInsertLyricistQuery.bindValue(QStringLiteral(":name"), name); queryResult = execQuery(d->mInsertLyricistQuery); if (!queryResult || !d->mInsertLyricistQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertLyricist" << d->mInsertLyricistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertLyricist" << d->mInsertLyricistQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertLyricist" << d->mInsertLyricistQuery.lastError(); d->mInsertLyricistQuery.finish(); return result; } result = d->mLyricistId; ++d->mLyricistId; d->mInsertLyricistQuery.finish(); Q_EMIT lyricistsAdded(internalAllLyricistsPartialData()); return result; } QHash DatabaseInterface::internalAllFileName() { auto allFileNames = QHash{}; auto queryResult = execQuery(d->mSelectAllTrackFilesQuery); if (!queryResult || !d->mSelectAllTrackFilesQuery.isSelect() || !d->mSelectAllTrackFilesQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertMusicSource" << d->mSelectAllTrackFilesQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertMusicSource" << d->mSelectAllTrackFilesQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertMusicSource" << d->mSelectAllTrackFilesQuery.lastError(); d->mSelectAllTrackFilesQuery.finish(); return allFileNames; } while(d->mSelectAllTrackFilesQuery.next()) { auto fileName = d->mSelectAllTrackFilesQuery.record().value(0).toUrl(); auto fileModificationTime = d->mSelectAllTrackFilesQuery.record().value(1).toDateTime(); allFileNames[fileName] = fileModificationTime; } d->mSelectAllTrackFilesQuery.finish(); return allFileNames; } qulonglong DatabaseInterface::internalArtistIdFromName(const QString &name) { auto result = qulonglong(0); if (name.isEmpty()) { return result; } d->mSelectArtistByNameQuery.bindValue(QStringLiteral(":name"), name); auto queryResult = execQuery(d->mSelectArtistByNameQuery); if (!queryResult || !d->mSelectArtistByNameQuery.isSelect() || !d->mSelectArtistByNameQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.lastError(); d->mSelectArtistByNameQuery.finish(); return result; } if (!d->mSelectArtistByNameQuery.next()) { d->mSelectArtistByNameQuery.finish(); return result; } result = d->mSelectArtistByNameQuery.record().value(0).toULongLong(); d->mSelectArtistByNameQuery.finish(); return result; } void DatabaseInterface::removeTrackInDatabase(qulonglong trackId) { d->mRemoveTrackQuery.bindValue(QStringLiteral(":trackId"), trackId); auto result = execQuery(d->mRemoveTrackQuery); if (!result || !d->mRemoveTrackQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeTrackInDatabase" << d->mRemoveTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeTrackInDatabase" << d->mRemoveTrackQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeTrackInDatabase" << d->mRemoveTrackQuery.lastError(); } d->mRemoveTrackQuery.finish(); } void DatabaseInterface::updateTrackInDatabase(const MusicAudioTrack &oneTrack, const QString &albumPath) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":fileName"), oneTrack.resourceURI()); d->mUpdateTrackQuery.bindValue(QStringLiteral(":trackId"), oneTrack.databaseId()); d->mUpdateTrackQuery.bindValue(QStringLiteral(":title"), oneTrack.title()); insertArtist(oneTrack.artist()); d->mUpdateTrackQuery.bindValue(QStringLiteral(":artistName"), oneTrack.artist()); d->mUpdateTrackQuery.bindValue(QStringLiteral(":albumTitle"), oneTrack.albumName()); if (oneTrack.isValidAlbumArtist()) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":albumArtistName"), oneTrack.albumArtist()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":albumArtistName"), {}); } d->mUpdateTrackQuery.bindValue(QStringLiteral(":albumPath"), albumPath); if (oneTrack.trackNumberIsValid()) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":trackNumber"), oneTrack.trackNumber()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":trackNumber"), {}); } if (oneTrack.discNumberIsValid()) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":discNumber"), oneTrack.discNumber()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":discNumber"), {}); } d->mUpdateTrackQuery.bindValue(QStringLiteral(":trackDuration"), QVariant::fromValue(oneTrack.duration().msecsSinceStartOfDay())); d->mUpdateTrackQuery.bindValue(QStringLiteral(":trackRating"), oneTrack.rating()); if (insertGenre(oneTrack.genre()) != 0) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":genre"), oneTrack.genre()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":genre"), {}); } if (insertComposer(oneTrack.composer()) != 0) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":composer"), oneTrack.composer()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":composer"), {}); } if (insertLyricist(oneTrack.lyricist()) != 0) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":lyricist"), oneTrack.lyricist()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":lyricist"), {}); } d->mUpdateTrackQuery.bindValue(QStringLiteral(":comment"), oneTrack.comment()); d->mUpdateTrackQuery.bindValue(QStringLiteral(":year"), oneTrack.year()); if (oneTrack.channelsIsValid()) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":channels"), oneTrack.channels()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":channels"), {}); } if (oneTrack.bitRateIsValid()) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":bitRate"), oneTrack.bitRate()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":bitRate"), {}); } if (oneTrack.sampleRateIsValid()) { d->mUpdateTrackQuery.bindValue(QStringLiteral(":sampleRate"), oneTrack.sampleRate()); } else { d->mUpdateTrackQuery.bindValue(QStringLiteral(":sampleRate"), {}); } auto result = execQuery(d->mUpdateTrackQuery); if (!result || !d->mUpdateTrackQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << d->mUpdateTrackQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << d->mUpdateTrackQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << d->mUpdateTrackQuery.lastError(); } d->mUpdateTrackQuery.finish(); } void DatabaseInterface::insertRadio(const TrackDataType &oneTrack) { QSqlQuery query = d->mUpdateRadioQuery; if (oneTrack.databaseId() == -1ull) { query = d->mInsertRadioQuery; } query.bindValue(QStringLiteral(":httpAddress"), oneTrack.resourceURI()); query.bindValue(QStringLiteral(":radioId"), oneTrack.databaseId()); query.bindValue(QStringLiteral(":title"), oneTrack.title()); query.bindValue(QStringLiteral(":comment"), oneTrack.comment()); query.bindValue(QStringLiteral(":trackRating"), oneTrack.rating()); auto result = execQuery(query); if (!result || !query.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastError(); - }else{ - TrackDataType radio(oneTrack); - radio[TrackDataType::key_type::ArtistRole] = radio[TrackDataType::key_type::TitleRole]; - if (radio[TrackDataType::key_type::DatabaseIdRole] == -1) { - radio[TrackDataType::key_type::DatabaseIdRole] = internalRadioIdFromHttpAddress(oneTrack.resourceURI().toString()); - radio[TrackDataType::key_type::AlbumRole] = QStringLiteral("Radios"); - radio[TrackDataType::key_type::ArtistRole] = radio[TrackDataType::key_type::TitleRole]; - radio[DataType::key_type::ElementTypeRole] = ElisaUtils::Radio; - // Genre and rating missing for now, see buildRadioDataFromDatabaseRecord. Should be added if used for radios. + } else { + if (oneTrack[TrackDataType::key_type::DatabaseIdRole] == -1) { + auto radio = internalOneRadioPartialData(internalRadioIdFromHttpAddress(oneTrack.resourceURI().toString())); Q_EMIT radioAdded(radio); } else { + auto radio = internalOneRadioPartialData(oneTrack.databaseId()); + Q_EMIT radioModified(radio); } } query.finish(); } void DatabaseInterface::removeRadio(qulonglong radioId) { QSqlQuery query = d->mDeleteRadioQuery; query.bindValue(QStringLiteral(":radioId"), radioId); auto result = execQuery(query); if (!result || !query.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastError(); }else{ Q_EMIT radioRemoved(radioId); } query.finish(); } void DatabaseInterface::removeAlbumInDatabase(qulonglong albumId) { d->mRemoveAlbumQuery.bindValue(QStringLiteral(":albumId"), albumId); auto result = execQuery(d->mRemoveAlbumQuery); if (!result || !d->mRemoveAlbumQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeAlbumInDatabase" << d->mRemoveAlbumQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeAlbumInDatabase" << d->mRemoveAlbumQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeAlbumInDatabase" << d->mRemoveAlbumQuery.lastError(); } d->mRemoveAlbumQuery.finish(); } void DatabaseInterface::removeArtistInDatabase(qulonglong artistId) { d->mRemoveArtistQuery.bindValue(QStringLiteral(":artistId"), artistId); auto result = execQuery(d->mRemoveArtistQuery); if (!result || !d->mRemoveArtistQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeArtistInDatabase" << d->mRemoveArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeArtistInDatabase" << d->mRemoveArtistQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::removeArtistInDatabase" << d->mRemoveArtistQuery.lastError(); } d->mRemoveArtistQuery.finish(); } void DatabaseInterface::reloadExistingDatabase() { qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::reloadExistingDatabase"; d->mArtistId = genericInitialId(d->mQueryMaximumArtistIdQuery); d->mComposerId = genericInitialId(d->mQueryMaximumComposerIdQuery); d->mLyricistId = genericInitialId(d->mQueryMaximumLyricistIdQuery); d->mAlbumId = genericInitialId(d->mQueryMaximumAlbumIdQuery); d->mTrackId = genericInitialId(d->mQueryMaximumTrackIdQuery); d->mGenreId = genericInitialId(d->mQueryMaximumGenreIdQuery); } qulonglong DatabaseInterface::genericInitialId(QSqlQuery &request) { auto result = qulonglong(0); auto transactionResult = startTransaction(); if (!transactionResult) { return result; } auto queryResult = execQuery(request); if (!queryResult || !request.isSelect() || !request.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertMusicSource" << request.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertMusicSource" << request.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::insertMusicSource" << request.lastError(); request.finish(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } if (request.next()) { result = request.record().value(0).toULongLong() + 1; request.finish(); } transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } QList DatabaseInterface::fetchTrackIds(qulonglong albumId) { auto allTracks = QList(); d->mSelectTrackIdQuery.bindValue(QStringLiteral(":albumId"), albumId); auto result = execQuery(d->mSelectTrackIdQuery); if (!result || !d->mSelectTrackIdQuery.isSelect() || !d->mSelectTrackIdQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::fetchTrackIds" << d->mSelectTrackIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::fetchTrackIds" << d->mSelectTrackIdQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::fetchTrackIds" << d->mSelectTrackIdQuery.lastError(); } while (d->mSelectTrackIdQuery.next()) { const auto ¤tRecord = d->mSelectTrackIdQuery.record(); allTracks.push_back(currentRecord.value(0).toULongLong()); } d->mSelectTrackIdQuery.finish(); return allTracks; } qulonglong DatabaseInterface::internalAlbumIdFromTitleAndArtist(const QString &title, const QString &artist, const QString &albumPath) { auto result = qulonglong(0); d->mSelectAlbumIdFromTitleQuery.bindValue(QStringLiteral(":title"), title); d->mSelectAlbumIdFromTitleQuery.bindValue(QStringLiteral(":artistName"), artist); auto queryResult = execQuery(d->mSelectAlbumIdFromTitleQuery); if (!queryResult || !d->mSelectAlbumIdFromTitleQuery.isSelect() || !d->mSelectAlbumIdFromTitleQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumIdFromTitleAndArtist" << d->mSelectAlbumIdFromTitleQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumIdFromTitleAndArtist" << d->mSelectAlbumIdFromTitleQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumIdFromTitleAndArtist" << d->mSelectAlbumIdFromTitleQuery.lastError(); d->mSelectAlbumIdFromTitleQuery.finish(); return result; } if (d->mSelectAlbumIdFromTitleQuery.next()) { result = d->mSelectAlbumIdFromTitleQuery.record().value(0).toULongLong(); } d->mSelectAlbumIdFromTitleQuery.finish(); if (result == 0) { d->mSelectAlbumIdFromTitleWithoutArtistQuery.bindValue(QStringLiteral(":title"), title); d->mSelectAlbumIdFromTitleWithoutArtistQuery.bindValue(QStringLiteral(":albumPath"), albumPath); auto queryResult = execQuery(d->mSelectAlbumIdFromTitleWithoutArtistQuery); if (!queryResult || !d->mSelectAlbumIdFromTitleWithoutArtistQuery.isSelect() || !d->mSelectAlbumIdFromTitleWithoutArtistQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumIdFromTitleAndArtist" << d->mSelectAlbumIdFromTitleWithoutArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumIdFromTitleAndArtist" << d->mSelectAlbumIdFromTitleWithoutArtistQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalAlbumIdFromTitleAndArtist" << d->mSelectAlbumIdFromTitleWithoutArtistQuery.lastError(); d->mSelectAlbumIdFromTitleWithoutArtistQuery.finish(); return result; } if (d->mSelectAlbumIdFromTitleWithoutArtistQuery.next()) { result = d->mSelectAlbumIdFromTitleWithoutArtistQuery.record().value(0).toULongLong(); } d->mSelectAlbumIdFromTitleWithoutArtistQuery.finish(); } return result; } MusicAudioTrack DatabaseInterface::internalTrackFromDatabaseId(qulonglong id) { auto result = MusicAudioTrack(); if (result.isValid()) { return result; } if (!d || !d->mTracksDatabase.isValid() || !d->mInitFinished) { return result; } d->mSelectTrackFromIdQuery.bindValue(QStringLiteral(":trackId"), id); auto queryResult = execQuery(d->mSelectTrackFromIdQuery); if (!queryResult || !d->mSelectTrackFromIdQuery.isSelect() || !d->mSelectTrackFromIdQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackFromDatabaseId" << d->mSelectTrackFromIdQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackFromDatabaseId" << d->mSelectTrackFromIdQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackFromDatabaseId" << d->mSelectTrackFromIdQuery.lastError(); d->mSelectTrackFromIdQuery.finish(); return result; } if (!d->mSelectTrackFromIdQuery.next()) { d->mSelectTrackFromIdQuery.finish(); return result; } const auto ¤tRecord = d->mSelectTrackFromIdQuery.record(); result = buildTrackFromDatabaseRecord(currentRecord); d->mSelectTrackFromIdQuery.finish(); return result; } qulonglong DatabaseInterface::internalTrackIdFromTitleAlbumTracDiscNumber(const QString &title, const QString &artist, const std::optional &album, std::optional trackNumber, std::optional discNumber) { auto result = qulonglong(0); if (!d) { return result; } d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":title"), title); d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":artist"), artist); 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); if (!queryResult || !d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.isSelect() || !d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.lastError(); d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.finish(); return result; } if (d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.next()) { result = d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.record().value(0).toULongLong(); } d->mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery.finish(); return result; } qulonglong DatabaseInterface::getDuplicateTrackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &trackArtist, const QString &album, const QString &albumArtist, const QString &trackPath, int trackNumber, int discNumber, int priority) { auto result = qulonglong(0); if (!d) { return result; } d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":title"), title); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":trackArtist"), trackArtist); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":album"), album); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":albumPath"), trackPath); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":albumArtist"), albumArtist); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":trackNumber"), trackNumber); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":discNumber"), discNumber); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.bindValue(QStringLiteral(":priority"), priority); auto queryResult = execQuery(d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery); if (!queryResult || !d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.isSelect() || !d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.lastError(); d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.finish(); return result; } if (d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.next()) { result = d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.record().value(0).toULongLong(); } d->mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery.finish(); return result; } qulonglong DatabaseInterface::internalTrackIdFromFileName(const QUrl &fileName) { auto result = qulonglong(0); if (!d) { return result; } d->mSelectTracksMapping.bindValue(QStringLiteral(":fileName"), fileName.toString()); auto queryResult = execQuery(d->mSelectTracksMapping); if (!queryResult || !d->mSelectTracksMapping.isSelect() || !d->mSelectTracksMapping.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectTracksMapping.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectTracksMapping.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectTracksMapping.lastError(); d->mSelectTracksMapping.finish(); return result; } if (d->mSelectTracksMapping.next()) { const auto ¤tRecordValue = d->mSelectTracksMapping.record().value(0); if (currentRecordValue.isValid()) { result = currentRecordValue.toULongLong(); } } d->mSelectTracksMapping.finish(); return result; } qulonglong DatabaseInterface::internalRadioIdFromHttpAddress(const QString &httpAddress) { auto result = qulonglong(0); if (!d) { return result; } d->mSelectRadioIdFromHttpAddress.bindValue(QStringLiteral(":httpAddress"), httpAddress); auto queryResult = execQuery(d->mSelectRadioIdFromHttpAddress); if (!queryResult || !d->mSelectRadioIdFromHttpAddress.isSelect() || !d->mSelectRadioIdFromHttpAddress.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectRadioIdFromHttpAddress.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectRadioIdFromHttpAddress.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectRadioIdFromHttpAddress.lastError(); d->mSelectRadioIdFromHttpAddress.finish(); return result; } if (d->mSelectRadioIdFromHttpAddress.next()) { const auto ¤tRecordValue = d->mSelectRadioIdFromHttpAddress.record().value(0); if (currentRecordValue.isValid()) { result = currentRecordValue.toULongLong(); } } d->mSelectRadioIdFromHttpAddress.finish(); return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::internalTracksFromAuthor(const QString &ArtistName) { auto allTracks = ListTrackDataType{}; d->mSelectTracksFromArtist.bindValue(QStringLiteral(":artistName"), ArtistName); auto result = execQuery(d->mSelectTracksFromArtist); if (!result || !d->mSelectTracksFromArtist.isSelect() || !d->mSelectTracksFromArtist.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::tracksFromAuthor" << d->mSelectTracksFromArtist.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::tracksFromAuthor" << d->mSelectTracksFromArtist.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::tracksFromAuthor" << d->mSelectTracksFromArtist.lastError(); return allTracks; } while (d->mSelectTracksFromArtist.next()) { const auto ¤tRecord = d->mSelectTracksFromArtist.record(); allTracks.push_back(buildTrackDataFromDatabaseRecord(currentRecord)); } d->mSelectTracksFromArtist.finish(); return allTracks; } QList DatabaseInterface::internalAlbumIdsFromAuthor(const QString &ArtistName) { auto allAlbumIds = QList(); d->mSelectAlbumIdsFromArtist.bindValue(QStringLiteral(":artistName"), ArtistName); auto result = execQuery(d->mSelectAlbumIdsFromArtist); if (!result || !d->mSelectAlbumIdsFromArtist.isSelect() || !d->mSelectAlbumIdsFromArtist.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::tracksFromAuthor" << d->mSelectAlbumIdsFromArtist.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::tracksFromAuthor" << d->mSelectAlbumIdsFromArtist.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::tracksFromAuthor" << d->mSelectAlbumIdsFromArtist.lastError(); return allAlbumIds; } while (d->mSelectAlbumIdsFromArtist.next()) { const auto ¤tRecord = d->mSelectAlbumIdsFromArtist.record(); allAlbumIds.push_back(currentRecord.value(0).toULongLong()); } d->mSelectAlbumIdsFromArtist.finish(); return allAlbumIds; } DatabaseInterface::ListArtistDataType DatabaseInterface::internalAllArtistsPartialData(QSqlQuery &artistsQuery) { auto result = ListArtistDataType{}; if (!internalGenericPartialData(artistsQuery)) { return result; } while(artistsQuery.next()) { auto newData = ArtistDataType{}; const auto ¤tRecord = artistsQuery.record(); newData[DataType::key_type::DatabaseIdRole] = currentRecord.value(0); newData[DataType::key_type::TitleRole] = currentRecord.value(1); newData[DataType::key_type::GenreRole] = QVariant::fromValue(currentRecord.value(2).toString().split(QStringLiteral(", "))); newData[DataType::key_type::ElementTypeRole] = ElisaUtils::Artist; result.push_back(newData); } artistsQuery.finish(); return result; } DatabaseInterface::ListAlbumDataType DatabaseInterface::internalAllAlbumsPartialData(QSqlQuery &query) { auto result = ListAlbumDataType{}; if (!internalGenericPartialData(query)) { return result; } while(query.next()) { auto newData = AlbumDataType{}; const auto ¤tRecord = query.record(); newData[DataType::key_type::DatabaseIdRole] = currentRecord.value(0); newData[DataType::key_type::TitleRole] = currentRecord.value(1); if (!currentRecord.value(3).toString().isEmpty()) { newData[DataType::key_type::ImageUrlRole] = currentRecord.value(3); } else if (!currentRecord.value(10).toString().isEmpty()) { newData[DataType::key_type::ImageUrlRole] = QVariant{QStringLiteral("image://cover/") + currentRecord.value(10).toUrl().toLocalFile()}; } auto allArtists = currentRecord.value(6).toString().split(QStringLiteral(", ")); allArtists.removeDuplicates(); newData[DataType::key_type::AllArtistsRole] = QVariant::fromValue(allArtists); if (!currentRecord.value(4).isNull()) { newData[DataType::key_type::IsValidAlbumArtistRole] = true; newData[DataType::key_type::SecondaryTextRole] = currentRecord.value(4); } else { newData[DataType::key_type::IsValidAlbumArtistRole] = false; if (currentRecord.value(5).toInt() == 1) { newData[DataType::key_type::SecondaryTextRole] = allArtists.first(); } else if (currentRecord.value(5).toInt() > 1) { newData[DataType::key_type::SecondaryTextRole] = QStringLiteral("Various Artists"); } } newData[DataType::key_type::ArtistRole] = newData[DataType::key_type::SecondaryTextRole]; newData[DataType::key_type::HighestTrackRating] = currentRecord.value(7); newData[DataType::key_type::IsSingleDiscAlbumRole] = currentRecord.value(9); newData[DataType::key_type::GenreRole] = QVariant::fromValue(currentRecord.value(8).toString().split(QStringLiteral(", "))); newData[DataType::key_type::ElementTypeRole] = ElisaUtils::Album; result.push_back(newData); } query.finish(); return result; } DatabaseInterface::AlbumDataType DatabaseInterface::internalOneAlbumPartialData(qulonglong databaseId) { auto result = AlbumDataType{}; d->mSelectAlbumQuery.bindValue(QStringLiteral(":albumId"), databaseId); if (!internalGenericPartialData(d->mSelectAlbumQuery)) { return result; } if (d->mSelectAlbumQuery.next()) { const auto ¤tRecord = d->mSelectAlbumQuery.record(); result[DataType::key_type::DatabaseIdRole] = currentRecord.value(0); result[DataType::key_type::TitleRole] = currentRecord.value(1); if (!currentRecord.value(4).toString().isEmpty()) { result[DataType::key_type::ImageUrlRole] = currentRecord.value(4); } else if (!currentRecord.value(11).toString().isEmpty()) { result[DataType::key_type::ImageUrlRole] = QVariant{QStringLiteral("image://cover/") + currentRecord.value(11).toUrl().toLocalFile()}; } auto allArtists = currentRecord.value(8).toString().split(QStringLiteral(", ")); allArtists.removeDuplicates(); result[DataType::key_type::AllArtistsRole] = QVariant::fromValue(allArtists); if (!currentRecord.value(2).isNull()) { result[DataType::key_type::IsValidAlbumArtistRole] = true; result[DataType::key_type::SecondaryTextRole] = currentRecord.value(2); } else { result[DataType::key_type::IsValidAlbumArtistRole] = false; if (currentRecord.value(7).toInt() == 1) { result[DataType::key_type::SecondaryTextRole] = allArtists.first(); } else if (currentRecord.value(7).toInt() > 1) { result[DataType::key_type::SecondaryTextRole] = QStringLiteral("Various Artists"); } } result[DataType::key_type::ArtistRole] = result[DataType::key_type::SecondaryTextRole]; result[DataType::key_type::HighestTrackRating] = currentRecord.value(9); result[DataType::key_type::IsSingleDiscAlbumRole] = currentRecord.value(6); result[DataType::key_type::GenreRole] = QVariant::fromValue(currentRecord.value(10).toString().split(QStringLiteral(", "))); result[DataType::key_type::ElementTypeRole] = ElisaUtils::Album; } d->mSelectAlbumQuery.finish(); return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::internalAllTracksPartialData() { auto result = ListTrackDataType{}; if (!internalGenericPartialData(d->mSelectAllTracksQuery)) { return result; } while(d->mSelectAllTracksQuery.next()) { const auto ¤tRecord = d->mSelectAllTracksQuery.record(); auto newData = buildTrackDataFromDatabaseRecord(currentRecord); result.push_back(newData); } d->mSelectAllTracksQuery.finish(); return result; } DatabaseInterface::ListRadioDataType DatabaseInterface::internalAllRadiosPartialData() { auto result = ListRadioDataType{}; if (!internalGenericPartialData(d->mSelectAllRadiosQuery)) { return result; } while(d->mSelectAllRadiosQuery.next()) { const auto ¤tRecord = d->mSelectAllRadiosQuery.record(); auto newData = buildRadioDataFromDatabaseRecord(currentRecord); result.push_back(newData); } d->mSelectAllRadiosQuery.finish(); return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::internalRecentlyPlayedTracksData(int count) { auto result = ListTrackDataType{}; d->mSelectAllRecentlyPlayedTracksQuery.bindValue(QStringLiteral(":maximumResults"), count); if (!internalGenericPartialData(d->mSelectAllRecentlyPlayedTracksQuery)) { return result; } while(d->mSelectAllRecentlyPlayedTracksQuery.next()) { const auto ¤tRecord = d->mSelectAllRecentlyPlayedTracksQuery.record(); auto newData = buildTrackDataFromDatabaseRecord(currentRecord); result.push_back(newData); } d->mSelectAllRecentlyPlayedTracksQuery.finish(); return result; } DatabaseInterface::ListTrackDataType DatabaseInterface::internalFrequentlyPlayedTracksData(int count) { auto result = ListTrackDataType{}; d->mSelectAllFrequentlyPlayedTracksQuery.bindValue(QStringLiteral(":maximumResults"), count); if (!internalGenericPartialData(d->mSelectAllFrequentlyPlayedTracksQuery)) { return result; } while(d->mSelectAllFrequentlyPlayedTracksQuery.next()) { const auto ¤tRecord = d->mSelectAllFrequentlyPlayedTracksQuery.record(); auto newData = buildTrackDataFromDatabaseRecord(currentRecord); result.push_back(newData); } d->mSelectAllFrequentlyPlayedTracksQuery.finish(); return result; } DatabaseInterface::TrackDataType DatabaseInterface::internalOneTrackPartialData(qulonglong databaseId) { auto result = TrackDataType{}; d->mSelectTrackFromIdQuery.bindValue(QStringLiteral(":trackId"), databaseId); if (!internalGenericPartialData(d->mSelectTrackFromIdQuery)) { return result; } if (d->mSelectTrackFromIdQuery.next()) { const auto ¤tRecord = d->mSelectTrackFromIdQuery.record(); result = buildTrackDataFromDatabaseRecord(currentRecord); } d->mSelectTrackFromIdQuery.finish(); return result; } DatabaseInterface::TrackDataType DatabaseInterface::internalOneRadioPartialData(qulonglong databaseId) { auto result = TrackDataType{}; d->mSelectRadioFromIdQuery.bindValue(QStringLiteral(":radioId"), databaseId); if (!internalGenericPartialData(d->mSelectRadioFromIdQuery)) { return result; } if (d->mSelectRadioFromIdQuery.next()) { const auto ¤tRecord = d->mSelectRadioFromIdQuery.record(); result = buildRadioDataFromDatabaseRecord(currentRecord); } d->mSelectRadioFromIdQuery.finish(); return result; } DatabaseInterface::ListGenreDataType DatabaseInterface::internalAllGenresPartialData() { ListGenreDataType result; if (!internalGenericPartialData(d->mSelectAllGenresQuery)) { return result; } while(d->mSelectAllGenresQuery.next()) { auto newData = GenreDataType{}; const auto ¤tRecord = d->mSelectAllGenresQuery.record(); newData[DataType::key_type::DatabaseIdRole] = currentRecord.value(0); newData[DataType::key_type::TitleRole] = currentRecord.value(1); newData[DataType::key_type::ElementTypeRole] = ElisaUtils::Genre; result.push_back(newData); } d->mSelectAllGenresQuery.finish(); return result; } DatabaseInterface::ListArtistDataType DatabaseInterface::internalAllComposersPartialData() { ListArtistDataType result; if (!internalGenericPartialData(d->mSelectAllComposersQuery)) { return result; } while(d->mSelectAllComposersQuery.next()) { auto newData = ArtistDataType{}; const auto ¤tRecord = d->mSelectAllComposersQuery.record(); newData[DataType::key_type::DatabaseIdRole] = currentRecord.value(0); newData[DataType::key_type::TitleRole] = currentRecord.value(1); newData[DataType::key_type::ElementTypeRole] = ElisaUtils::Composer; result.push_back(newData); } d->mSelectAllComposersQuery.finish(); return result; } DatabaseInterface::ListArtistDataType DatabaseInterface::internalAllLyricistsPartialData() { ListArtistDataType result; if (!internalGenericPartialData(d->mSelectAllLyricistsQuery)) { return result; } while(d->mSelectAllLyricistsQuery.next()) { auto newData = ArtistDataType{}; const auto ¤tRecord = d->mSelectAllLyricistsQuery.record(); newData[DataType::key_type::DatabaseIdRole] = currentRecord.value(0); newData[DataType::key_type::TitleRole] = currentRecord.value(1); newData[DataType::key_type::ElementTypeRole] = ElisaUtils::Lyricist; result.push_back(newData); } d->mSelectAllLyricistsQuery.finish(); return result; } bool DatabaseInterface::prepareQuery(QSqlQuery &query, const QString &queryText) const { query.setForwardOnly(true); return query.prepare(queryText); } bool DatabaseInterface::execQuery(QSqlQuery &query) { #if !defined NDEBUG auto timer = QElapsedTimer{}; timer.start(); #endif auto result = query.exec(); #if !defined NDEBUG if (timer.nsecsElapsed() > 10000000) { qCDebug(orgKdeElisaDatabase) << "[[" << timer.nsecsElapsed() << "]]" << query.lastQuery(); } #endif return result; } void DatabaseInterface::updateAlbumArtist(qulonglong albumId, const QString &title, const QString &albumPath, const QString &artistName) { d->mUpdateAlbumArtistQuery.bindValue(QStringLiteral(":albumId"), albumId); insertArtist(artistName); d->mUpdateAlbumArtistQuery.bindValue(QStringLiteral(":artistName"), artistName); auto queryResult = execQuery(d->mUpdateAlbumArtistQuery); if (!queryResult || !d->mUpdateAlbumArtistQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumArtist" << d->mUpdateAlbumArtistQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumArtist" << d->mUpdateAlbumArtistQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumArtist" << d->mUpdateAlbumArtistQuery.lastError(); d->mUpdateAlbumArtistQuery.finish(); return; } d->mUpdateAlbumArtistQuery.finish(); d->mUpdateAlbumArtistInTracksQuery.bindValue(QStringLiteral(":albumTitle"), title); d->mUpdateAlbumArtistInTracksQuery.bindValue(QStringLiteral(":albumPath"), albumPath); d->mUpdateAlbumArtistInTracksQuery.bindValue(QStringLiteral(":artistName"), artistName); queryResult = execQuery(d->mUpdateAlbumArtistInTracksQuery); if (!queryResult || !d->mUpdateAlbumArtistInTracksQuery.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumArtist" << d->mUpdateAlbumArtistInTracksQuery.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumArtist" << d->mUpdateAlbumArtistInTracksQuery.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateAlbumArtist" << d->mUpdateAlbumArtistInTracksQuery.lastError(); d->mUpdateAlbumArtistInTracksQuery.finish(); return; } d->mUpdateAlbumArtistInTracksQuery.finish(); } void DatabaseInterface::updateTrackStatistics(const QUrl &fileName, const QDateTime &time) { d->mUpdateTrackStatistics.bindValue(QStringLiteral(":fileName"), fileName); d->mUpdateTrackStatistics.bindValue(QStringLiteral(":playDate"), time.toMSecsSinceEpoch()); auto queryResult = execQuery(d->mUpdateTrackStatistics); if (!queryResult || !d->mUpdateTrackStatistics.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackStatistics" << d->mUpdateTrackStatistics.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackStatistics" << d->mUpdateTrackStatistics.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackStatistics" << d->mUpdateTrackStatistics.lastError(); d->mUpdateTrackStatistics.finish(); return; } d->mUpdateTrackStatistics.finish(); d->mUpdateTrackFirstPlayStatistics.bindValue(QStringLiteral(":fileName"), fileName); d->mUpdateTrackFirstPlayStatistics.bindValue(QStringLiteral(":playDate"), time.toMSecsSinceEpoch()); queryResult = execQuery(d->mUpdateTrackFirstPlayStatistics); if (!queryResult || !d->mUpdateTrackFirstPlayStatistics.isActive()) { Q_EMIT databaseError(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackStatistics" << d->mUpdateTrackFirstPlayStatistics.lastQuery(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackStatistics" << d->mUpdateTrackFirstPlayStatistics.boundValues(); qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackStatistics" << d->mUpdateTrackFirstPlayStatistics.lastError(); d->mUpdateTrackFirstPlayStatistics.finish(); return; } d->mUpdateTrackFirstPlayStatistics.finish(); } #include "moc_databaseinterface.cpp" diff --git a/src/elisautils.h b/src/elisautils.h index 26dd08e2..6b2b7666 100644 --- a/src/elisautils.h +++ b/src/elisautils.h @@ -1,81 +1,81 @@ /* * Copyright 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 ELISAUTILS_H #define ELISAUTILS_H #include "elisaLib_export.h" #include #include #include #include namespace ElisaUtils { Q_NAMESPACE enum PlayListEnqueueMode { AppendPlayList, ReplacePlayList, }; Q_ENUM_NS(PlayListEnqueueMode) enum PlayListEnqueueTriggerPlay { DoNotTriggerPlay, TriggerPlay, }; Q_ENUM_NS(PlayListEnqueueTriggerPlay) using EntryData = std::tuple; using EntryDataList = QList; enum PlayListEntryType { Album, Artist, Genre, Lyricist, Composer, Track, FileName, - Unknown, Radio, + Unknown, }; Q_ENUM_NS(PlayListEntryType) enum FilterType { UnknownFilter, NoFilter, FilterById, FilterByGenre, FilterByArtist, FilterByGenreAndArtist, FilterByRecentlyPlayed, FilterByFrequentlyPlayed, }; Q_ENUM_NS(FilterType) } Q_DECLARE_METATYPE(ElisaUtils::EntryData) Q_DECLARE_METATYPE(ElisaUtils::EntryDataList) #endif // ELISAUTILS_H diff --git a/src/mediaplaylist.cpp b/src/mediaplaylist.cpp index 999393d5..471abe8b 100644 --- a/src/mediaplaylist.cpp +++ b/src/mediaplaylist.cpp @@ -1,1387 +1,1388 @@ /* * 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 "mediaplaylist.h" #include "databaseinterface.h" #include "musicaudiotrack.h" #include "musiclistenersmanager.h" #include #include #include #include #include #include #include #include #include #include class MediaPlayListPrivate { public: QList mData; QList mTrackData; MusicListenersManager* mMusicListenersManager = nullptr; QPersistentModelIndex mPreviousTrack; QPersistentModelIndex mCurrentTrack; QPersistentModelIndex mNextTrack; QVariantMap mPersistentState; QMediaPlaylist mLoadPlaylist; int mCurrentPlayListPosition = 0; bool mRandomPlay = false; bool mRepeatPlay = false; bool mForceUndo = false; QList mRandomPositions = {0, 0, 0}; }; MediaPlayList::MediaPlayList(QObject *parent) : QAbstractListModel(parent), d(new MediaPlayListPrivate), dOld(new MediaPlayListPrivate) { connect(&d->mLoadPlaylist, &QMediaPlaylist::loaded, this, &MediaPlayList::loadPlayListLoaded); connect(&d->mLoadPlaylist, &QMediaPlaylist::loadFailed, this, &MediaPlayList::loadPlayListLoadFailed); } MediaPlayList::~MediaPlayList() = default; int MediaPlayList::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return d->mData.size(); } QHash MediaPlayList::roleNames() const { auto roles = QAbstractItemModel::roleNames(); roles[static_cast(ColumnsRoles::IsValidRole)] = "isValid"; roles[static_cast(ColumnsRoles::DatabaseIdRole)] = "databaseId"; roles[static_cast(ColumnsRoles::TitleRole)] = "title"; roles[static_cast(ColumnsRoles::StringDurationRole)] = "duration"; roles[static_cast(ColumnsRoles::ArtistRole)] = "artist"; roles[static_cast(ColumnsRoles::AlbumArtistRole)] = "albumArtist"; 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::GenreRole)] = "genre"; roles[static_cast(ColumnsRoles::LyricistRole)] = "lyricist"; roles[static_cast(ColumnsRoles::ComposerRole)] = "composer"; roles[static_cast(ColumnsRoles::CommentRole)] = "comment"; roles[static_cast(ColumnsRoles::YearRole)] = "year"; roles[static_cast(ColumnsRoles::ChannelsRole)] = "channels"; roles[static_cast(ColumnsRoles::BitRateRole)] = "bitRate"; roles[static_cast(ColumnsRoles::SampleRateRole)] = "sampleRate"; roles[static_cast(ColumnsRoles::CountRole)] = "count"; roles[static_cast(ColumnsRoles::IsPlayingRole)] = "isPlaying"; roles[static_cast(ColumnsRoles::IsSingleDiscAlbumRole)] = "isSingleDiscAlbum"; roles[static_cast(ColumnsRoles::SecondaryTextRole)] = "secondaryText"; roles[static_cast(ColumnsRoles::ImageUrlRole)] = "imageUrl"; roles[static_cast(ColumnsRoles::ShadowForImageRole)] = "shadowForImage"; roles[static_cast(ColumnsRoles::ResourceRole)] = "trackResource"; roles[static_cast(ColumnsRoles::TrackDataRole)] = "trackData"; roles[static_cast(ColumnsRoles::AlbumIdRole)] = "albumId"; roles[static_cast(ColumnsRoles::AlbumSectionRole)] = "albumSection"; + roles[static_cast(ColumnsRoles::ElementTypeRole)] = "entryType"; return roles; } QVariant MediaPlayList::data(const QModelIndex &index, int role) const { auto result = QVariant(); if (!index.isValid()) { return result; } if (d->mData[index.row()].mIsValid) { switch(role) { case ColumnsRoles::IsValidRole: result = d->mData[index.row()].mIsValid; break; case ColumnsRoles::IsPlayingRole: result = d->mData[index.row()].mIsPlaying; break; case ColumnsRoles::StringDurationRole: { QTime trackDuration = d->mTrackData[index.row()][TrackDataType::key_type::DurationRole].toTime(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(); } break; } case ColumnsRoles::AlbumSectionRole: result = QJsonDocument{QJsonArray{d->mTrackData[index.row()][TrackDataType::key_type::AlbumRole].toString(), d->mTrackData[index.row()][TrackDataType::key_type::AlbumArtistRole].toString(), d->mTrackData[index.row()][TrackDataType::key_type::ImageUrlRole].toUrl().toString()}}.toJson(); break; default: 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) { case ColumnsRoles::IsValidRole: result = d->mData[index.row()].mIsValid; break; case ColumnsRoles::TitleRole: result = d->mData[index.row()].mTitle; break; case ColumnsRoles::IsPlayingRole: result = d->mData[index.row()].mIsPlaying; break; case ColumnsRoles::ArtistRole: result = d->mData[index.row()].mArtist; break; case ColumnsRoles::AlbumArtistRole: result = d->mData[index.row()].mArtist; break; case ColumnsRoles::AlbumRole: result = d->mData[index.row()].mAlbum; break; case ColumnsRoles::TrackNumberRole: result = -1; break; case ColumnsRoles::IsSingleDiscAlbumRole: result = false; break; case Qt::DisplayRole: result = d->mData[index.row()].mTitle; break; case ColumnsRoles::ImageUrlRole: result = QUrl(QStringLiteral("image://icon/error")); break; case ColumnsRoles::ShadowForImageRole: result = false; break; case ColumnsRoles::AlbumSectionRole: result = QJsonDocument{QJsonArray{d->mData[index.row()].mAlbum.toString(), d->mData[index.row()].mArtist.toString(), QUrl(QStringLiteral("image://icon/error")).toString()}}.toJson(); break; default: result = {}; } } return result; } bool MediaPlayList::setData(const QModelIndex &index, const QVariant &value, int role) { bool modelModified = false; if (!index.isValid()) { return modelModified; } if (index.row() < 0 || index.row() >= d->mData.size()) { return modelModified; } if (role < ColumnsRoles::IsValidRole || role > ColumnsRoles::IsPlayingRole) { return modelModified; } auto convertedRole = static_cast(role); switch(convertedRole) { case ColumnsRoles::IsPlayingRole: { modelModified = true; auto newState = static_cast(value.toInt()); d->mData[index.row()].mIsPlaying = newState; Q_EMIT dataChanged(index, index, {role}); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } break; } case ColumnsRoles::TitleRole: { modelModified = true; d->mData[index.row()].mTitle = value; d->mTrackData[index.row()][static_cast(role)] = value; Q_EMIT dataChanged(index, index, {role}); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } break; } case ColumnsRoles::ArtistRole: { modelModified = true; d->mData[index.row()].mArtist = value; d->mTrackData[index.row()][static_cast(role)] = value; Q_EMIT dataChanged(index, index, {role}); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } break; } default: modelModified = false; } return modelModified; } bool MediaPlayList::removeRows(int row, int count, const QModelIndex &parent) { beginRemoveRows(parent, row, row + count - 1); for (int i = row, cpt = 0; cpt < count; ++i, ++cpt) { d->mData.removeAt(i); d->mTrackData.removeAt(i); } endRemoveRows(); if (!d->mCurrentTrack.isValid()) { d->mCurrentTrack = index(d->mCurrentPlayListPosition, 0); if (d->mCurrentTrack.isValid()) { notifyCurrentTrackChanged(); } if (!d->mCurrentTrack.isValid()) { Q_EMIT playListFinished(); resetCurrentTrack(); if (!d->mCurrentTrack.isValid()) { notifyCurrentTrackChanged(); } } } if (!d->mNextTrack.isValid() || !d->mPreviousTrack.isValid()) { notifyPreviousAndNextTracks(); } if (!d->mCurrentTrack.isValid() && rowCount(parent) <= row) { resetCurrentTrack(); } if (d->mRandomPlay) { createRandomList(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); return false; } bool MediaPlayList::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) { if (sourceParent != destinationParent) { return false; } if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild)) { return false; } for (auto cptItem = 0; cptItem < count; ++cptItem) { if (sourceRow < destinationChild) { d->mData.move(sourceRow, destinationChild - 1); d->mTrackData.move(sourceRow, destinationChild - 1); } else { d->mData.move(sourceRow, destinationChild); d->mTrackData.move(sourceRow, destinationChild); } } endMoveRows(); Q_EMIT persistentStateChanged(); return true; } void MediaPlayList::move(int from, int to, int n) { if (from < to) { moveRows({}, from, n, {}, to + 1); } else { moveRows({}, from, n, {}, to); } } void MediaPlayList::enqueueRestoredEntry(const MediaPlayListEntry &newEntry) { enqueueCommon(); beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size()); d->mData.push_back(newEntry); d->mTrackData.push_back({}); endInsertRows(); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); if (!newEntry.mIsValid) { if (newEntry.mEntryType == ElisaUtils::Radio) { Q_EMIT newEntryInList(newEntry.mId, {}, ElisaUtils::Radio); } else if (newEntry.mTrackUrl.isValid()) { auto entryURL = newEntry.mTrackUrl.toUrl(); if (entryURL.isLocalFile()) { auto entryString = entryURL.toLocalFile(); QFileInfo newTrackFile(entryString); if (newTrackFile.exists()) { d->mData.last().mIsValid = true; } Q_EMIT newEntryInList(0, entryString, ElisaUtils::FileName); } } else { Q_EMIT newTrackByNameInList(newEntry.mTitle, newEntry.mArtist, newEntry.mAlbum, newEntry.mTrackNumber, newEntry.mDiscNumber); } } else { Q_EMIT newEntryInList(newEntry.mId, {}, ElisaUtils::Track); } if (!newEntry.mIsValid) { Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0), {MediaPlayList::IsPlayingRole}); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } } } void MediaPlayList::enqueueArtist(const QString &artistName) { enqueueCommon(); auto newEntry = MediaPlayListEntry{artistName}; newEntry.mEntryType = ElisaUtils::Artist; beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size()); d->mData.push_back(newEntry); d->mTrackData.push_back({}); endInsertRows(); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT newEntryInList(0, artistName, newEntry.mEntryType); Q_EMIT persistentStateChanged(); } void MediaPlayList::enqueueFilesList(const ElisaUtils::EntryDataList &newEntries) { enqueueCommon(); beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size() + newEntries.size() - 1); for (const auto &oneTrackUrl : newEntries) { auto newEntry = MediaPlayListEntry(QUrl::fromLocalFile(std::get<1>(oneTrackUrl))); newEntry.mEntryType = ElisaUtils::FileName; d->mData.push_back(newEntry); d->mTrackData.push_back({}); if (newEntry.mTrackUrl.isValid()) { auto entryURL = newEntry.mTrackUrl.toUrl(); if (entryURL.isLocalFile()) { auto entryString = entryURL.toLocalFile(); QFileInfo newTrackFile(entryString); if (newTrackFile.exists()) { d->mData.last().mIsValid = true; } Q_EMIT newEntryInList(0, entryString, newEntry.mEntryType); } } } endInsertRows(); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0), {MediaPlayList::IsPlayingRole}); } void MediaPlayList::enqueueTracksListById(const ElisaUtils::EntryDataList &newEntries, ElisaUtils::PlayListEntryType type) { enqueueCommon(); beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size() + newEntries.size() - 1); for (const auto &newTrack : newEntries) { auto newMediaPlayListEntry = MediaPlayListEntry{std::get<0>(newTrack), std::get<1>(newTrack), type}; d->mData.push_back(newMediaPlayListEntry); d->mTrackData.push_back({}); Q_EMIT newEntryInList(newMediaPlayListEntry.mId, newMediaPlayListEntry.mTitle.toString(), newMediaPlayListEntry.mEntryType); } endInsertRows(); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0), {MediaPlayList::IsPlayingRole}); } void MediaPlayList::enqueueOneEntry(const ElisaUtils::EntryData &entryData, ElisaUtils::PlayListEntryType type) { enqueueCommon(); beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size()); d->mData.push_back(MediaPlayListEntry{std::get<0>(entryData), std::get<1>(entryData), type}); d->mTrackData.push_back({}); Q_EMIT newEntryInList(std::get<0>(entryData), std::get<1>(entryData), type); endInsertRows(); Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); } void MediaPlayList::enqueueMultipleEntries(const ElisaUtils::EntryDataList &entriesData, ElisaUtils::PlayListEntryType type) { enqueueCommon(); beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size() + entriesData.size() - 1); for (const auto &entryData : entriesData) { d->mData.push_back(MediaPlayListEntry{std::get<0>(entryData), std::get<1>(entryData), type}); d->mTrackData.push_back({}); Q_EMIT newEntryInList(std::get<0>(entryData), std::get<1>(entryData), type); } endInsertRows(); Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); } void MediaPlayList::replaceAndPlay(const ElisaUtils::EntryData &newEntry, ElisaUtils::PlayListEntryType databaseIdType) { enqueue(newEntry, databaseIdType, ElisaUtils::PlayListEnqueueMode::ReplacePlayList, ElisaUtils::PlayListEnqueueTriggerPlay::TriggerPlay); } void MediaPlayList::clearPlayList(bool prepareUndo) { if (d->mData.isEmpty()) { return; } if(prepareUndo){ Q_EMIT clearPlayListPlayer(); this->copyD(); } beginRemoveRows({}, 0, d->mData.count() - 1); d->mData.clear(); d->mTrackData.clear(); endRemoveRows(); d->mCurrentPlayListPosition = 0; d->mCurrentTrack = QPersistentModelIndex{}; notifyCurrentTrackChanged(); displayOrHideUndoInline(true); Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); } void MediaPlayList::clearPlayList() { this->clearPlayList(true); } void MediaPlayList::undoClearPlayList() { clearPlayList(false); beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size() + dOld->mData.size() - 1); for (auto &newTrack : dOld->mData) { d->mData.push_back(newTrack); d->mTrackData.push_back({}); if (ElisaUtils::FileName == newTrack.mEntryType && newTrack.mTrackUrl.isValid()) { auto entryURL = newTrack.mTrackUrl.toUrl(); if (entryURL.isLocalFile()) { auto entryString = entryURL.toLocalFile(); QFileInfo newTrackFile(entryString); if (newTrackFile.exists()) { d->mData.last().mIsValid = true; } Q_EMIT newEntryInList(0, entryString, newTrack.mEntryType); } } else if(ElisaUtils::Artist == newTrack.mEntryType){ Q_EMIT newEntryInList(0, newTrack.mArtist.toString(), newTrack.mEntryType); } else{ Q_EMIT newEntryInList(newTrack.mId, newTrack.mTitle.toString(), newTrack.mEntryType); } } endInsertRows(); d->mMusicListenersManager = dOld->mMusicListenersManager; d->mPersistentState = dOld->mPersistentState; d->mCurrentPlayListPosition = dOld->mCurrentPlayListPosition; d->mRandomPlay = dOld->mRandomPlay; d->mRepeatPlay = dOld->mRepeatPlay; auto candidateTrack = index(dOld->mCurrentPlayListPosition, 0); if (candidateTrack.isValid() && candidateTrack.data(ColumnsRoles::IsValidRole).toBool()) { d->mCurrentTrack = candidateTrack; notifyCurrentTrackChanged(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); Q_EMIT persistentStateChanged(); Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0), {MediaPlayList::IsPlayingRole}); displayOrHideUndoInline(false); Q_EMIT undoClearPlayListPlayer(); } void MediaPlayList::updateRadioData(const QVariant &value, int role) { auto convertedRole = static_cast(role); if (d->mCurrentTrack.data(convertedRole) != value) { this->setData(d->mCurrentTrack, value, role); } } void MediaPlayList::enqueueCommon() { displayOrHideUndoInline(false); } void MediaPlayList::copyD() { dOld->mData = d->mData; dOld->mTrackData = d->mTrackData; dOld->mMusicListenersManager = d->mMusicListenersManager; dOld->mCurrentTrack = d->mCurrentTrack; dOld->mPersistentState = d->mPersistentState; dOld->mCurrentPlayListPosition = d->mCurrentPlayListPosition; dOld->mRandomPlay = d->mRandomPlay; dOld->mRepeatPlay = d->mRepeatPlay; } void MediaPlayList::loadPlaylist(const QString &localFileName) { d->mLoadPlaylist.clear(); d->mLoadPlaylist.load(QUrl::fromLocalFile(localFileName), "m3u"); } void MediaPlayList::loadPlaylist(const QUrl &fileName) { d->mLoadPlaylist.clear(); d->mLoadPlaylist.load(fileName, "m3u"); } void MediaPlayList::enqueue(const ElisaUtils::EntryData &newEntry, ElisaUtils::PlayListEntryType databaseIdType) { enqueue(newEntry, databaseIdType, ElisaUtils::PlayListEnqueueMode::AppendPlayList, ElisaUtils::PlayListEnqueueTriggerPlay::DoNotTriggerPlay); } void MediaPlayList::enqueue(const ElisaUtils::EntryDataList &newEntries, ElisaUtils::PlayListEntryType databaseIdType) { enqueue(newEntries, databaseIdType, ElisaUtils::PlayListEnqueueMode::AppendPlayList, ElisaUtils::PlayListEnqueueTriggerPlay::DoNotTriggerPlay); } void MediaPlayList::enqueue(qulonglong newEntryDatabaseId, const QString &newEntryTitle, ElisaUtils::PlayListEntryType databaseIdType, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay) { enqueue(ElisaUtils::EntryData{newEntryDatabaseId, newEntryTitle}, databaseIdType, enqueueMode, triggerPlay); } void MediaPlayList::enqueue(const ElisaUtils::EntryData &newEntry, ElisaUtils::PlayListEntryType databaseIdType, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay) { if (enqueueMode == ElisaUtils::ReplacePlayList) { if(d->mData.size()>0){ d->mForceUndo = true; } clearPlayList(); } enqueueCommon(); switch (databaseIdType) { case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Genre: case ElisaUtils::Track: case ElisaUtils::Radio: enqueueOneEntry(newEntry, databaseIdType); break; case ElisaUtils::FileName: enqueueFilesList({newEntry}); break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::Unknown: break; } if (triggerPlay == ElisaUtils::TriggerPlay) { Q_EMIT ensurePlay(); } if (enqueueMode == ElisaUtils::ReplacePlayList) { d->mForceUndo = false; } } void MediaPlayList::enqueue(const ElisaUtils::EntryDataList &newEntries, ElisaUtils::PlayListEntryType databaseIdType, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay) { if (newEntries.isEmpty()) { return; } if (enqueueMode == ElisaUtils::ReplacePlayList) { if(d->mData.size()>0){ d->mForceUndo = true; } clearPlayList(); } enqueueCommon(); switch (databaseIdType) { case ElisaUtils::Track: case ElisaUtils::Radio: enqueueTracksListById(newEntries, databaseIdType); break; case ElisaUtils::FileName: enqueueFilesList(newEntries); break; case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Genre: enqueueMultipleEntries(newEntries, databaseIdType); break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::Unknown: break; } if (triggerPlay == ElisaUtils::TriggerPlay) { Q_EMIT ensurePlay(); } if (enqueueMode == ElisaUtils::ReplacePlayList) { d->mForceUndo = false; } } bool MediaPlayList::savePlaylist(const QUrl &fileName) { QMediaPlaylist savePlaylist; for (int i = 0; i < d->mData.size(); ++i) { const auto &oneTrack = d->mData.at(i); const auto &oneTrackData = d->mTrackData.at(i); if (oneTrack.mIsValid) { savePlaylist.addMedia(oneTrackData.resourceURI()); } } return savePlaylist.save(fileName, "m3u"); } QVariantMap MediaPlayList::persistentState() const { auto currentState = QVariantMap(); auto result = QList(); for (int trackIndex = 0; trackIndex < d->mData.size(); ++trackIndex) { auto oneData = QList(); const auto &oneEntry = d->mData[trackIndex]; if (oneEntry.mIsValid) { const auto &oneTrack = d->mTrackData[trackIndex]; oneData.push_back(QString::number(oneTrack.databaseId())); oneData.push_back(oneTrack.title()); oneData.push_back(oneTrack.artist()); 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)); } } currentState[QStringLiteral("playList")] = result; currentState[QStringLiteral("currentTrack")] = d->mCurrentPlayListPosition; currentState[QStringLiteral("randomPlay")] = d->mRandomPlay; currentState[QStringLiteral("repeatPlay")] = d->mRepeatPlay; return currentState; } MusicListenersManager *MediaPlayList::musicListenersManager() const { return d->mMusicListenersManager; } int MediaPlayList::tracksCount() const { return rowCount(); } QPersistentModelIndex MediaPlayList::previousTrack() const { return d->mPreviousTrack; } QPersistentModelIndex MediaPlayList::currentTrack() const { return d->mCurrentTrack; } QPersistentModelIndex MediaPlayList::nextTrack() const { return d->mNextTrack; } int MediaPlayList::currentTrackRow() const { return d->mCurrentTrack.row(); } bool MediaPlayList::randomPlay() const { return d->mRandomPlay; } bool MediaPlayList::repeatPlay() const { return d->mRepeatPlay; } void MediaPlayList::setPersistentState(const QVariantMap &persistentStateValue) { if (d->mPersistentState == persistentStateValue) { return; } qDebug() << "MediaPlayList::setPersistentState" << persistentStateValue; d->mPersistentState = persistentStateValue; auto persistentState = d->mPersistentState[QStringLiteral("playList")].toList(); for (auto &oneData : persistentState) { auto trackData = oneData.toStringList(); if (trackData.size() != 7) { continue; } auto restoredId = trackData[0].toULongLong(); auto restoredTitle = trackData[1]; auto restoredArtist = trackData[2]; auto restoredAlbum = trackData[3]; auto restoredTrackNumber = trackData[4]; auto restoredDiscNumber = trackData[5]; auto mEntryType = static_cast(trackData[6].toInt()); enqueueRestoredEntry({restoredId, restoredTitle, restoredArtist, restoredAlbum, restoredTrackNumber, restoredDiscNumber, mEntryType}); } restorePlayListPosition(); restoreRandomPlay(); restoreRepeatPlay(); Q_EMIT persistentStateChanged(); } void MediaPlayList::removeSelection(QList selection) { std::sort(selection.begin(), selection.end()); std::reverse(selection.begin(), selection.end()); for (auto oneItem : selection) { removeRow(oneItem); } if (d->mRandomPlay) { createRandomList(); } } void MediaPlayList::tracksListAdded(qulonglong newDatabaseId, const QString &entryTitle, ElisaUtils::PlayListEntryType databaseIdType, const ListTrackDataType &tracks) { for (int playListIndex = 0; playListIndex < d->mData.size(); ++playListIndex) { auto &oneEntry = d->mData[playListIndex]; if (oneEntry.mEntryType != databaseIdType) { continue; } if (oneEntry.mTitle != entryTitle) { continue; } if (newDatabaseId != 0 && oneEntry.mId != newDatabaseId) { continue; } d->mTrackData[playListIndex] = tracks.first(); oneEntry.mId = tracks.first().databaseId(); oneEntry.mIsValid = true; oneEntry.mEntryType = ElisaUtils::Track; Q_EMIT dataChanged(index(playListIndex, 0), index(playListIndex, 0), {}); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } if (tracks.size() > 1) { beginInsertRows(QModelIndex(), playListIndex + 1, playListIndex - 1 + tracks.size()); for (int trackIndex = 1; trackIndex < tracks.size(); ++trackIndex) { auto newEntry = MediaPlayListEntry{tracks[trackIndex]}; newEntry.mEntryType = ElisaUtils::Track; d->mData.push_back(newEntry); d->mTrackData.push_back(tracks[trackIndex]); } endInsertRows(); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } Q_EMIT tracksCountChanged(); Q_EMIT remainingTracksChanged(); } Q_EMIT persistentStateChanged(); } } void MediaPlayList::trackChanged(const TrackDataType &track) { for (int i = 0; i < d->mData.size(); ++i) { auto &oneEntry = d->mData[i]; if (oneEntry.mEntryType != ElisaUtils::Artist && oneEntry.mIsValid) { if (oneEntry.mTrackUrl.toUrl().isValid() && track.resourceURI() != oneEntry.mTrackUrl.toUrl()) { continue; } if (!oneEntry.mTrackUrl.toUrl().isValid() && (oneEntry.mId == 0 || track.databaseId() != oneEntry.mId)) { continue; } const auto &trackData = d->mTrackData[i]; if (!trackData.empty()) { bool sameData = true; for (auto oneKeyIterator = track.constKeyValueBegin(); oneKeyIterator != track.constKeyValueEnd(); ++oneKeyIterator) { if (trackData[(*oneKeyIterator).first] != (*oneKeyIterator).second) { sameData = false; break; } } if (sameData) { continue; } } d->mTrackData[i] = track; Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } continue; } else if (oneEntry.mEntryType == ElisaUtils::Radio ) { if (track.databaseId() != oneEntry.mId) { continue; } d->mTrackData[i] = track; oneEntry.mId = track.databaseId(); oneEntry.mIsValid = true; Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } break; } else if (oneEntry.mEntryType != ElisaUtils::Artist && !oneEntry.mIsValid && !oneEntry.mTrackUrl.isValid()) { if (track.find(TrackDataType::key_type::TitleRole) != track.end() && track.title() != oneEntry.mTitle) { continue; } if (track.find(TrackDataType::key_type::AlbumRole) != track.end() && track.album() != oneEntry.mAlbum) { continue; } if (track.find(TrackDataType::key_type::TrackNumberRole) != track.end() && track.trackNumber() != oneEntry.mTrackNumber) { continue; } if (track.find(TrackDataType::key_type::DiscNumberRole) != track.end() && track.discNumber() != oneEntry.mDiscNumber) { continue; } d->mTrackData[i] = track; oneEntry.mId = track.databaseId(); oneEntry.mIsValid = true; Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } else if (i == d->mCurrentTrack.row()) { notifyCurrentTrackChanged(); } else if (i == d->mNextTrack.row() || i == d->mPreviousTrack.row()) { notifyPreviousAndNextTracks(); } break; } else if (oneEntry.mEntryType != ElisaUtils::Artist && !oneEntry.mIsValid && oneEntry.mTrackUrl.isValid()) { if (track.resourceURI() != oneEntry.mTrackUrl) { continue; } d->mTrackData[i] = track; oneEntry.mId = track.databaseId(); oneEntry.mIsValid = true; Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); restorePlayListPosition(); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } else if (i == d->mCurrentTrack.row()) { notifyCurrentTrackChanged(); } else if (i == d->mNextTrack.row() || i == d->mPreviousTrack.row()) { notifyPreviousAndNextTracks(); } break; } } } void MediaPlayList::trackRemoved(qulonglong trackId) { for (int i = 0; i < d->mData.size(); ++i) { auto &oneEntry = d->mData[i]; if (oneEntry.mIsValid) { if (oneEntry.mId == trackId) { oneEntry.mIsValid = false; oneEntry.mTitle = d->mTrackData[i].title(); oneEntry.mArtist = d->mTrackData[i].artist(); oneEntry.mAlbum = d->mTrackData[i].album(); oneEntry.mTrackNumber = d->mTrackData[i].trackNumber(); oneEntry.mDiscNumber = d->mTrackData[i].discNumber(); Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); if (!d->mCurrentTrack.isValid()) { resetCurrentTrack(); } } } } } void MediaPlayList::setMusicListenersManager(MusicListenersManager *musicListenersManager) { if (d->mMusicListenersManager == musicListenersManager) { return; } d->mMusicListenersManager = musicListenersManager; if (d->mMusicListenersManager) { d->mMusicListenersManager->subscribeForTracks(this); } Q_EMIT musicListenersManagerChanged(); } void MediaPlayList::setRandomPlay(bool value) { if (d->mRandomPlay != value) { d->mRandomPlay = value; createRandomList(); Q_EMIT randomPlayChanged(); Q_EMIT remainingTracksChanged(); notifyPreviousAndNextTracks(); } } void MediaPlayList::setRepeatPlay(bool value) { if (d->mRepeatPlay != value) { d->mRepeatPlay = value; Q_EMIT repeatPlayChanged(); Q_EMIT remainingTracksChanged(); notifyPreviousAndNextTracks(); } } void MediaPlayList::displayOrHideUndoInline(bool value) { if(value){ Q_EMIT displayUndoInline(); }else { if(!d->mForceUndo){ Q_EMIT hideUndoInline(); } } } void MediaPlayList::skipNextTrack() { if (!d->mCurrentTrack.isValid()) { return; } if (d->mRandomPlay) { d->mRandomPositions.removeFirst(); d->mCurrentTrack = index(d->mRandomPositions.at(1), 0); d->mRandomPositions.append(QRandomGenerator::global()->bounded(rowCount())); } else { if (d->mCurrentTrack.row() >= rowCount() - 1) { d->mCurrentTrack = index(0, 0); if (!d->mRepeatPlay) { Q_EMIT playListFinished(); } } else { d->mCurrentTrack = index(d->mCurrentTrack.row() + 1, 0); } } notifyCurrentTrackChanged(); } void MediaPlayList::skipPreviousTrack() { if (!d->mCurrentTrack.isValid()) { return; } if (d->mRandomPlay) { d->mRandomPositions.removeLast(); d->mCurrentTrack = index(d->mRandomPositions.at(0), 0); d->mRandomPositions.prepend(QRandomGenerator::global()->bounded(rowCount())); } else { if (d->mCurrentTrack.row() == 0) { if (d->mRepeatPlay) { d->mCurrentTrack = index(rowCount() - 1, 0); } else { return; } } else { d->mCurrentTrack = index(d->mCurrentTrack.row() - 1, 0); } } notifyCurrentTrackChanged(); } void MediaPlayList::switchTo(int row) { if (!d->mCurrentTrack.isValid()) { return; } if (d->mRandomPlay) { d->mCurrentTrack = index(row, 0); d->mRandomPositions.replace(1, row); } else { d->mCurrentTrack = index(row, 0); } notifyCurrentTrackChanged(); } void MediaPlayList::trackInError(const QUrl &sourceInError, QMediaPlayer::Error playerError) { Q_UNUSED(playerError) for (int i = 0; i < d->mData.size(); ++i) { auto &oneTrack = d->mData[i]; if (oneTrack.mIsValid) { const auto &oneTrackData = d->mTrackData.at(i); if (oneTrackData.resourceURI() == sourceInError) { oneTrack.mIsValid = false; Q_EMIT dataChanged(index(i, 0), index(i, 0), {ColumnsRoles::IsValidRole}); } } } } void MediaPlayList::loadPlayListLoaded() { clearPlayList(); for (int i = 0; i < d->mLoadPlaylist.mediaCount(); ++i) { enqueue(ElisaUtils::EntryData{0, d->mLoadPlaylist.media(i).canonicalUrl().toLocalFile()}, ElisaUtils::FileName); } restorePlayListPosition(); restoreRandomPlay(); restoreRepeatPlay(); Q_EMIT persistentStateChanged(); d->mLoadPlaylist.clear(); Q_EMIT playListLoaded(); } void MediaPlayList::loadPlayListLoadFailed() { d->mLoadPlaylist.clear(); Q_EMIT playListLoadFailed(); } void MediaPlayList::resetCurrentTrack() { for(int row = 0; row < rowCount(); ++row) { auto candidateTrack = index(row, 0); if (candidateTrack.isValid() && candidateTrack.data(ColumnsRoles::IsValidRole).toBool()) { d->mCurrentTrack = candidateTrack; notifyCurrentTrackChanged(); break; } } } void MediaPlayList::notifyPreviousAndNextTracks() { if (!d->mCurrentTrack.isValid()) { d->mPreviousTrack = QPersistentModelIndex(); d->mNextTrack = QPersistentModelIndex(); } auto mOldPreviousTrack = d->mPreviousTrack; auto mOldNextTrack = d->mNextTrack; // use random list for previous and next types if (d->mRandomPlay) { d->mPreviousTrack = index(d->mRandomPositions.first(), 0); d->mNextTrack = index(d->mRandomPositions.last(), 0); } else if (d->mRepeatPlay) { // forward to end or begin when repeating if (d->mCurrentTrack.row() == 0) { d->mPreviousTrack = index(rowCount() - 1, 0); } else { d->mPreviousTrack = index(d->mCurrentTrack.row() - 1, 0); } if (d->mCurrentTrack.row() == rowCount() - 1) { d->mNextTrack = index(0, 0); } else { d->mNextTrack = index(d->mCurrentTrack.row() + 1, 0); } } else { // return nothing if no tracks available if (d->mCurrentTrack.row() == 0) { d->mPreviousTrack = QPersistentModelIndex(); } else { d->mPreviousTrack = index(d->mCurrentTrack.row() - 1, 0); } if (d->mCurrentTrack.row() == rowCount() - 1) { d->mNextTrack = QPersistentModelIndex(); } else { d->mNextTrack = index(d->mCurrentTrack.row() + 1, 0); } } if (d->mPreviousTrack != mOldPreviousTrack) { Q_EMIT previousTrackChanged(d->mPreviousTrack); } if (d->mNextTrack != mOldNextTrack) { Q_EMIT nextTrackChanged(d->mNextTrack); } } void MediaPlayList::notifyCurrentTrackChanged() { // determine previous and next tracks first notifyPreviousAndNextTracks(); Q_EMIT currentTrackChanged(d->mCurrentTrack); Q_EMIT currentTrackRowChanged(); Q_EMIT remainingTracksChanged(); bool currentTrackIsValid = d->mCurrentTrack.isValid(); if (currentTrackIsValid) { d->mCurrentPlayListPosition = d->mCurrentTrack.row(); } } void MediaPlayList::restorePlayListPosition() { auto playerCurrentTrack = d->mPersistentState.find(QStringLiteral("currentTrack")); if (playerCurrentTrack != d->mPersistentState.end()) { auto newIndex = index(playerCurrentTrack->toInt(), 0); if (newIndex.isValid() && (newIndex != d->mCurrentTrack)) { d->mCurrentTrack = newIndex; notifyCurrentTrackChanged(); if (d->mCurrentTrack.isValid()) { d->mPersistentState.erase(playerCurrentTrack); } } } } void MediaPlayList::restoreRandomPlay() { auto randomPlayStoredValue = d->mPersistentState.find(QStringLiteral("randomPlay")); if (randomPlayStoredValue != d->mPersistentState.end()) { setRandomPlay(randomPlayStoredValue->toBool()); } } void MediaPlayList::restoreRepeatPlay() { auto repeatPlayStoredValue = d->mPersistentState.find(QStringLiteral("repeatPlay")); if (repeatPlayStoredValue != d->mPersistentState.end()) { setRepeatPlay(repeatPlayStoredValue->toBool()); } } QDebug operator<<(const QDebug &stream, const MediaPlayListEntry &data) { stream << data.mTitle << data.mAlbum << data.mArtist << data.mTrackUrl << data.mTrackNumber << data.mDiscNumber << data.mId << data.mIsValid; return stream; } int MediaPlayList::remainingTracks() const { if (!d->mCurrentTrack.isValid()) { return -1; } if (d->mRandomPlay || d->mRepeatPlay) { return -1; } else { return rowCount() - d->mCurrentTrack.row() - 1; } } void MediaPlayList::createRandomList() { for (auto& position : d->mRandomPositions) { position = QRandomGenerator::global()->bounded(rowCount()); } if (d->mCurrentTrack.isValid()) { d->mRandomPositions.replace(1, d->mCurrentTrack.row()); } } #include "moc_mediaplaylist.cpp" diff --git a/src/modeldataloader.cpp b/src/modeldataloader.cpp index a3eb4c8f..93020cec 100644 --- a/src/modeldataloader.cpp +++ b/src/modeldataloader.cpp @@ -1,490 +1,435 @@ /* * 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" #include class ModelDataLoaderPrivate { public: DatabaseInterface *mDatabase = nullptr; QMimeDatabase mMimeDatabase; FileScanner mFileScanner; ElisaUtils::PlayListEntryType mModelType = ElisaUtils::Unknown; ModelDataLoader::FilterType mFilterType = ModelDataLoader::FilterType::UnknownFilter; QString mArtist; QString mAlbumTitle; QString mAlbumArtist; QString mGenre; qulonglong mDatabaseId = 0; }; 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::databaseGenresAdded); + this, &ModelDataLoader::genresAdded); connect(database, &DatabaseInterface::albumsAdded, this, &ModelDataLoader::databaseAlbumsAdded); connect(database, &DatabaseInterface::albumModified, - this, &ModelDataLoader::databaseAlbumModified); + this, &ModelDataLoader::albumModified); connect(database, &DatabaseInterface::albumRemoved, - this, &ModelDataLoader::databaseAlbumRemoved); + this, &ModelDataLoader::albumRemoved); connect(database, &DatabaseInterface::tracksAdded, this, &ModelDataLoader::databaseTracksAdded); connect(database, &DatabaseInterface::trackModified, - this, &ModelDataLoader::databaseTrackModified); + this, &ModelDataLoader::trackModified); connect(database, &DatabaseInterface::trackRemoved, - this, &ModelDataLoader::databaseTrackRemoved); + this, &ModelDataLoader::trackRemoved); connect(database, &DatabaseInterface::artistsAdded, this, &ModelDataLoader::databaseArtistsAdded); connect(database, &DatabaseInterface::artistRemoved, - this, &ModelDataLoader::databaseArtistRemoved); + this, &ModelDataLoader::artistRemoved); connect(this, &ModelDataLoader::saveRadioModified, database, &DatabaseInterface::insertRadio); connect(this, &ModelDataLoader::removeRadio, database, &DatabaseInterface::removeRadio); connect(database, &DatabaseInterface::radioAdded, - this, &ModelDataLoader::databaseRadioAdded); + this, &ModelDataLoader::radioAdded); connect(database, &DatabaseInterface::radioModified, - this, &ModelDataLoader::databaseRadioModified); + this, &ModelDataLoader::radioModified); connect(database, &DatabaseInterface::radioRemoved, - this, &ModelDataLoader::databaseRadioRemoved); + this, &ModelDataLoader::radioRemoved); } 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::loadDataByDatabaseId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::FilterById; d->mDatabaseId = databaseId; switch (dataType) { case ElisaUtils::Track: Q_EMIT allTrackData(d->mDatabase->trackDataFromDatabaseId(databaseId)); 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::loadDataByFileName(ElisaUtils::PlayListEntryType dataType, const QUrl &fileName) { if (!d->mDatabase) { return; } d->mFilterType = ModelDataLoader::FilterType::UnknownFilter; switch (dataType) { case ElisaUtils::FileName: { auto result = d->mFileScanner.scanOneFile(fileName, d->mMimeDatabase); Q_EMIT allTrackData(result.toTrackData()); 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::databaseTrackModified(const TrackDataType &modifiedTrack) -{ - Q_EMIT trackModified(modifiedTrack); -} - -void ModelDataLoader::databaseTrackRemoved(qulonglong removedTrackId) -{ - Q_EMIT trackRemoved(removedTrackId); -} - -void ModelDataLoader::databaseGenresAdded(const ListGenreDataType &newData) -{ - Q_EMIT genresAdded(newData); -} - 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::databaseArtistRemoved(qulonglong removedDatabaseId) -{ - Q_EMIT artistRemoved(removedDatabaseId); -} - 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; } } -void ModelDataLoader::databaseAlbumRemoved(qulonglong removedDatabaseId) -{ - Q_EMIT albumRemoved(removedDatabaseId); -} - -void ModelDataLoader::databaseAlbumModified(const AlbumDataType &modifiedAlbum) -{ - Q_EMIT albumModified(modifiedAlbum); -} - -void ModelDataLoader::updateRadioData(const TrackDataType &trackDataType) -{ - Q_EMIT saveRadioModified(trackDataType); -} - -void ModelDataLoader::deleteRadioData(qulonglong radioId) -{ - Q_EMIT removeRadio(radioId); -} - -void ModelDataLoader::databaseRadioAdded(TrackDataType radio) -{ - Q_EMIT radioAdded(radio); -} - -void ModelDataLoader::databaseRadioModified(TrackDataType radio) -{ - Q_EMIT radioModified(radio); -} - -void ModelDataLoader::databaseRadioRemoved(qulonglong radioId) -{ - Q_EMIT radioRemoved(radioId); -} - #include "moc_modeldataloader.cpp" diff --git a/src/modeldataloader.h b/src/modeldataloader.h index 033cd444..80757c24 100644 --- a/src/modeldataloader.h +++ b/src/modeldataloader.h @@ -1,161 +1,139 @@ /* * 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 "models/datamodel.h" #include #include class ModelDataLoaderPrivate; class ELISALIB_EXPORT ModelDataLoader : public QObject { Q_OBJECT public: using ListAlbumDataType = DatabaseInterface::ListAlbumDataType; using ListArtistDataType = DatabaseInterface::ListArtistDataType; using ListGenreDataType = DatabaseInterface::ListGenreDataType; using ListTrackDataType = DatabaseInterface::ListTrackDataType; using ListRadioDataType = DatabaseInterface::ListRadioDataType; using TrackDataType = DatabaseInterface::TrackDataType; using AlbumDataType = DatabaseInterface::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); 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 loadDataByDatabaseId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId); void loadDataByFileName(ElisaUtils::PlayListEntryType dataType, const QUrl &fileName); void loadRecentlyPlayedData(ElisaUtils::PlayListEntryType dataType); void loadFrequentlyPlayedData(ElisaUtils::PlayListEntryType dataType); - void updateRadioData(const ModelDataLoader::TrackDataType &trackDataType); - - void deleteRadioData(qulonglong radioId); - -private: +private Q_SLOTS: void databaseTracksAdded(const ListTrackDataType &newData); - void databaseTrackModified(const TrackDataType &modifiedTrack); - - void databaseTrackRemoved(qulonglong removedTrackId); - - void databaseGenresAdded(const ListGenreDataType &newData); - void databaseArtistsAdded(const ListArtistDataType &newData); - void databaseArtistRemoved(qulonglong removedDatabaseId); - void databaseAlbumsAdded(const ListAlbumDataType &newData); - void databaseAlbumRemoved(qulonglong removedDatabaseId); - - void databaseAlbumModified(const AlbumDataType &modifiedAlbum); - - void databaseRadioAdded(const TrackDataType radio); - - void databaseRadioModified(const TrackDataType radio); - - void databaseRadioRemoved(qulonglong radioId); - private: std::unique_ptr d; }; #endif // MODELDATALOADER_H diff --git a/src/models/trackmetadatamodel.cpp b/src/models/trackmetadatamodel.cpp index ab1fcceb..dbeab24f 100644 --- a/src/models/trackmetadatamodel.cpp +++ b/src/models/trackmetadatamodel.cpp @@ -1,701 +1,562 @@ /* * 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 #include - TrackMetadataModel::TrackMetadataModel(QObject *parent) : QAbstractListModel(parent) { connect(&mLyricsValueWatcher, &QFutureWatcher::finished, this, &TrackMetadataModel::lyricsValueIsReady); } TrackMetadataModel::~TrackMetadataModel() { if (mLyricsValueWatcher.isRunning() && !mLyricsValueWatcher.isFinished()) { mLyricsValueWatcher.waitForFinished(); } } int TrackMetadataModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return mTrackData.count(); } -QVariant TrackMetadataModel::dataGeneral(const QModelIndex &index, int role) const +QVariant TrackMetadataModel::data(const QModelIndex &index, int role) const { auto result = QVariant{}; const auto currentKey = mTrackKeys[index.row()]; switch (role) { case Qt::DisplayRole: switch (currentKey) { case DatabaseInterface::TrackNumberRole: { auto trackNumber = mTrackData.trackNumber(); if (trackNumber > 0) { result = trackNumber; } break; } case DatabaseInterface::DiscNumberRole: { auto discNumber = mTrackData.discNumber(); if (discNumber > 0) { result = discNumber; } break; } case DatabaseInterface::ChannelsRole: { auto channels = mTrackData.channels(); if (channels > 0) { result = channels; } break; } case DatabaseInterface::BitRateRole: { auto bitRate = mTrackData.bitRate(); if (bitRate > 0) { result = bitRate; } break; } case DatabaseInterface::SampleRateRole: { auto sampleRate = mTrackData.sampleRate(); if (sampleRate > 0) { result = sampleRate; } break; } default: result = mTrackData[currentKey]; break; } 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::LyricsRole: result = i18nc("Lyrics label for track metadata view", "Lyrics"); break; case DatabaseInterface::ResourceRole: result = i18nc("Radio HTTP address for radio metadata view", "Stream Http Address"); break; case DatabaseInterface::SecondaryTextRole: case DatabaseInterface::ImageUrlRole: case DatabaseInterface::ShadowForImageRole: case DatabaseInterface::ChildModelRole: case DatabaseInterface::StringDurationRole: case DatabaseInterface::MilliSecondsDurationRole: case DatabaseInterface::IsValidAlbumArtistRole: case DatabaseInterface::AllArtistsRole: case DatabaseInterface::HighestTrackRating: 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::ResourceRole: 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::LyricsRole: result = LongTextEntry; 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::IsValidAlbumArtistRole: case DatabaseInterface::AllArtistsRole: case DatabaseInterface::HighestTrackRating: 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; } -QVariant TrackMetadataModel::dataRadio(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::CommentRole: - result = i18nc("Comment label for track metadata view", "Comment"); - break; - case DatabaseInterface::ResourceRole: - result = i18nc("Radio HTTP address for radio metadata view", "Stream Http Address"); - break; - case DatabaseInterface::ChannelsRole: - case DatabaseInterface::BitRateRole: - case DatabaseInterface::SampleRateRole: - case DatabaseInterface::LastPlayDate: - case DatabaseInterface::PlayCounter: - case DatabaseInterface::LyricsRole: - case DatabaseInterface::YearRole: - case DatabaseInterface::ComposerRole: - case DatabaseInterface::ArtistRole: - case DatabaseInterface::AlbumRole: - case DatabaseInterface::AlbumArtistRole: - case DatabaseInterface::TrackNumberRole: - case DatabaseInterface::DiscNumberRole: - case DatabaseInterface::RatingRole: - case DatabaseInterface::GenreRole: - case DatabaseInterface::LyricistRole: - case DatabaseInterface::DurationRole: - 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::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: - case DatabaseInterface::IsValidAlbumArtistRole: - break; - } - break; - case ItemTypeRole: - switch (currentKey) - { - case DatabaseInterface::TitleRole: - result = TextEntry; - break; - case DatabaseInterface::ResourceRole: - result = TextEntry; - break; - case DatabaseInterface::CommentRole: - result = TextEntry; - break; - case DatabaseInterface::ArtistRole: - case DatabaseInterface::AlbumRole: - case DatabaseInterface::AlbumArtistRole: - case DatabaseInterface::TrackNumberRole: - case DatabaseInterface::DiscNumberRole: - case DatabaseInterface::RatingRole: - case DatabaseInterface::GenreRole: - case DatabaseInterface::LyricistRole: - case DatabaseInterface::ComposerRole: - case DatabaseInterface::YearRole: - case DatabaseInterface::LastPlayDate: - case DatabaseInterface::PlayCounter: - case DatabaseInterface::LyricsRole: - 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::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: - case DatabaseInterface::IsValidAlbumArtistRole: - break; - } - break; - } - - return result; -} - -QVariant TrackMetadataModel::data(const QModelIndex &index, int role) const -{ - if (this->mIsRadio) { - return dataRadio(index, role); - } else { - return dataGeneral(index, role); - } -} - bool TrackMetadataModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (data(index, role) != value) { mTrackData[mTrackKeys[index.row()]] = value; 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; } QUrl TrackMetadataModel::coverUrl() const { if (mCoverImage.isEmpty()) { return QUrl(QStringLiteral("image://icon/media-optical-audio")); } else { return mCoverImage; } } MusicListenersManager *TrackMetadataModel::manager() const { return mManager; } QString TrackMetadataModel::lyrics() const { return mFullData[TrackDataType::key_type::LyricsRole].toString(); } +qulonglong TrackMetadataModel::databaseId() const +{ + return mDatabaseId; +} + void TrackMetadataModel::trackData(const TrackMetadataModel::TrackDataType &trackData) { if (!mFullData.isEmpty() && trackData.databaseId() != mFullData.databaseId()) { return; } - fillDataFromTrackData(trackData); + const QList fieldsForTrack({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, + DatabaseInterface::DatabaseIdRole}); + + fillDataFromTrackData(trackData, fieldsForTrack); } -void TrackMetadataModel::fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData) +void TrackMetadataModel::fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData, + const QList &fieldsForTrack) { - const QList mFieldsForClassicTrack({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}); - - const QList mFieldsForRadioTrack({DatabaseInterface::TitleRole,DatabaseInterface::ResourceRole, DatabaseInterface::CommentRole, DatabaseInterface::DatabaseIdRole, - DatabaseInterface::ArtistRole, DatabaseInterface::AlbumRole}); - beginResetModel(); mFullData = trackData; mTrackData.clear(); mTrackKeys.clear(); - for (DatabaseInterface::ColumnsRoles role : (isRadio() ? mFieldsForRadioTrack : mFieldsForClassicTrack)){ + for (DatabaseInterface::ColumnsRoles role : fieldsForTrack) { 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(); fetchLyrics(); + mDatabaseId = trackData[DatabaseInterface::DatabaseIdRole].toULongLong(); + Q_EMIT databaseIdChanged(); + 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::fillLyricsDataFromTrack() { beginInsertRows({}, mTrackData.size(), mTrackData.size()); mTrackKeys.push_back(DatabaseInterface::LyricsRole); mTrackData[DatabaseInterface::LyricsRole] = mLyricsValueWatcher.result(); endInsertRows(); } void TrackMetadataModel::lyricsValueIsReady() { if (!mLyricsValueWatcher.result().isEmpty()) { fillLyricsDataFromTrack(); mFullData[DatabaseInterface::LyricsRole] = mLyricsValueWatcher.result(); Q_EMIT lyricsChanged(); } } +void TrackMetadataModel::initializeById(ElisaUtils::PlayListEntryType type, qulonglong databaseId) +{ + mFullData.clear(); + mTrackData.clear(); + mCoverImage.clear(); + mFileUrl.clear(); + + Q_EMIT lyricsChanged(); + + Q_EMIT needDataByDatabaseId(type, databaseId); +} + 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); - - if (isRadio()) { - connect(this, &TrackMetadataModel::saveRadioData, - &mDataLoader, &ModelDataLoader::updateRadioData); - connect(this, &TrackMetadataModel::deleteRadioData, - &mDataLoader, &ModelDataLoader::deleteRadioData); - connect(&mDataLoader, &ModelDataLoader::radioAdded, - this, &TrackMetadataModel::radioAdded); - connect(&mDataLoader, &ModelDataLoader::radioModified, - this, &TrackMetadataModel::radioModified); - connect(&mDataLoader, &ModelDataLoader::radioRemoved, - this, &TrackMetadataModel::radioRemoved); - connect(&mDataLoader, &ModelDataLoader::allRadioData, - this, &TrackMetadataModel::trackData); - } else { - connect(&mDataLoader, &ModelDataLoader::trackModified, - this, &TrackMetadataModel::trackData); - connect(&mDataLoader, &ModelDataLoader::allTrackData, - this, &TrackMetadataModel::trackData); - } + connect(this, &TrackMetadataModel::saveRadioData, + &mDataLoader, &ModelDataLoader::saveRadioModified); + connect(this, &TrackMetadataModel::deleteRadioData, + &mDataLoader, &ModelDataLoader::removeRadio); + connect(&mDataLoader, &ModelDataLoader::trackModified, + this, &TrackMetadataModel::trackData); + connect(&mDataLoader, &ModelDataLoader::allTrackData, + this, &TrackMetadataModel::trackData); + connect(&mDataLoader, &ModelDataLoader::allRadioData, + this, &TrackMetadataModel::radioData); + connect(&mDataLoader, &ModelDataLoader::radioAdded, + this, &TrackMetadataModel::radioData); + connect(&mDataLoader, &ModelDataLoader::radioModified, + this, &TrackMetadataModel::radioData); } void TrackMetadataModel::fetchLyrics() { auto lyricicsValue = QtConcurrent::run(QThreadPool::globalInstance(), [=]() { auto trackData = mFileScanner.scanOneFile(mFullData[DatabaseInterface::ResourceRole].toUrl(), mMimeDatabase); if (!trackData.lyrics().isEmpty()) { return trackData.lyrics(); } return QString{}; }); mLyricsValueWatcher.setFuture(lyricicsValue); } -void TrackMetadataModel::initializeByTrackId(qulonglong databaseId) -{ - mFullData.clear(); - mTrackData.clear(); - mCoverImage.clear(); - mFileUrl.clear(); - - Q_EMIT lyricsChanged(); - - Q_EMIT needDataByDatabaseId((isRadio() ? ElisaUtils::Radio : ElisaUtils::Track), databaseId); -} - void TrackMetadataModel::initializeForNewRadio() { mFullData.clear(); mTrackData.clear(); fillDataForNewRadio(); } void TrackMetadataModel::fillDataForNewRadio() { beginResetModel(); mTrackData.clear(); mTrackKeys.clear(); for (auto role : { DatabaseInterface::TitleRole, DatabaseInterface::ResourceRole, DatabaseInterface::CommentRole, DatabaseInterface::DatabaseIdRole - }) { +}) { mTrackKeys.push_back(role); if (role == DatabaseInterface::DatabaseIdRole) { mTrackData[role] = -1; - Q_EMIT hideDeleteButton(); } else { mTrackData[role] = QString(); } } filterDataFromTrackData(); endResetModel(); } void TrackMetadataModel::initializeByTrackFileName(const QString &fileName) { mFullData.clear(); mTrackData.clear(); mCoverImage.clear(); mFileUrl.clear(); Q_EMIT lyricsChanged(); Q_EMIT needDataByFileName(ElisaUtils::FileName, QUrl::fromLocalFile(fileName)); } void TrackMetadataModel::setManager(MusicListenersManager *newManager) { initialize(newManager, nullptr); } -void TrackMetadataModel::setIsRadio(bool isRadio){ - this->mIsRadio = isRadio; - - Q_EMIT isRadioChanged(); -} - void TrackMetadataModel::setDatabase(DatabaseInterface *trackDatabase) { initialize(nullptr, trackDatabase); } -bool TrackMetadataModel::isRadio(){ - return this->mIsRadio; -} - void TrackMetadataModel::saveData() { Q_EMIT saveRadioData(mTrackData); } void TrackMetadataModel::deleteRadio() { if (mTrackData[DatabaseInterface::DatabaseIdRole]>=0) { Q_EMIT deleteRadioData(mTrackData[DatabaseInterface::DatabaseIdRole].toULongLong()); } } -void TrackMetadataModel::radioAdded(const TrackDataType &radiosData){ - mTrackData[DatabaseInterface::DatabaseIdRole] = radiosData[DatabaseInterface::DatabaseIdRole]; - Q_EMIT showDeleteButton(); - radioModified(); -} +void TrackMetadataModel::radioData(const TrackDataType &radiosData) +{ + if (!mFullData.isEmpty() && mFullData[DatabaseInterface::DatabaseIdRole].toInt() != -1 && + mFullData.databaseId() != radiosData.databaseId()) { + return; + } -void TrackMetadataModel::radioModified(){ - Q_EMIT disableApplyButton(); -} + const QList fieldsForTrack({DatabaseInterface::TitleRole, DatabaseInterface::ResourceRole, + DatabaseInterface::CommentRole, DatabaseInterface::DatabaseIdRole}); -void TrackMetadataModel::radioRemoved(){ - Q_EMIT closeWindow(); + fillDataFromTrackData(radiosData, fieldsForTrack); } #include "moc_trackmetadatamodel.cpp" diff --git a/src/models/trackmetadatamodel.h b/src/models/trackmetadatamodel.h index 291674c7..e55850ee 100644 --- a/src/models/trackmetadatamodel.h +++ b/src/models/trackmetadatamodel.h @@ -1,211 +1,193 @@ /* * 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 "filescanner.h" #include #include #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(qulonglong databaseId + READ databaseId + NOTIFY databaseIdChanged) + Q_PROPERTY(MusicListenersManager* manager READ manager WRITE setManager NOTIFY managerChanged) Q_PROPERTY(QString lyrics READ lyrics NOTIFY lyricsChanged) - Q_PROPERTY(bool isRadio - READ isRadio - WRITE setIsRadio - NOTIFY isRadioChanged) - public: enum ColumnRoles { ItemNameRole = Qt::UserRole + 1, ItemTypeRole, }; enum ItemType { TextEntry, IntegerEntry, RatingEntry, DateEntry, LongTextEntry, }; Q_ENUM(ItemType) using TrackDataType = DatabaseInterface::TrackDataType; explicit TrackMetadataModel(QObject *parent = nullptr); ~TrackMetadataModel() override; 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; QUrl coverUrl() const; QString fileUrl() const; MusicListenersManager* manager() const; QString lyrics() const; - bool isRadio(); + qulonglong databaseId() const; Q_SIGNALS: void needDataByDatabaseId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId); void needDataByFileName(ElisaUtils::PlayListEntryType dataType, const QUrl &fileName); void coverUrlChanged(); void fileUrlChanged(); void managerChanged(); void lyricsChanged(); void saveRadioData(const DatabaseInterface::TrackDataType &trackDataType); void deleteRadioData(qulonglong radioId); - void disableApplyButton(); - - void hideDeleteButton(); - - void showDeleteButton(); - - void closeWindow(); - - void isRadioChanged(); + void databaseIdChanged(); public Q_SLOTS: void trackData(const TrackMetadataModel::TrackDataType &trackData); - void initializeByTrackId(qulonglong databaseId); + void initializeById(ElisaUtils::PlayListEntryType type, qulonglong databaseId); void initializeByTrackFileName(const QString &fileName); void initializeForNewRadio(); void setManager(MusicListenersManager *newManager); - void setIsRadio(bool isRadio); - void setDatabase(DatabaseInterface *trackDatabase); void saveData(); void deleteRadio(); - void radioAdded(const TrackMetadataModel::TrackDataType &radiosData); - - void radioModified(); - - void radioRemoved(); + void radioData(const TrackMetadataModel::TrackDataType &radiosData); protected: - void fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData); + void fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData, + const QList &fieldsForTrack); void fillDataForNewRadio(); virtual void filterDataFromTrackData(); void removeMetaData(DatabaseInterface::ColumnsRoles metaData); TrackDataType::mapped_type dataFromType(TrackDataType::key_type metaData) const; virtual void fillLyricsDataFromTrack(); private Q_SLOTS: void lyricsValueIsReady(); private: void initialize(MusicListenersManager *newManager, DatabaseInterface *trackDatabase); void fetchLyrics(); - QVariant dataGeneral(const QModelIndex &index, int role) const; - - QVariant dataRadio(const QModelIndex &index, int role) const; - TrackDataType mFullData; TrackDataType mTrackData; QUrl mCoverImage; QString mFileUrl; + qulonglong mDatabaseId = 0; + QList mTrackKeys; ModelDataLoader mDataLoader; MusicListenersManager *mManager = nullptr; - bool mIsRadio = false; - FileScanner mFileScanner; QMimeDatabase mMimeDatabase; QFutureWatcher mLyricsValueWatcher; }; #endif // TRACKMETADATAMODEL_H diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp index c445b542..50d67a42 100644 --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -1,473 +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()) { d->mListenerThread.start(); d->mDatabaseThread.start(); d->mDatabaseInterface.moveToThread(&d->mDatabaseThread); connect(&d->mDatabaseInterface, &DatabaseInterface::requestsInitDone, this, &MusicListenersManager::databaseReady); connect(this, &MusicListenersManager::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::clearData); connect(&d->mDatabaseInterface, &DatabaseInterface::cleanedDatabase, this, &MusicListenersManager::cleanedDatabase); 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)); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &MusicListenersManager::applicationAboutToQuit); connect(Elisa::ElisaConfiguration::self(), &Elisa::ElisaConfiguration::configChanged, this, &MusicListenersManager::configChanged); connect(&d->mConfigFileWatcher, &QFileSystemWatcher::fileChanged, this, &MusicListenersManager::configChanged); 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()); connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, this, &MusicListenersManager::increaseImportedTracksCount); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND connect(&d->mBalooDetector, &BalooDetector::balooAvailabilityChanged, this, &MusicListenersManager::balooAvailabilityChanged); #endif 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::newTrackByNameInList, d->mTracksListener.get(), &TracksListener::trackByNameInList); } int MusicListenersManager::importedTracksCount() const { return d->mImportedTracksCount; } ElisaApplication *MusicListenersManager::elisaApplication() const { return d->mElisaApplication; } 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() { 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(); } else { 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 DatabaseInterface::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(); } 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); QMetaObject::invokeMethod(d->mFileListener.fileListing(), "init", Qt::QueuedConnection); 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); QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection); 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); } } #include "moc_musiclistenersmanager.cpp" diff --git a/src/musiclistenersmanager.h b/src/musiclistenersmanager.h index 8589e762..c450b94a 100644 --- a/src/musiclistenersmanager.h +++ b/src/musiclistenersmanager.h @@ -1,177 +1,179 @@ /* * 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 "databaseinterface.h" #include #include #include class MusicListenersManagerPrivate; class MediaPlayList; class ElisaApplication; class ModelDataLoader; 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(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; 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 importedTracksCountChanged(); void elisaApplicationChanged(); void removeTracksInError(const QList &tracks); void displayTrackError(const QString &fileName); void indexerBusyChanged(); void clearDatabase(); 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 DatabaseInterface::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 diff --git a/src/qml/DataListView.qml b/src/qml/DataListView.qml index 3afbff64..348ac455 100644 --- a/src/qml/DataListView.qml +++ b/src/qml/DataListView.qml @@ -1,218 +1,240 @@ /* * 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 . */ import QtQuick 2.10 import QtQuick.Controls 2.3 import org.kde.kirigami 2.5 as Kirigami import org.kde.elisa 1.0 FocusScope { id: viewHeader property var viewType property var filterType property alias isSubPage: listView.isSubPage property alias mainTitle: listView.mainTitle property alias secondaryTitle: listView.secondaryTitle property int databaseId property alias showSection: listView.showSection property alias expandedFilterView: listView.expandedFilterView property alias image: listView.image property var modelType property alias sortRole: proxyModel.sortRole property var sortAscending property bool displaySingleAlbum: false property alias radioCase: listView.showCreateRadioButton function openMetaDataView(databaseId){ - metadataLoader.setSource("MediaTrackMetadataView.qml", {"databaseId": databaseId, "isRadio": viewHeader.radioCase}); + if (viewHeader.radioCase) { + metadataLoader.setSource("MediaTrackMetadataView.qml", + { + "initialDatabaseId": databaseId, + "modelType": modelType, + "showImage": false, + "showTrackFileName": false, + "showDeleteButton": databaseId !== -1, + "showApplyButton": true, + "editableMetadata": true, + }); + } else { + metadataLoader.setSource("MediaTrackMetadataView.qml", + { + "initialDatabaseId": databaseId, + "modelType": modelType, + "showImage": true, + "showTrackFileName": true, + "showDeleteButton": false, + "showApplyButton": false, + "editableMetadata": false, + }); + } metadataLoader.active = true } DataModel { id: realModel } AllTracksProxyModel { id: proxyModel sourceModel: realModel onEntriesToEnqueue: elisa.mediaPlayList.enqueue(newEntries, databaseIdType, enqueueMode, triggerPlay) } Loader { id: metadataLoader active: false onLoaded: item.show() } Component { id: albumDelegate ListBrowserDelegate { id: entry width: listView.delegateWidth height: elisaTheme.delegateHeight focus: true databaseId: model.databaseId title: model.title ? model.title : '' artist: model.artist ? model.artist : '' album: model.album ? model.album : '' albumArtist: model.albumArtist ? model.albumArtist : '' duration: model.duration ? model.duration : '' imageUrl: model.imageUrl ? model.imageUrl : '' trackNumber: model.trackNumber ? model.trackNumber : -1 discNumber: model.discNumber ? model.discNumber : -1 rating: model.rating isSelected: listView.currentIndex === index isAlternateColor: (index % 2) === 1 detailedView: false onEnqueue: elisa.mediaPlayList.enqueue(databaseId, name, ElisaUtils.Track, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) onReplaceAndPlay: elisa.mediaPlayList.enqueue(databaseId, name, ElisaUtils.Track, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) onClicked: listView.currentIndex = index onActiveFocusChanged: { if (activeFocus && listView.currentIndex !== index) { listView.currentIndex = index } } onCallOpenMetaDataView: { openMetaDataView(databaseId) } } } Component { id: detailedTrackDelegate ListBrowserDelegate { id: entry width: listView.delegateWidth height: elisaTheme.trackDelegateHeight focus: true databaseId: model.databaseId title: model.title ? model.title : '' artist: model.artist ? model.artist : '' album: model.album ? model.album : '' albumArtist: model.albumArtist ? model.albumArtist : '' duration: model.duration ? model.duration : '' imageUrl: model.imageUrl ? model.imageUrl : '' trackNumber: model.trackNumber ? model.trackNumber : -1 discNumber: model.discNumber ? model.discNumber : -1 rating: model.rating hideDiscNumber: model.isSingleDiscAlbum isSelected: listView.currentIndex === index isAlternateColor: (index % 2) === 1 onEnqueue: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) onReplaceAndPlay: elisa.mediaPlayList.enqueue(databaseId, name, modelType, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) onClicked: { listView.currentIndex = index entry.forceActiveFocus() } onCallOpenMetaDataView: { openMetaDataView(databaseId) } } } ListBrowserView { id: listView focus: true anchors.fill: parent contentModel: proxyModel delegate: (displaySingleAlbum ? albumDelegate : detailedTrackDelegate) enableSorting: !displaySingleAlbum allowArtistNavigation: isSubPage onShowArtist: { viewManager.openChildView(secondaryTitle, '', elisaTheme.artistIcon, 0, ElisaUtils.Artist, ViewManager.NoDiscHeaders) } onGoBack: viewManager.goBack() Loader { anchors.centerIn: parent height: Kirigami.Units.gridUnit * 5 width: height visible: realModel.isBusy active: realModel.isBusy sourceComponent: BusyIndicator { anchors.centerIn: parent } } } Connections { target: elisa onMusicManagerChanged: realModel.initialize(elisa.musicManager, elisa.musicManager.viewDatabase, modelType) } Connections { target: listView.navigationBar onCreateRadio: { openMetaDataView(-1) } } Component.onCompleted: { if (elisa.musicManager) { realModel.initialize(elisa.musicManager, elisa.musicManager.viewDatabase, modelType, filterType, mainTitle, secondaryTitle, databaseId) } if (sortAscending === ViewManager.SortAscending) { proxyModel.sortModel(Qt.AscendingOrder) } else if (sortAscending === ViewManager.SortDescending) { proxyModel.sortModel(Qt.DescendingOrder) } } } diff --git a/src/qml/GridBrowserDelegate.qml b/src/qml/GridBrowserDelegate.qml index 3e059326..2f53ee44 100644 --- a/src/qml/GridBrowserDelegate.qml +++ b/src/qml/GridBrowserDelegate.qml @@ -1,434 +1,441 @@ /* * 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 . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtQml.Models 2.1 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.5 as Kirigami FocusScope { id: gridEntry property var imageUrl property bool shadowForImage property string fileUrl property alias mainText: mainLabel.text property alias secondaryText: secondaryLabel.text property var databaseId property bool delegateDisplaySecondaryText: true property bool isPartial property bool isSelected property bool showDetailsButton: false property bool showOpenButton: true property bool showPlayButton: true property bool showEnqueueButton: true signal enqueue(var databaseId, var name) signal replaceAndPlay(var databaseId, var name) signal open() signal selected() Loader { id: metadataLoader active: false && gridEntry.fileUrl onLoaded: item.show() sourceComponent: MediaTrackMetadataView { fileName: gridEntry.fileUrl ? gridEntry.fileUrl : '' + showImage: true + modelType: modelType + showTrackFileName: true + showDeleteButton: false + showApplyButton: false + editableMetadata: false + onRejected: metadataLoader.active = false; } } Keys.onReturnPressed: open() Keys.onEnterPressed: open() Accessible.role: Accessible.ListItem Accessible.name: mainText Rectangle { id: stateIndicator anchors.fill: parent z: 1 color: "transparent" opacity: 0.4 radius: 3 } ColumnLayout { anchors.fill: parent z: 2 spacing: 0 MouseArea { id: hoverHandle hoverEnabled: true acceptedButtons: Qt.LeftButton Layout.preferredHeight: gridEntry.height Layout.fillWidth: true onClicked: { gridEntry.selected() } onDoubleClicked: open() TextMetrics { id: mainLabelSize font: mainLabel.font text: mainLabel.text } TextMetrics { id: secondaryLabelSize font: secondaryLabel.font text: secondaryLabel.text } ColumnLayout { id: mainData spacing: 0 anchors.fill: parent Item { Layout.preferredHeight: gridEntry.width * elisaTheme.gridDelegateThumbnail Layout.preferredWidth: gridEntry.width * elisaTheme.gridDelegateThumbnail Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Loader { id: hoverLoader active: false anchors { bottom: parent.bottom bottomMargin: 2 left: parent.left leftMargin: 2 } z: 1 opacity: 0 sourceComponent: Row { spacing: 2 Button { id: detailsButton objectName: 'detailsButton' icon.name: 'help-about' hoverEnabled: true ToolTip.visible: hovered ToolTip.delay: 1000 ToolTip.text: i18nc("Show track metadata", "View Details") Accessible.role: Accessible.Button Accessible.name: ToolTip.text Accessible.description: ToolTip.text Accessible.onPressAction: clicked() onClicked: { if (metadataLoader.active === false) { metadataLoader.active = true } else { metadataLoader.item.close(); metadataLoader.active = false } } Keys.onReturnPressed: clicked() Keys.onEnterPressed: clicked() visible: showDetailsButton width: elisaTheme.delegateToolButtonSize height: elisaTheme.delegateToolButtonSize } Button { id: replaceAndPlayButton objectName: 'replaceAndPlayButton' icon.name: 'media-playback-start' hoverEnabled: true ToolTip.visible: hovered ToolTip.delay: 1000 ToolTip.text: i18nc("Clear play list and add whole container to play list", "Play now, replacing current playlist") Accessible.role: Accessible.Button Accessible.name: ToolTip.text Accessible.description: ToolTip.text Accessible.onPressAction: onClicked onClicked: replaceAndPlay(databaseId, mainText) Keys.onReturnPressed: replaceAndPlay(databaseId, mainText) Keys.onEnterPressed: replaceAndPlay(databaseId, mainText) visible: showPlayButton width: elisaTheme.delegateToolButtonSize height: elisaTheme.delegateToolButtonSize } Button { id: enqueueButton objectName: 'enqueueButton' icon.name: 'list-add' hoverEnabled: true ToolTip.visible: hovered ToolTip.delay: 1000 ToolTip.text: i18nc("Add whole container to play list", "Add to playlist") Accessible.role: Accessible.Button Accessible.name: ToolTip.text Accessible.description: ToolTip.text Accessible.onPressAction: onClicked onClicked: enqueue(databaseId, mainText) Keys.onReturnPressed: enqueue(databaseId, mainText) Keys.onEnterPressed: enqueue(databaseId, mainText) visible: showEnqueueButton width: elisaTheme.delegateToolButtonSize height: elisaTheme.delegateToolButtonSize } Button { id: openButton objectName: 'openButton' icon.name: 'go-next-view-page' hoverEnabled: true ToolTip.visible: hovered ToolTip.delay: 1000 ToolTip.text: i18nc("Open view of the container", "Open") Accessible.role: Accessible.Button Accessible.name: ToolTip.text Accessible.description: ToolTip.text Accessible.onPressAction: onClicked onClicked: open() visible: showOpenButton width: elisaTheme.delegateToolButtonSize height: elisaTheme.delegateToolButtonSize } } } Loader { id: coverImageLoader active: !isPartial anchors.fill: parent sourceComponent: Image { id: coverImage anchors.fill: parent sourceSize.width: parent.width sourceSize.height: parent.height fillMode: Image.PreserveAspectFit smooth: true source: (gridEntry.imageUrl !== undefined ? gridEntry.imageUrl : "") asynchronous: true layer.enabled: shadowForImage layer.effect: DropShadow { source: coverImage radius: 10 spread: 0.1 samples: 21 color: myPalette.shadow } } } Loader { active: isPartial anchors.centerIn: parent height: Kirigami.Units.gridUnit * 5 width: height sourceComponent: BusyIndicator { anchors.centerIn: parent running: true } } } LabelWithToolTip { id: mainLabel font.weight: Font.Bold color: myPalette.text // FIXME: Center-aligned text looks better overall, but // sometimes results in font kerning issues // See https://bugreports.qt.io/browse/QTBUG-49646 horizontalAlignment: Text.AlignHCenter Layout.topMargin: elisaTheme.layoutVerticalMargin * 0.5 Layout.maximumWidth: gridEntry.width * 0.9 Layout.minimumWidth: Layout.maximumWidth Layout.maximumHeight: delegateDisplaySecondaryText ? (mainLabelSize.boundingRect.height - mainLabelSize.boundingRect.y) : (mainLabelSize.boundingRect.height - mainLabelSize.boundingRect.y) * 2 Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.bottomMargin: delegateDisplaySecondaryText ? 0 : elisaTheme.layoutVerticalMargin wrapMode: delegateDisplaySecondaryText ? Label.NoWrap : Label.Wrap elide: Text.ElideRight } LabelWithToolTip { id: secondaryLabel font.weight: Font.Light color: myPalette.text // FIXME: Center-aligned text looks better overall, but // sometimes results in font kerning issues // See https://bugreports.qt.io/browse/QTBUG-49646 horizontalAlignment: Text.AlignHCenter Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.maximumWidth: gridEntry.width * 0.9 Layout.minimumWidth: Layout.maximumWidth Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom visible: delegateDisplaySecondaryText elide: Text.ElideRight } } } Item { Layout.fillHeight: true } } states: [ State { name: 'notSelected' when: !gridEntry.activeFocus && !hoverHandle.containsMouse && !gridEntry.isSelected PropertyChanges { target: stateIndicator color: 'transparent' } PropertyChanges { target: stateIndicator opacity: 1.0 } PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0.0 } }, State { name: 'hovered' when: hoverHandle.containsMouse && !gridEntry.activeFocus PropertyChanges { target: stateIndicator color: myPalette.highlight } PropertyChanges { target: stateIndicator opacity: 0.2 } PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: hoverLoader opacity: 1.0 } }, State { name: 'selected' when: gridEntry.isSelected && !gridEntry.activeFocus PropertyChanges { target: stateIndicator color: myPalette.mid } PropertyChanges { target: stateIndicator opacity: 0.6 } PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: hoverLoader opacity: 0. } }, State { name: 'hoveredOrSelected' when: gridEntry.activeFocus PropertyChanges { target: stateIndicator color: myPalette.highlight } PropertyChanges { target: stateIndicator opacity: 0.6 } PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: hoverLoader opacity: 1.0 } } ] } diff --git a/src/qml/MediaTrackMetadataView.qml b/src/qml/MediaTrackMetadataView.qml index dd98541d..15115061 100644 --- a/src/qml/MediaTrackMetadataView.qml +++ b/src/qml/MediaTrackMetadataView.qml @@ -1,255 +1,243 @@ /* * Copyright 2017 Alexander Stippich * 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 . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtQml.Models 2.2 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 Window { id: trackMetadata - property int databaseId: 0 + property int initialDatabaseId: 0 + property var modelType property string fileName - property alias isRadio: realModel.isRadio + property bool editableMetadata + property alias showImage: metadataImage.visible + property alias showTrackFileName: fileNameRow.visible + property alias showDeleteButton: deleteButtonBox.visible + property alias showApplyButton: applyButton.visible signal rejected() LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true - title: { - if (trackMetadata.isRadio && databaseId === -1) { - return i18nc("Window title for track metadata", "Create a Radio") - } - - return i18nc("Window title for track metadata", "View Details") - } + title: (initialDatabaseId === -1) ? i18nc("Window title for track metadata", "Create a Radio") : i18nc("Window title for track metadata", "View Details") TrackMetadataModel { id: realModel manager: elisa.musicManager } modality: Qt.NonModal flags: Qt.Dialog | Qt.WindowCloseButtonHint color: myPalette.window minimumHeight: elisaTheme.coverImageSize * 1.8 minimumWidth: elisaTheme.coverImageSize * 2.8 ColumnLayout { anchors.fill: parent anchors.margins: elisaTheme.layoutVerticalMargin spacing: elisaTheme.layoutVerticalMargin RowLayout { id: metadataView Layout.fillHeight: true Layout.fillWidth: true spacing: 0 Image { + id: metadataImage + source: realModel.coverUrl sourceSize.width: elisaTheme.coverImageSize sourceSize.height: elisaTheme.coverImageSize fillMode: Image.PreserveAspectFit Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Layout.preferredHeight: elisaTheme.coverImageSize Layout.preferredWidth: elisaTheme.coverImageSize Layout.minimumHeight: elisaTheme.coverImageSize Layout.minimumWidth: elisaTheme.coverImageSize Layout.maximumHeight: elisaTheme.coverImageSize Layout.maximumWidth: elisaTheme.coverImageSize - - visible: !trackMetadata.isRadio } ListView { id: trackData Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: 2 * elisaTheme.layoutHorizontalMargin focus: true ScrollBar.vertical: ScrollBar { id: scrollBar } boundsBehavior: Flickable.StopAtBounds clip: true ScrollHelper { id: scrollHelper flickable: trackData anchors.fill: trackData } model: realModel Component { id: metaDataDelegate MetaDataDelegate { width: scrollBar.visible ? (!LayoutMirroring.enabled ? trackData.width - scrollBar.width : trackData.width) : trackData.width } } Component { id: editableMetaDataDelegate EditableMetaDataDelegate { width: scrollBar.visible ? (!LayoutMirroring.enabled ? trackData.width - scrollBar.width : trackData.width) : trackData.width onRadioEdited: applyButton.enabled = true } } - delegate: trackMetadata.isRadio ? editableMetaDataDelegate: metaDataDelegate + delegate: editableMetadata ? editableMetaDataDelegate: metaDataDelegate } } RowLayout { + id: fileNameRow + Layout.alignment: Qt.AlignLeft | Qt.AlignBottom Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.bottomMargin: elisaTheme.layoutVerticalMargin spacing: elisaTheme.layoutHorizontalMargin Image { Layout.preferredWidth: fileNameLabel.height Layout.preferredHeight: fileNameLabel.height sourceSize.width: fileNameLabel.height sourceSize.height: fileNameLabel.height source: elisaTheme.folderIcon } LabelWithToolTip { id: fileNameLabel Layout.fillWidth: true text: realModel.fileUrl elide: Text.ElideRight } - - visible: !trackMetadata.isRadio } - RowLayout{ + RowLayout { spacing: elisaTheme.layoutVerticalMargin DialogButtonBox { + id: deleteButtonBox + Layout.minimumHeight: implicitHeight alignment: Qt.AlignLeft Button { id: deleteButton text: qsTr("Delete") DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole - onClicked: realModel.deleteRadio() + onClicked: + { + elisa.musicManager.deleteElementById(modelType, realModel.databaseId) + trackMetadata.close() + } } } DialogButtonBox { id: buttons Layout.fillWidth: true Layout.minimumHeight: implicitHeight alignment: Qt.AlignRight Button { id: applyButton text: qsTr("Apply") DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole - onClicked: realModel.saveData() + onClicked: + { + realModel.saveData() + enabled = false + if (!deleteButtonBox.visible && editableMetadata) { + deleteButtonBox.visible = true + } + } enabled: false } Button { text: qsTr("Close") DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole onClicked: trackMetadata.close() } } - - visible: trackMetadata.isRadio - } - - DialogButtonBox { - id: buttonsTrack - - Layout.fillWidth: true - Layout.minimumHeight: implicitHeight - - standardButtons: DialogButtonBox.Close - alignment: Qt.AlignRight - onRejected: trackMetadata.close() - - visible: !trackMetadata.isRadio } } Connections { target: elisa onMusicManagerChanged: { - if (databaseId === -1) { + if (initialDatabaseId === -1) { realModel.initializeForNewRadio() - } else if (databaseId !== 0) { - realModel.initializeByTrackId(databaseId) + } else if (initialDatabaseId !== 0) { + realModel.initializeById(modelType, initialDatabaseId) } else { realModel.initializeByTrackFileName(fileName) } } } - Connections{ - target: realModel - - onDisableApplyButton: applyButton.enabled = false - onShowDeleteButton: deleteButton.visible = true - onHideDeleteButton: deleteButton.visible = false - onCloseWindow: trackMetadata.close() - } - Component.onCompleted: { if (elisa.musicManager) { - if (databaseId === -1) { + if (initialDatabaseId === -1) { realModel.initializeForNewRadio() - } else if (databaseId !== 0) { - realModel.initializeByTrackId(databaseId) + } else if (initialDatabaseId !== 0) { + realModel.initializeById(modelType, initialDatabaseId) } else { realModel.initializeByTrackFileName(fileName) } } } } diff --git a/src/qml/PlayListBasicView.qml b/src/qml/PlayListBasicView.qml index 059babd1..6c50f227 100644 --- a/src/qml/PlayListBasicView.qml +++ b/src/qml/PlayListBasicView.qml @@ -1,171 +1,172 @@ /* * Copyright 2016-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 . */ import QtQuick 2.10 import QtQuick.Controls 2.2 import QtQml.Models 2.1 import org.kde.elisa 1.0 ListView { id: playListView property alias playListModel: playListModelDelegate.model property string title signal startPlayback() signal pausePlayback() signal displayError(var errorText) focus: true keyNavigationEnabled: true activeFocusOnTab: true currentIndex: -1 Accessible.role: Accessible.List Accessible.name: title section.property: 'albumSection' section.criteria: ViewSection.FullString section.labelPositioning: ViewSection.InlineLabels section.delegate: PlayListAlbumHeader { headerData: JSON.parse(section) width: scrollBar.visible ? (!LayoutMirroring.enabled ? playListView.width - scrollBar.width : playListView.width) : playListView.width height: elisaTheme.playListHeaderHeight } ScrollBar.vertical: ScrollBar { id: scrollBar } boundsBehavior: Flickable.StopAtBounds clip: true ScrollHelper { id: scrollHelper flickable: playListView anchors.fill: playListView } add: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 100 } } populate: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 100 } } remove: Transition { NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 100 } } displaced: Transition { NumberAnimation { properties: "x,y"; duration: 100; easing.type: Easing.InOutQuad} } model: DelegateModel { id: playListModelDelegate groups: [ DelegateModelGroup { name: "selected" } ] delegate: DraggableItem { id: item placeholderHeight: topItem.placeholderHeight focus: true PlayListEntry { id: entry focus: true width: scrollBar.visible ? (!LayoutMirroring.enabled ? playListView.width - scrollBar.width : playListView.width) : playListView.width scrollBarWidth: scrollBar.visible ? scrollBar.width : 0 index: model.index isAlternateColor: item.DelegateModel.itemsIndex % 2 isSelected: playListView.currentIndex === index containsMouse: item.containsMouse databaseId: model.databaseId ? model.databaseId : 0 + entryType: model.entryType ? model.entryType : ElisaUtils.Unknown title: model.title ? model.title : '' artist: model.artist ? model.artist : '' album: model.album ? model.album : '' albumArtist: model.albumArtist ? model.albumArtist : '' duration: model.duration ? model.duration : '' fileName: model.trackResource ? model.trackResource : '' imageUrl: model.imageUrl ? model.imageUrl : '' trackNumber: model.trackNumber ? model.trackNumber : -1 discNumber: model.discNumber ? model.discNumber : -1 rating: model.rating ? model.rating : 0 isSingleDiscAlbum: model.isSingleDiscAlbum !== undefined ? model.isSingleDiscAlbum : true isValid: model.isValid isPlaying: model.isPlaying onStartPlayback: playListView.startPlayback() onPausePlayback: playListView.pausePlayback() onRemoveFromPlaylist: playListView.playListModel.removeRows(trackIndex, 1) onSwitchToTrack: playListView.playListModel.switchTo(trackIndex) onActiveFocusChanged: { if (activeFocus && playListView.currentIndex !== index) { playListView.currentIndex = index } } } draggedItemParent: playListView onClicked: { playListView.currentIndex = index entry.forceActiveFocus() } onDoubleClicked: { if (model.isValid) { playListView.playListModel.switchTo(model.index) playListView.startPlayback() } } onMoveItemRequested: { playListModel.move(from, to, 1); } } } onCountChanged: if (count === 0) { currentIndex = -1; } } diff --git a/src/qml/PlayListEntry.qml b/src/qml/PlayListEntry.qml index 7b76905e..d8ae9e89 100644 --- a/src/qml/PlayListEntry.qml +++ b/src/qml/PlayListEntry.qml @@ -1,490 +1,498 @@ /* * 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 . */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 FocusScope { id: playListEntry property var index property bool isSingleDiscAlbum property int isPlaying property bool isSelected property bool isValid property bool isAlternateColor property bool containsMouse property int databaseId: 0 + property var entryType property string title property string artist property string album property string albumArtist property string duration property url fileName property url imageUrl property int trackNumber property int discNumber property int rating property bool hasValidDiscNumber: true property int scrollBarWidth property bool simpleMode: false signal startPlayback() signal pausePlayback() signal removeFromPlaylist(var trackIndex) signal switchToTrack(var trackIndex) Accessible.role: Accessible.ListItem Accessible.name: title + ' ' + album + ' ' + artist height: elisaTheme.playListDelegateHeight Action { id: removeFromPlayList text: i18nc("Remove current track from play list", "Remove") icon.name: "error" onTriggered: { playListEntry.removeFromPlaylist(playListEntry.index) } } Action { id: playNow text: i18nc("Play now current track from play list", "Play Now") icon.name: "media-playback-start" enabled: !(isPlaying === MediaPlayList.IsPlaying) && isValid onTriggered: { if (isPlaying === MediaPlayList.NotPlaying) { playListEntry.switchToTrack(playListEntry.index) } playListEntry.startPlayback() } } Action { id: pauseNow text: i18nc("Pause current track from play list", "Pause") icon.name: "media-playback-pause" enabled: isPlaying === MediaPlayList.IsPlaying && isValid onTriggered: playListEntry.pausePlayback() } Action { id: showInfo text: i18nc("Show track metadata", "View Details") icon.name: "help-about" enabled: isValid onTriggered: { if (metadataLoader.active === false) { metadataLoader.active = true } else { metadataLoader.item.close(); metadataLoader.active = false } } } Loader { id: metadataLoader active: false onLoaded: item.show() sourceComponent: MediaTrackMetadataView { - databaseId: playListEntry.databaseId + initialDatabaseId: playListEntry.databaseId fileName: playListEntry.fileName - onRejected: metadataLoader.active = false; + showImage: entryType == ElisaUtils.Track + modelType: entryType + showTrackFileName: entryType == ElisaUtils.Track + showDeleteButton: entryType != ElisaUtils.Track + showApplyButton: entryType != ElisaUtils.Track + editableMetadata: entryType != ElisaUtils.Track + + onRejected: metadataLoader.active = false } } Rectangle { id: entryBackground anchors.fill: parent anchors.rightMargin: LayoutMirroring.enabled ? scrollBarWidth : 0 z: 1 color: simpleMode ? "transparent" : myPalette.base height: elisaTheme.playListDelegateHeight } RowLayout { id: trackRow z: 2 anchors.fill: parent anchors.leftMargin: elisaTheme.layoutHorizontalMargin anchors.rightMargin: LayoutMirroring.enabled ? scrollBarWidth : 0 spacing: elisaTheme.layoutHorizontalMargin / 4 Item { id: playIconItem implicitHeight: elisaTheme.playListDelegateHeight implicitWidth: elisaTheme.playListDelegateHeight Layout.maximumWidth: elisaTheme.playListDelegateHeight Layout.maximumHeight: elisaTheme.playListDelegateHeight Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.leftMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 Layout.rightMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 Image { id: playIcon anchors.centerIn: parent opacity: (isPlaying === MediaPlayList.IsPlaying || isPlaying === MediaPlayList.IsPaused ? 1.0 : 0.0) source: (isPlaying === MediaPlayList.IsPlaying ? Qt.resolvedUrl(elisaTheme.playingIndicatorIcon) : Qt.resolvedUrl(elisaTheme.pausedIndicatorIcon)) width: elisaTheme.smallControlButtonSize height: elisaTheme.smallControlButtonSize sourceSize.width: elisaTheme.smallControlButtonSize sourceSize.height: elisaTheme.smallControlButtonSize fillMode: Image.PreserveAspectFit mirror: LayoutMirroring.enabled visible: opacity > 0.0 layer.enabled: simpleMode layer.effect: ColorOverlay { cached: true color: myPalette.highlightedText } } } Item { id: fakeDiscNumberItem visible: isValid && (!hasValidDiscNumber || isSingleDiscAlbum) Layout.preferredWidth: (fakeDiscNumberSize.boundingRect.width - fakeDiscNumberSize.boundingRect.x) + (elisaTheme.layoutHorizontalMargin / 4) Layout.minimumWidth: (fakeDiscNumberSize.boundingRect.width - fakeDiscNumberSize.boundingRect.x) + (elisaTheme.layoutHorizontalMargin / 4) Layout.maximumWidth: (fakeDiscNumberSize.boundingRect.width - fakeDiscNumberSize.boundingRect.x) + (elisaTheme.layoutHorizontalMargin / 4) TextMetrics { id: fakeDiscNumberSize text: '/9' } } Label { id: trackNumberLabel horizontalAlignment: Text.AlignRight text: trackNumber !== -1 ? Number(trackNumber).toLocaleString(Qt.locale(), 'f', 0) : '' font.weight: (isPlaying ? Font.Bold : Font.Light) color: simpleMode ? myPalette.highlightedText : myPalette.text Layout.alignment: Qt.AlignVCenter | Qt.AlignRight visible: isValid Layout.preferredWidth: ((trackNumberSize.boundingRect.width - trackNumberSize.boundingRect.x) > (realTrackNumberSize.boundingRect.width - realTrackNumberSize.boundingRect.x) ? (trackNumberSize.boundingRect.width - trackNumberSize.boundingRect.x) : (realTrackNumberSize.boundingRect.width - realTrackNumberSize.boundingRect.x)) Layout.minimumWidth: ((trackNumberSize.boundingRect.width - trackNumberSize.boundingRect.x) > (realTrackNumberSize.boundingRect.width - realTrackNumberSize.boundingRect.x) ? (trackNumberSize.boundingRect.width - trackNumberSize.boundingRect.x) : (realTrackNumberSize.boundingRect.width - realTrackNumberSize.boundingRect.x)) Layout.maximumWidth: ((trackNumberSize.boundingRect.width - trackNumberSize.boundingRect.x) > (realTrackNumberSize.boundingRect.width - realTrackNumberSize.boundingRect.x) ? (trackNumberSize.boundingRect.width - trackNumberSize.boundingRect.x) : (realTrackNumberSize.boundingRect.width - realTrackNumberSize.boundingRect.x)) Layout.rightMargin: !LayoutMirroring.enabled ? (discNumber !== 0 && !isSingleDiscAlbum ? 0 : elisaTheme.layoutHorizontalMargin / 2) : 0 Layout.leftMargin: LayoutMirroring.enabled ? (discNumber !== 0 && !isSingleDiscAlbum ? 0 : elisaTheme.layoutHorizontalMargin / 2) : 0 TextMetrics { id: trackNumberSize text: (99).toLocaleString(Qt.locale(), 'f', 0) } TextMetrics { id: realTrackNumberSize text: Number(trackNumber).toLocaleString(Qt.locale(), 'f', 0) } } Label { horizontalAlignment: Text.AlignCenter text: '/' visible: isValid && discNumber !== 0 && !isSingleDiscAlbum font.weight: (isPlaying ? Font.Bold : Font.Light) color: simpleMode ? myPalette.highlightedText : myPalette.text Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.preferredWidth: (numberSeparatorSize.boundingRect.width - numberSeparatorSize.boundingRect.x) Layout.minimumWidth: (numberSeparatorSize.boundingRect.width - numberSeparatorSize.boundingRect.x) Layout.maximumWidth: (numberSeparatorSize.boundingRect.width - numberSeparatorSize.boundingRect.x) TextMetrics { id: numberSeparatorSize text: '/' } } Label { horizontalAlignment: Text.AlignRight font.weight: (isPlaying ? Font.Bold : Font.Light) color: simpleMode ? myPalette.highlightedText : myPalette.text text: Number(discNumber).toLocaleString(Qt.locale(), 'f', 0) visible: isValid && discNumber !== 0 && !isSingleDiscAlbum Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.preferredWidth: ((discNumberSize.boundingRect.width - discNumberSize.boundingRect.x) > (realDiscNumberSize.boundingRect.width - realDiscNumberSize.boundingRect.x) ? (discNumberSize.boundingRect.width - discNumberSize.boundingRect.x) : (realDiscNumberSize.boundingRect.width - realDiscNumberSize.boundingRect.x)) Layout.minimumWidth: ((discNumberSize.boundingRect.width - discNumberSize.boundingRect.x) > (realDiscNumberSize.boundingRect.width - realDiscNumberSize.boundingRect.x) ? (discNumberSize.boundingRect.width - discNumberSize.boundingRect.x) : (realDiscNumberSize.boundingRect.width - realDiscNumberSize.boundingRect.x)) Layout.maximumWidth: ((discNumberSize.boundingRect.width - discNumberSize.boundingRect.x) > (realDiscNumberSize.boundingRect.width - realDiscNumberSize.boundingRect.x) ? (discNumberSize.boundingRect.width - discNumberSize.boundingRect.x) : (realDiscNumberSize.boundingRect.width - realDiscNumberSize.boundingRect.x)) Layout.rightMargin: !LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin / 2) : 0 Layout.leftMargin: LayoutMirroring.enabled ? (elisaTheme.layoutHorizontalMargin / 2) : 0 TextMetrics { id: discNumberSize text: '9' } TextMetrics { id: realDiscNumberSize text: Number(discNumber).toLocaleString(Qt.locale(), 'f', 0) } } LabelWithToolTip { id: mainCompactLabel text: title font.weight: (isPlaying ? Font.Bold : Font.Normal) color: simpleMode ? myPalette.highlightedText : myPalette.text Layout.maximumWidth: mainCompactLabel.implicitWidth + 1 Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft visible: isValid elide: Text.ElideRight horizontalAlignment: Text.AlignLeft } LabelWithToolTip { id: mainInvalidCompactLabel text: title font.weight: Font.Normal color: simpleMode ? myPalette.highlightedText : myPalette.text Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft visible: !isValid elide: Text.ElideRight } Item { Layout.fillWidth: true Layout.preferredWidth: 0 } Loader { id: hoverLoader active: false visible: active Layout.alignment: Qt.AlignVCenter | Qt.AlignRight sourceComponent: Row { anchors.centerIn: parent FlatButtonWithToolTip { id: infoButton objectName: 'infoButton' implicitHeight: elisaTheme.playListDelegateHeight implicitWidth: elisaTheme.playListDelegateHeight icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize action: showInfo } FlatButtonWithToolTip { id: playPauseButton objectName: 'playPauseButton' implicitHeight: elisaTheme.playListDelegateHeight implicitWidth: elisaTheme.playListDelegateHeight icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize scale: LayoutMirroring.enabled ? -1 : 1 // We can mirror the symmetrical pause icon action: !(isPlaying === MediaPlayList.IsPlaying) ? playNow : pauseNow } FlatButtonWithToolTip { id: removeButton objectName: 'removeButton' action: removeFromPlayList implicitHeight: elisaTheme.playListDelegateHeight implicitWidth: elisaTheme.playListDelegateHeight icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize } } } RatingStar { id: ratingWidget starRating: rating starSize: elisaTheme.ratingStarSize visible: rating > 0 } LabelWithToolTip { id: durationLabel text: duration font.weight: (isPlaying ? Font.Bold : Font.Normal) color: simpleMode ? myPalette.highlightedText : myPalette.text Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.rightMargin: elisaTheme.layoutHorizontalMargin horizontalAlignment: Text.AlignRight } } states: [ State { name: 'notSelected' when: !containsMouse && !isSelected && !playListEntry.activeFocus && !simpleMode PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: entryBackground color: (isAlternateColor ? myPalette.alternateBase : myPalette.base) } PropertyChanges { target: entryBackground opacity: 1. } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 0.0 } }, State { name: 'hovered' when: containsMouse && !playListEntry.activeFocus && !simpleMode PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: entryBackground color: myPalette.highlight } PropertyChanges { target: entryBackground opacity: 0.2 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } }, State { name: 'selected' when: !playListEntry.activeFocus && isSelected && !simpleMode PropertyChanges { target: hoverLoader active: false } PropertyChanges { target: entryBackground color: myPalette.mid } PropertyChanges { target: entryBackground opacity: 1. } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } }, State { name: 'focused' when: playListEntry.activeFocus && !simpleMode PropertyChanges { target: hoverLoader active: true } PropertyChanges { target: entryBackground color: myPalette.highlight } PropertyChanges { target: entryBackground opacity: 0.6 } PropertyChanges { target: ratingWidget hoverWidgetOpacity: 1.0 } } ] } diff --git a/src/qml/SimplePlayListView.qml b/src/qml/SimplePlayListView.qml index 71fd8fbd..2f554f2a 100644 --- a/src/qml/SimplePlayListView.qml +++ b/src/qml/SimplePlayListView.qml @@ -1,127 +1,128 @@ /* * Copyright 2016-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 . */ import QtQuick 2.10 import QtQuick.Controls 2.2 import QtQml.Models 2.1 import org.kde.elisa 1.0 ListView { id: playListView property alias playListModel: playListModelDelegate.model signal startPlayback() signal pausePlayback() signal displayError(var errorText) focus: true activeFocusOnTab: true keyNavigationEnabled: true section.property: 'albumSection' section.criteria: ViewSection.FullString section.labelPositioning: ViewSection.InlineLabels section.delegate: BasicPlayListAlbumHeader { headerData: JSON.parse(section) width: scrollBar.visible ? (!LayoutMirroring.enabled ? playListView.width - scrollBar.width : playListView.width) : playListView.width height: elisaTheme.playListHeaderHeight - 3 * elisaTheme.layoutVerticalMargin textColor: myPalette.highlightedText } ScrollBar.vertical: ScrollBar { id: scrollBar } boundsBehavior: Flickable.StopAtBounds clip: true add: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 100 } } populate: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 100 } } remove: Transition { NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 100 } } displaced: Transition { NumberAnimation { properties: "x,y"; duration: 100; easing.type: Easing.InOutQuad} } model: DelegateModel { id: playListModelDelegate groups: [ DelegateModelGroup { name: "selected" } ] delegate: MouseArea { id: item height: elisaTheme.playListDelegateHeight width: scrollBar.visible ? (!LayoutMirroring.enabled ? playListView.width - scrollBar.width : playListView.width) : playListView.width onClicked: playListView.playListModel.switchTo(index) PlayListEntry { id: entry focus: true width: scrollBar.visible ? (!LayoutMirroring.enabled ? playListView.width - scrollBar.width : playListView.width) : playListView.width scrollBarWidth: scrollBar.visible ? scrollBar.width : 0 index: model.index containsMouse: item.containsMouse simpleMode: true databaseId: model.databaseId ? model.databaseId : 0 + entryType: model.entryType ? model.entryType : ElisaUtils.Unknown title: model.title ? model.title : '' artist: model.artist ? model.artist : '' album: model.album ? model.album : '' albumArtist: model.albumArtist ? model.albumArtist : '' duration: model.duration ? model.duration : '' fileName: model.trackResource ? model.trackResource : '' imageUrl: model.imageUrl ? model.imageUrl : '' trackNumber: model.trackNumber ? model.trackNumber : -1 discNumber: model.discNumber ? model.discNumber : -1 rating: model.rating ? model.rating : 0 isSingleDiscAlbum: model.isSingleDiscAlbum !== undefined ? model.isSingleDiscAlbum : true isValid: model.isValid isPlaying: model.isPlaying } } } }