diff --git a/autotests/albummodeltest.cpp b/autotests/albummodeltest.cpp index af6c1f25..cd451a11 100644 --- a/autotests/albummodeltest.cpp +++ b/autotests/albummodeltest.cpp @@ -1,365 +1,436 @@ /* * Copyright 2015-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "musicalbum.h" #include "musicaudiotrack.h" #include "databaseinterface.h" #include "albummodel.h" #include #include #include #include #include #include #include #include #include #include #include #include class AlbumModelTests: public QObject { Q_OBJECT private: QHash> mNewTracks; QHash mNewCovers; private Q_SLOTS: void initTestCase() { mNewTracks[QStringLiteral("album1")] = { {true, QStringLiteral("$1"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist1"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 1, 1, QTime::fromMSecsSinceStartOfDay(1), {QUrl::fromLocalFile(QStringLiteral("/$1"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$1"))}}, {true, QStringLiteral("$2"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist2"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 2, 2, QTime::fromMSecsSinceStartOfDay(2), {QUrl::fromLocalFile(QStringLiteral("/$2"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$2"))}}, {true, QStringLiteral("$3"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist3"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 3, 3, QTime::fromMSecsSinceStartOfDay(3), {QUrl::fromLocalFile(QStringLiteral("/$3"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$3"))}}, {true, QStringLiteral("$4"), QStringLiteral("0"), QStringLiteral("track4"), QStringLiteral("artist4"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 4, 4, QTime::fromMSecsSinceStartOfDay(4), {QUrl::fromLocalFile(QStringLiteral("/$4"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$4"))}}, }; mNewTracks[QStringLiteral("album2")] = { {true, QStringLiteral("$5"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 1, 1, QTime::fromMSecsSinceStartOfDay(5), {QUrl::fromLocalFile(QStringLiteral("/$5"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$5"))}}, {true, QStringLiteral("$6"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 2, 1, QTime::fromMSecsSinceStartOfDay(6), {QUrl::fromLocalFile(QStringLiteral("/$6"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$6"))}}, {true, QStringLiteral("$7"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 3, 1, QTime::fromMSecsSinceStartOfDay(7), {QUrl::fromLocalFile(QStringLiteral("/$7"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$7"))}}, {true, QStringLiteral("$8"), QStringLiteral("0"), QStringLiteral("track4"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 4, 1, QTime::fromMSecsSinceStartOfDay(8), {QUrl::fromLocalFile(QStringLiteral("/$8"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$8"))}}, {true, QStringLiteral("$9"), QStringLiteral("0"), QStringLiteral("track5"), QStringLiteral("artist1"), QStringLiteral("album2"), QStringLiteral("artist1"), 5, 1, QTime::fromMSecsSinceStartOfDay(9), {QUrl::fromLocalFile(QStringLiteral("/$9"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$9"))}}, {true, QStringLiteral("$10"), QStringLiteral("0"), QStringLiteral("track6"), QStringLiteral("artist1 and artist2"), QStringLiteral("album2"), QStringLiteral("artist1"), 6, 1, QTime::fromMSecsSinceStartOfDay(10), {QUrl::fromLocalFile(QStringLiteral("/$10"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$10"))}} }; mNewTracks[QStringLiteral("album3")] = { {true, QStringLiteral("$11"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist2"), QStringLiteral("album3"), QStringLiteral("artist2"), 1, 1, QTime::fromMSecsSinceStartOfDay(11), {QUrl::fromLocalFile(QStringLiteral("/$11"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$11"))}}, {true, QStringLiteral("$12"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist2"), QStringLiteral("album3"), QStringLiteral("artist2"), 2, 1, QTime::fromMSecsSinceStartOfDay(12), {QUrl::fromLocalFile(QStringLiteral("/$12"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$12"))}}, {true, QStringLiteral("$13"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist2"), QStringLiteral("album3"), QStringLiteral("artist2"), 3, 1, QTime::fromMSecsSinceStartOfDay(13), {QUrl::fromLocalFile(QStringLiteral("/$13"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$13"))}} }; mNewTracks[QStringLiteral("album4")] = { {true, QStringLiteral("$14"), QStringLiteral("0"), QStringLiteral("track1"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 1, 1, QTime::fromMSecsSinceStartOfDay(14), {QUrl::fromLocalFile(QStringLiteral("/$14"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$14"))}}, {true, QStringLiteral("$15"), QStringLiteral("0"), QStringLiteral("track2"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 2, 1, QTime::fromMSecsSinceStartOfDay(15), {QUrl::fromLocalFile(QStringLiteral("/$15"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$15"))}}, {true, QStringLiteral("$16"), QStringLiteral("0"), QStringLiteral("track3"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 3, 1, QTime::fromMSecsSinceStartOfDay(16), {QUrl::fromLocalFile(QStringLiteral("/$16"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$16"))}}, {true, QStringLiteral("$17"), QStringLiteral("0"), QStringLiteral("track4"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 4, 1, QTime::fromMSecsSinceStartOfDay(17), {QUrl::fromLocalFile(QStringLiteral("/$17"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$17"))}}, {true, QStringLiteral("$18"), QStringLiteral("0"), QStringLiteral("track5"), QStringLiteral("artist2"), QStringLiteral("album4"), QStringLiteral("artist2"), 5, 1, QTime::fromMSecsSinceStartOfDay(18), {QUrl::fromLocalFile(QStringLiteral("/$18"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$18"))}} }; mNewCovers[QStringLiteral("album1")] = QUrl::fromLocalFile(QStringLiteral("album1")); mNewCovers[QStringLiteral("album2")] = QUrl::fromLocalFile(QStringLiteral("album2")); mNewCovers[QStringLiteral("album3")] = QUrl::fromLocalFile(QStringLiteral("album3")); mNewCovers[QStringLiteral("album4")] = QUrl::fromLocalFile(QStringLiteral("album4")); qRegisterMetaType>("QHash"); qRegisterMetaType>("QHash"); qRegisterMetaType>>("QHash>"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType("MusicArtist"); } void removeOneTrack() { auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); auto rootDirectory = QDir::root(); rootDirectory.mkpath(configDirectory.path()); auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); QFile dbFile(fileName); auto dbExists = dbFile.exists(); if (dbExists) { QCOMPARE(dbFile.remove(), true); } DatabaseInterface musicDb; AlbumModel albumsModel; connect(&musicDb, &DatabaseInterface::trackAdded, &albumsModel, &AlbumModel::trackAdded); connect(&musicDb, &DatabaseInterface::trackModified, &albumsModel, &AlbumModel::trackModified); connect(&musicDb, &DatabaseInterface::trackRemoved, &albumsModel, &AlbumModel::trackRemoved); musicDb.init(QStringLiteral("testDb")); QSignalSpy beginInsertRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&albumsModel, &AlbumModel::rowsInserted); QSignalSpy beginRemoveRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeRemoved); QSignalSpy endRemoveRowsSpy(&albumsModel, &AlbumModel::rowsRemoved); QSignalSpy dataChangedSpy(&albumsModel, &AlbumModel::dataChanged); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); musicDb.insertTracksList(mNewTracks, mNewCovers); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); albumsModel.setAlbumData(musicDb.albumFromTitle(QStringLiteral("album1"))); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); auto trackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track1"), QStringLiteral("album1"), QStringLiteral("artist1")); auto firstTrack = musicDb.trackFromDatabaseId(trackId); musicDb.removeTracksList({firstTrack.resourceURI()}); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 1); QCOMPARE(endRemoveRowsSpy.count(), 1); QCOMPARE(dataChangedSpy.count(), 0); } void removeOneAlbum() { auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); auto rootDirectory = QDir::root(); rootDirectory.mkpath(configDirectory.path()); auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); QFile dbFile(fileName); auto dbExists = dbFile.exists(); if (dbExists) { QCOMPARE(dbFile.remove(), true); } DatabaseInterface musicDb; AlbumModel albumsModel; connect(&musicDb, &DatabaseInterface::trackAdded, &albumsModel, &AlbumModel::trackAdded); connect(&musicDb, &DatabaseInterface::trackModified, &albumsModel, &AlbumModel::trackModified); connect(&musicDb, &DatabaseInterface::trackRemoved, &albumsModel, &AlbumModel::trackRemoved); musicDb.init(QStringLiteral("testDb")); QSignalSpy beginInsertRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&albumsModel, &AlbumModel::rowsInserted); QSignalSpy beginRemoveRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeRemoved); QSignalSpy endRemoveRowsSpy(&albumsModel, &AlbumModel::rowsRemoved); QSignalSpy dataChangedSpy(&albumsModel, &AlbumModel::dataChanged); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); musicDb.insertTracksList(mNewTracks, mNewCovers); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); albumsModel.setAlbumData(musicDb.albumFromTitle(QStringLiteral("album1"))); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); auto firstTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track1"), QStringLiteral("album1"), QStringLiteral("artist1")); auto firstTrack = musicDb.trackFromDatabaseId(firstTrackId); auto secondTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track2"), QStringLiteral("album1"), QStringLiteral("artist2")); auto secondTrack = musicDb.trackFromDatabaseId(secondTrackId); auto thirdTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track3"), QStringLiteral("album1"), QStringLiteral("artist3")); auto thirdTrack = musicDb.trackFromDatabaseId(thirdTrackId); auto fourthTrackId = musicDb.trackIdFromTitleAlbumArtist(QStringLiteral("track4"), QStringLiteral("album1"), QStringLiteral("artist4")); auto fourthTrack = musicDb.trackFromDatabaseId(fourthTrackId); musicDb.removeTracksList({firstTrack.resourceURI(), secondTrack.resourceURI(), thirdTrack.resourceURI(), fourthTrack.resourceURI()}); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 4); QCOMPARE(endRemoveRowsSpy.count(), 4); QCOMPARE(dataChangedSpy.count(), 0); } void addOneTrack() { auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); auto rootDirectory = QDir::root(); rootDirectory.mkpath(configDirectory.path()); auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); QFile dbFile(fileName); auto dbExists = dbFile.exists(); if (dbExists) { QCOMPARE(dbFile.remove(), true); } DatabaseInterface musicDb; AlbumModel albumsModel; connect(&musicDb, &DatabaseInterface::trackAdded, &albumsModel, &AlbumModel::trackAdded); connect(&musicDb, &DatabaseInterface::trackModified, &albumsModel, &AlbumModel::trackModified); connect(&musicDb, &DatabaseInterface::trackRemoved, &albumsModel, &AlbumModel::trackRemoved); musicDb.init(QStringLiteral("testDb")); QSignalSpy beginInsertRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeInserted); QSignalSpy endInsertRowsSpy(&albumsModel, &AlbumModel::rowsInserted); QSignalSpy beginRemoveRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeRemoved); QSignalSpy endRemoveRowsSpy(&albumsModel, &AlbumModel::rowsRemoved); QSignalSpy dataChangedSpy(&albumsModel, &AlbumModel::dataChanged); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); musicDb.insertTracksList(mNewTracks, mNewCovers); QCOMPARE(beginInsertRowsSpy.count(), 0); QCOMPARE(endInsertRowsSpy.count(), 0); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); albumsModel.setAlbumData(musicDb.albumFromTitle(QStringLiteral("album1"))); QCOMPARE(beginInsertRowsSpy.count(), 1); QCOMPARE(endInsertRowsSpy.count(), 1); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); auto newTrack = MusicAudioTrack{true, QStringLiteral("$19"), QStringLiteral("0"), QStringLiteral("track6"), QStringLiteral("artist2"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 6, 1, QTime::fromMSecsSinceStartOfDay(19), {QUrl::fromLocalFile(QStringLiteral("/$19"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$19"))}}; auto newTracks = QHash>(); newTracks[newTrack.albumName()].push_back(newTrack); musicDb.insertTracksList(newTracks, mNewCovers); QCOMPARE(beginInsertRowsSpy.count(), 2); QCOMPARE(endInsertRowsSpy.count(), 2); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(beginInsertRowsSpy.at(1).at(1).toInt(), 4); QCOMPARE(beginInsertRowsSpy.at(1).at(2).toInt(), 4); QCOMPARE(albumsModel.data(albumsModel.index(4, 0), AlbumModel::TitleRole).toString(), QStringLiteral("track6")); auto secondNewTrack = MusicAudioTrack{true, QStringLiteral("$20"), QStringLiteral("0"), QStringLiteral("track5"), QStringLiteral("artist2"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 5, 1, QTime::fromMSecsSinceStartOfDay(20), {QUrl::fromLocalFile(QStringLiteral("/$20"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$20"))}}; auto secondNewTracks = QHash>(); secondNewTracks[secondNewTrack.albumName()].push_back(secondNewTrack); musicDb.insertTracksList(secondNewTracks, mNewCovers); QCOMPARE(beginInsertRowsSpy.count(), 3); QCOMPARE(endInsertRowsSpy.count(), 3); QCOMPARE(beginRemoveRowsSpy.count(), 0); QCOMPARE(endRemoveRowsSpy.count(), 0); QCOMPARE(dataChangedSpy.count(), 0); QCOMPARE(beginInsertRowsSpy.at(2).at(1).toInt(), 4); QCOMPARE(beginInsertRowsSpy.at(2).at(2).toInt(), 4); QCOMPARE(albumsModel.data(albumsModel.index(4, 0), AlbumModel::TitleRole).toString(), QStringLiteral("track5")); } + + void modifyOneTrack() + { + auto configDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::QStandardPaths::AppDataLocation)); + auto rootDirectory = QDir::root(); + rootDirectory.mkpath(configDirectory.path()); + auto fileName = configDirectory.filePath(QStringLiteral("elisaMusicDatabase.sqlite")); + QFile dbFile(fileName); + auto dbExists = dbFile.exists(); + + if (dbExists) { + QCOMPARE(dbFile.remove(), true); + } + + DatabaseInterface musicDb; + AlbumModel albumsModel; + + connect(&musicDb, &DatabaseInterface::trackAdded, + &albumsModel, &AlbumModel::trackAdded); + connect(&musicDb, &DatabaseInterface::trackModified, + &albumsModel, &AlbumModel::trackModified); + connect(&musicDb, &DatabaseInterface::trackRemoved, + &albumsModel, &AlbumModel::trackRemoved); + + musicDb.init(QStringLiteral("testDb")); + + QSignalSpy beginInsertRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeInserted); + QSignalSpy endInsertRowsSpy(&albumsModel, &AlbumModel::rowsInserted); + QSignalSpy beginRemoveRowsSpy(&albumsModel, &AlbumModel::rowsAboutToBeRemoved); + QSignalSpy endRemoveRowsSpy(&albumsModel, &AlbumModel::rowsRemoved); + QSignalSpy dataChangedSpy(&albumsModel, &AlbumModel::dataChanged); + + QCOMPARE(beginInsertRowsSpy.count(), 0); + QCOMPARE(endInsertRowsSpy.count(), 0); + QCOMPARE(beginRemoveRowsSpy.count(), 0); + QCOMPARE(endRemoveRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + + musicDb.insertTracksList(mNewTracks, mNewCovers); + + QCOMPARE(beginInsertRowsSpy.count(), 0); + QCOMPARE(endInsertRowsSpy.count(), 0); + QCOMPARE(beginRemoveRowsSpy.count(), 0); + QCOMPARE(endRemoveRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + + albumsModel.setAlbumData(musicDb.albumFromTitle(QStringLiteral("album1"))); + + QCOMPARE(beginInsertRowsSpy.count(), 1); + QCOMPARE(endInsertRowsSpy.count(), 1); + QCOMPARE(beginRemoveRowsSpy.count(), 0); + QCOMPARE(endRemoveRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 0); + + auto modifiedTrack = MusicAudioTrack{true, QStringLiteral("$3"), QStringLiteral("0"), QStringLiteral("track3"), + QStringLiteral("artist3"), QStringLiteral("album1"), QStringLiteral("Various Artists"), 5, 3, + QTime::fromMSecsSinceStartOfDay(3), {QUrl::fromLocalFile(QStringLiteral("/$3"))}, {QUrl::fromLocalFile(QStringLiteral("file://image$3"))}}; + + musicDb.modifyTracksList({modifiedTrack}); + + QCOMPARE(beginInsertRowsSpy.count(), 1); + QCOMPARE(endInsertRowsSpy.count(), 1); + QCOMPARE(beginRemoveRowsSpy.count(), 0); + QCOMPARE(endRemoveRowsSpy.count(), 0); + QCOMPARE(dataChangedSpy.count(), 1); + + QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), albumsModel.index(2, 0)); + QCOMPARE(dataChangedSpy.at(0).at(1).toModelIndex(), albumsModel.index(2, 0)); + + QCOMPARE(albumsModel.data(albumsModel.index(2, 0), AlbumModel::TrackNumberRole).toInt(), 5); + } }; QTEST_MAIN(AlbumModelTests) #include "albummodeltest.moc" diff --git a/src/albummodel.cpp b/src/albummodel.cpp index b2f3cb8e..6b6ab269 100644 --- a/src/albummodel.cpp +++ b/src/albummodel.cpp @@ -1,348 +1,360 @@ /* * Copyright 2015-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "albummodel.h" #include "musicstatistics.h" #include "databaseinterface.h" #include #include #include #include class AlbumModelPrivate { public: AlbumModelPrivate() { } bool mUseLocalIcons = false; QString mTitle; QString mAuthor; MusicAlbum mCurrentAlbum; }; AlbumModel::AlbumModel(QObject *parent) : QAbstractItemModel(parent), d(new AlbumModelPrivate) { } AlbumModel::~AlbumModel() { delete d; } int AlbumModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return d->mCurrentAlbum.tracksCount(); } QHash AlbumModel::roleNames() const { QHash roles; roles[static_cast(ColumnsRoles::TitleRole)] = "title"; roles[static_cast(ColumnsRoles::DurationRole)] = "duration"; roles[static_cast(ColumnsRoles::ArtistRole)] = "artist"; roles[static_cast(ColumnsRoles::AlbumRole)] = "album"; roles[static_cast(ColumnsRoles::AlbumArtistRole)] = "albumArtist"; roles[static_cast(ColumnsRoles::TrackNumberRole)] = "trackNumber"; roles[static_cast(ColumnsRoles::DiscNumberRole)] = "discNumber"; roles[static_cast(ColumnsRoles::RatingRole)] = "rating"; roles[static_cast(ColumnsRoles::ImageRole)] = "image"; roles[static_cast(ColumnsRoles::DatabaseIdRole)] = "databaseId"; roles[static_cast(ColumnsRoles::DiscFirstTrackRole)] = "isFirstTrackOfDisc"; return roles; } Qt::ItemFlags AlbumModel::flags(const QModelIndex &index) const { if (!index.isValid()) { return Qt::NoItemFlags; } return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } QVariant AlbumModel::data(const QModelIndex &index, int role) const { auto result = QVariant(); if (!index.isValid()) { return result; } if (index.column() != 0) { return result; } if (index.row() < 0) { return result; } if (index.parent().isValid()) { return result; } if (index.row() >= d->mCurrentAlbum.tracksCount()) { return result; } const auto ¤tTrack = d->mCurrentAlbum.trackFromIndex(index.row()); if (!currentTrack.isValid()) { return result; } result = internalDataTrack(currentTrack, role, index.row()); return result; } QVariant AlbumModel::internalDataTrack(const MusicAudioTrack &track, int role, int rowIndex) const { auto result = QVariant(); ColumnsRoles convertedRole = static_cast(role); switch(convertedRole) { case ColumnsRoles::TitleRole: result = track.title(); break; case ColumnsRoles::MilliSecondsDurationRole: result = track.duration().msecsSinceStartOfDay(); break; case ColumnsRoles::DurationRole: { QTime trackDuration = track.duration(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(); } break; } case ColumnsRoles::CreatorRole: result = track.artist(); break; case ColumnsRoles::ArtistRole: result = track.artist(); break; case ColumnsRoles::AlbumRole: result = track.albumName(); break; case ColumnsRoles::AlbumArtistRole: result = track.albumArtist(); break; case ColumnsRoles::TrackNumberRole: result = track.trackNumber(); break; case ColumnsRoles::DiscNumberRole: if (track.discNumber() > 0) { result = track.discNumber(); } break; case ColumnsRoles::DiscFirstTrackRole: if (rowIndex == 0) { result = true; } else { auto previousTrack = d->mCurrentAlbum.trackFromIndex(rowIndex - 1); result = (previousTrack.discNumber() != track.discNumber()); } break; case ColumnsRoles::RatingRole: result = 0; break; case ColumnsRoles::ImageRole: { if (d->mCurrentAlbum.albumArtURI().isValid()) { result = d->mCurrentAlbum.albumArtURI(); } else { if (d->mUseLocalIcons) { result = QUrl(QStringLiteral("qrc:/media-optical-audio.svg")); } else { result = QUrl(QStringLiteral("image://icon/media-optical-audio")); } } break; } case ColumnsRoles::ResourceRole: result = track.resourceURI(); break; case ColumnsRoles::IdRole: result = track.title(); break; case ColumnsRoles::DatabaseIdRole: result = track.databaseId(); break; } return result; } QModelIndex AlbumModel::index(int row, int column, const QModelIndex &parent) const { auto result = QModelIndex(); if (column != 0) { return result; } if (parent.isValid()) { return result; } if (row >= d->mCurrentAlbum.tracksCount()) { return result; } return createIndex(row, column); } QModelIndex AlbumModel::parent(const QModelIndex &child) const { Q_UNUSED(child) return {}; } int AlbumModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } MusicAlbum AlbumModel::albumData() const { return d->mCurrentAlbum; } QString AlbumModel::title() const { return d->mTitle; } QString AlbumModel::author() const { return d->mAuthor; } void AlbumModel::setAlbumData(MusicAlbum album) { if (d->mCurrentAlbum == album) { return; } if (d->mCurrentAlbum.tracksCount() > 0) { beginRemoveRows({}, 0, d->mCurrentAlbum.tracksCount() - 1); d->mCurrentAlbum = {}; endRemoveRows(); } beginInsertRows({}, 0, album.tracksCount() - 1); d->mCurrentAlbum = album; endInsertRows(); Q_EMIT albumDataChanged(); } void AlbumModel::setTitle(QString title) { if (d->mTitle == title) return; d->mTitle = title; emit titleChanged(); } void AlbumModel::setAuthor(QString author) { if (d->mAuthor == author) return; d->mAuthor = author; emit authorChanged(); } void AlbumModel::trackAdded(MusicAudioTrack newTrack) { if (newTrack.albumName() != d->mCurrentAlbum.title()) { return; } auto trackIndex = d->mCurrentAlbum.trackIndexFromId(newTrack.databaseId()); if (trackIndex != -1) { return; } bool trackInserted = false; for (int trackIndex = 0; trackIndex < d->mCurrentAlbum.tracksCount(); ++trackIndex) { const auto &oneTrack = d->mCurrentAlbum.trackFromIndex(trackIndex); if (oneTrack.discNumber() == newTrack.discNumber() && oneTrack.trackNumber() > newTrack.trackNumber()) { beginInsertRows({}, trackIndex, trackIndex); d->mCurrentAlbum.insertTrack(newTrack, trackIndex); endInsertRows(); trackInserted = true; break; } } if (!trackInserted) { beginInsertRows({}, d->mCurrentAlbum.tracksCount(), d->mCurrentAlbum.tracksCount()); d->mCurrentAlbum.insertTrack(newTrack, d->mCurrentAlbum.tracksCount()); endInsertRows(); } } void AlbumModel::trackModified(MusicAudioTrack modifiedTrack) { + if (modifiedTrack.albumName() != d->mCurrentAlbum.title()) { + return; + } + + auto trackIndex = d->mCurrentAlbum.trackIndexFromId(modifiedTrack.databaseId()); + + if (trackIndex == -1) { + return; + } + + d->mCurrentAlbum.updateTrack(modifiedTrack, trackIndex); + Q_EMIT dataChanged(index(trackIndex, 0), index(trackIndex, 0)); } void AlbumModel::trackRemoved(MusicAudioTrack removedTrack) { if (removedTrack.albumName() != d->mCurrentAlbum.title()) { return; } auto trackIndex = d->mCurrentAlbum.trackIndexFromId(removedTrack.databaseId()); if (trackIndex == -1) { return; } beginRemoveRows({}, trackIndex, trackIndex); d->mCurrentAlbum.removeTrackFromIndex(trackIndex); endRemoveRows(); } #include "moc_albummodel.cpp" diff --git a/src/databaseinterface.cpp b/src/databaseinterface.cpp index d08dbeec..cd7ddb0e 100644 --- a/src/databaseinterface.cpp +++ b/src/databaseinterface.cpp @@ -1,1678 +1,1762 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "databaseinterface.h" #include #include #include #include #include #include #include #include #include #include class DatabaseInterfacePrivate { public: DatabaseInterfacePrivate(QSqlDatabase tracksDatabase) : mTracksDatabase(tracksDatabase), mSelectAlbumQuery(mTracksDatabase), mSelectTrackQuery(mTracksDatabase), mSelectAlbumIdFromTitleQuery(mTracksDatabase), mInsertAlbumQuery(mTracksDatabase), mSelectTrackIdFromTitleAlbumIdArtistQuery(mTracksDatabase), mInsertTrackQuery(mTracksDatabase), mSelectAlbumTrackCountQuery(mTracksDatabase), mUpdateAlbumQuery(mTracksDatabase), mSelectTracksFromArtist(mTracksDatabase), mSelectTrackFromIdQuery(mTracksDatabase), mSelectCountAlbumsForArtistQuery(mTracksDatabase), mSelectTrackIdFromTitleAlbumArtistQuery(mTracksDatabase), mSelectAllAlbumsQuery(mTracksDatabase), mSelectAllAlbumsFromArtistQuery(mTracksDatabase), mSelectAllArtistsQuery(mTracksDatabase), mInsertArtistsQuery(mTracksDatabase), mSelectArtistByNameQuery(mTracksDatabase), mSelectArtistQuery(mTracksDatabase), mSelectTrackFromFilePathQuery(mTracksDatabase), mRemoveTrackQuery(mTracksDatabase), mRemoveAlbumQuery(mTracksDatabase), mRemoveArtistQuery(mTracksDatabase) { } QSqlDatabase mTracksDatabase; QSqlQuery mSelectAlbumQuery; QSqlQuery mSelectTrackQuery; QSqlQuery mSelectAlbumIdFromTitleQuery; QSqlQuery mInsertAlbumQuery; QSqlQuery mSelectTrackIdFromTitleAlbumIdArtistQuery; QSqlQuery mInsertTrackQuery; QSqlQuery mSelectAlbumTrackCountQuery; QSqlQuery mUpdateAlbumQuery; QSqlQuery mSelectTracksFromArtist; QSqlQuery mSelectTrackFromIdQuery; QSqlQuery mSelectCountAlbumsForArtistQuery; QSqlQuery mSelectTrackIdFromTitleAlbumArtistQuery; QSqlQuery mSelectAllAlbumsQuery; QSqlQuery mSelectAllAlbumsFromArtistQuery; QSqlQuery mSelectAllArtistsQuery; QSqlQuery mInsertArtistsQuery; QSqlQuery mSelectArtistByNameQuery; QSqlQuery mSelectArtistQuery; QSqlQuery mSelectTrackFromFilePathQuery; QSqlQuery mRemoveTrackQuery; QSqlQuery mRemoveAlbumQuery; QSqlQuery mRemoveArtistQuery; qulonglong mAlbumId = 1; qulonglong mArtistId = 1; qulonglong mTrackId = 1; bool mInitFinished = false; }; DatabaseInterface::DatabaseInterface(QObject *parent) : QObject(parent), d(nullptr) { } DatabaseInterface::~DatabaseInterface() { if (d) { d->mTracksDatabase.close(); } delete d; } void DatabaseInterface::init(const QString &dbName) { QSqlDatabase tracksDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName); 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) { qDebug() << "database open"; } else { qDebug() << "database not open"; } qDebug() << "DatabaseInterface::init" << (tracksDatabase.driver()->hasFeature(QSqlDriver::Transactions) ? "yes" : "no"); d = new DatabaseInterfacePrivate(tracksDatabase); initDatabase(); initRequest(); } MusicAlbum DatabaseInterface::albumFromTitle(QString title) const { auto result = MusicAlbum(); auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalAlbumFromTitle(title); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } QVector DatabaseInterface::allAlbums() const { auto result = QVector(); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } auto queryResult = d->mSelectAllAlbumsQuery.exec(); if (!queryResult || !d->mSelectAllAlbumsQuery.isSelect() || !d->mSelectAllAlbumsQuery.isActive()) { qDebug() << "DatabaseInterface::allAlbums" << d->mSelectAllAlbumsQuery.lastQuery(); qDebug() << "DatabaseInterface::allAlbums" << d->mSelectAllAlbumsQuery.boundValues(); qDebug() << "DatabaseInterface::allAlbums" << d->mSelectAllAlbumsQuery.lastError(); return result; } while(d->mSelectAllAlbumsQuery.next()) { auto newAlbum = MusicAlbum(); newAlbum.setDatabaseId(d->mSelectAllAlbumsQuery.record().value(0).toULongLong()); newAlbum.setTitle(d->mSelectAllAlbumsQuery.record().value(1).toString()); newAlbum.setId(d->mSelectAllAlbumsQuery.record().value(2).toString()); newAlbum.setArtist(d->mSelectAllAlbumsQuery.record().value(3).toString()); newAlbum.setAlbumArtURI(d->mSelectAllAlbumsQuery.record().value(4).toUrl()); newAlbum.setTracksCount(d->mSelectAllAlbumsQuery.record().value(5).toInt()); newAlbum.setIsSingleDiscAlbum(d->mSelectAllAlbumsQuery.record().value(6).toBool()); newAlbum.setTracks(fetchTracks(newAlbum.databaseId())); newAlbum.setTrackIds(fetchTrackIds(newAlbum.databaseId())); newAlbum.setValid(true); result.push_back(newAlbum); } d->mSelectAllAlbumsQuery.finish(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } QVector DatabaseInterface::allArtists() const { auto result = QVector(); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } QString currentFilter(QStringLiteral("%%1%")); auto queryResult = d->mSelectAllArtistsQuery.exec(); if (!queryResult || !d->mSelectAllArtistsQuery.isSelect() || !d->mSelectAllArtistsQuery.isActive()) { qDebug() << "DatabaseInterface::allArtists" << d->mSelectAllArtistsQuery.lastQuery(); qDebug() << "DatabaseInterface::allArtists" << d->mSelectAllArtistsQuery.boundValues(); qDebug() << "DatabaseInterface::allArtists" << d->mSelectAllArtistsQuery.lastError(); d->mSelectAllArtistsQuery.finish(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } while(d->mSelectAllArtistsQuery.next()) { auto newArtist = MusicArtist(); newArtist.setDatabaseId(d->mSelectAllArtistsQuery.record().value(0).toULongLong()); newArtist.setName(d->mSelectAllArtistsQuery.record().value(1).toString()); newArtist.setValid(true); d->mSelectCountAlbumsForArtistQuery.bindValue(QStringLiteral(":artistName"), newArtist.name()); auto queryResult = d->mSelectCountAlbumsForArtistQuery.exec(); if (!queryResult || !d->mSelectCountAlbumsForArtistQuery.isSelect() || !d->mSelectCountAlbumsForArtistQuery.isActive() || !d->mSelectCountAlbumsForArtistQuery.next()) { qDebug() << "DatabaseInterface::allArtists" << d->mSelectCountAlbumsForArtistQuery.lastQuery(); qDebug() << "DatabaseInterface::allArtists" << d->mSelectCountAlbumsForArtistQuery.boundValues(); qDebug() << "DatabaseInterface::allArtists" << d->mSelectCountAlbumsForArtistQuery.lastError(); d->mSelectCountAlbumsForArtistQuery.finish(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } newArtist.setAlbumsCount(d->mSelectCountAlbumsForArtistQuery.record().value(0).toInt()); d->mSelectCountAlbumsForArtistQuery.finish(); result.push_back(newArtist); } d->mSelectAllArtistsQuery.finish(); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } QVector DatabaseInterface::tracksFromAuthor(QString artistName) const { auto allTracks = QVector(); auto transactionResult = startTransaction(); if (!transactionResult) { return allTracks; } allTracks = internalTracksFromAuthor(artistName); transactionResult = finishTransaction(); if (!transactionResult) { return allTracks; } return allTracks; } MusicArtist DatabaseInterface::internalArtistFromId(qulonglong artistId) const { auto result = MusicArtist(); if (!d || !d->mTracksDatabase.isValid() || !d->mInitFinished) { return result; } d->mSelectArtistQuery.bindValue(QStringLiteral(":artistId"), artistId); auto queryResult = d->mSelectArtistQuery.exec(); if (!queryResult || !d->mSelectArtistQuery.isSelect() || !d->mSelectArtistQuery.isActive()) { qDebug() << "DatabaseInterface::internalArtistFromId" << d->mSelectArtistQuery.lastQuery(); qDebug() << "DatabaseInterface::internalArtistFromId" << d->mSelectArtistQuery.boundValues(); qDebug() << "DatabaseInterface::internalArtistFromId" << d->mSelectArtistQuery.lastError(); d->mSelectArtistQuery.finish(); return result; } if (!d->mSelectArtistQuery.next()) { d->mSelectArtistQuery.finish(); return result; } result.setDatabaseId(d->mSelectArtistQuery.record().value(0).toULongLong()); result.setName(d->mSelectArtistQuery.record().value(1).toString()); result.setValid(true); d->mSelectArtistQuery.finish(); d->mSelectCountAlbumsForArtistQuery.bindValue(QStringLiteral(":artistName"), result.name()); queryResult = d->mSelectCountAlbumsForArtistQuery.exec(); if (!queryResult || !d->mSelectCountAlbumsForArtistQuery.isSelect() || !d->mSelectCountAlbumsForArtistQuery.isActive() || !d->mSelectCountAlbumsForArtistQuery.next()) { qDebug() << "DatabaseInterface::internalArtistFromId" << d->mSelectCountAlbumsForArtistQuery.lastQuery(); qDebug() << "DatabaseInterface::internalArtistFromId" << d->mSelectCountAlbumsForArtistQuery.boundValues(); qDebug() << "DatabaseInterface::internalArtistFromId" << d->mSelectCountAlbumsForArtistQuery.lastError(); d->mSelectCountAlbumsForArtistQuery.finish(); return result; } result.setAlbumsCount(d->mSelectCountAlbumsForArtistQuery.record().value(0).toInt()); d->mSelectCountAlbumsForArtistQuery.finish(); return result; } MusicAudioTrack DatabaseInterface::trackFromDatabaseId(qulonglong id) const { auto result = MusicAudioTrack(); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } result = internalTrackFromDatabaseId(id); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } qulonglong DatabaseInterface::trackIdFromTitleAlbumArtist(QString title, QString album, QString artist) const { auto result = qulonglong(0); if (!d) { return result; } auto transactionResult = startTransaction(); if (!transactionResult) { return result; } - d->mSelectTrackIdFromTitleAlbumArtistQuery.bindValue(QStringLiteral(":title"), title); - d->mSelectTrackIdFromTitleAlbumArtistQuery.bindValue(QStringLiteral(":album"), album); - d->mSelectTrackIdFromTitleAlbumArtistQuery.bindValue(QStringLiteral(":artist"), artist); - - auto queryResult = d->mSelectTrackIdFromTitleAlbumArtistQuery.exec(); - - if (!queryResult || !d->mSelectTrackIdFromTitleAlbumArtistQuery.isSelect() || !d->mSelectTrackIdFromTitleAlbumArtistQuery.isActive()) { - qDebug() << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumArtistQuery.lastQuery(); - qDebug() << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumArtistQuery.boundValues(); - qDebug() << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumArtistQuery.lastError(); - - d->mSelectTrackIdFromTitleAlbumArtistQuery.finish(); - - transactionResult = finishTransaction(); - if (!transactionResult) { - return result; - } - } - - if (d->mSelectTrackIdFromTitleAlbumArtistQuery.next()) { - result = d->mSelectTrackIdFromTitleAlbumArtistQuery.record().value(0).toInt(); - } - - d->mSelectTrackIdFromTitleAlbumArtistQuery.finish(); + result = internalTrackIdFromTitleAlbumArtist(title, album, artist); transactionResult = finishTransaction(); if (!transactionResult) { return result; } return result; } void DatabaseInterface::insertTracksList(QHash > tracks, QHash covers) { auto transactionResult = startTransaction(); if (!transactionResult) { return; } auto newAddedAlbums = QVector(); auto newTracks = QVector(); auto maximumAlbumId = qulonglong(0); quintptr albumId = 0; for (const auto &album : tracks) { bool albumIsNew = false; bool albumIsModified = false; MusicAlbum newAlbum; int previousDiscNumber = album[0].discNumber(); for(const auto &track : album) { if (newAlbum.artist().isNull() && !track.albumArtist().isEmpty()) { newAlbum.setArtist(track.albumArtist()); } if (!track.albumArtist().isEmpty() && newAlbum.artist() != track.albumArtist()) { newAlbum.setArtist(i18nc("Artist name for albums with more than one artist (like compilations", "Various Artists")); } if (newAlbum.title().isNull()) { newAlbum.setTitle(track.albumName()); } if (newAlbum.albumArtURI().isEmpty()) { newAlbum.setAlbumArtURI(covers[track.albumName()]); } if (previousDiscNumber != track.discNumber()) { newAlbum.setIsSingleDiscAlbum(false); } } if (newAlbum.artist().isEmpty()) { continue; } newAlbum.setTracksCount(album.size()); d->mSelectAlbumIdFromTitleQuery.bindValue(QStringLiteral(":title"), newAlbum.title()); d->mSelectAlbumIdFromTitleQuery.bindValue(QStringLiteral(":artistId"), insertArtist(newAlbum.artist())); auto result = d->mSelectAlbumIdFromTitleQuery.exec(); if (!result || !d->mSelectAlbumIdFromTitleQuery.isSelect() || !d->mSelectAlbumIdFromTitleQuery.isActive()) { qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectAlbumIdFromTitleQuery.lastQuery(); qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectAlbumIdFromTitleQuery.boundValues(); qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectAlbumIdFromTitleQuery.lastError(); rollBackTransaction(); return; } if (d->mSelectAlbumIdFromTitleQuery.next()) { albumId = d->mSelectAlbumIdFromTitleQuery.record().value(0).toULongLong(); maximumAlbumId = std::max(maximumAlbumId, d->mSelectAlbumIdFromTitleQuery.record().value(0).toULongLong()); d->mSelectAlbumIdFromTitleQuery.finish(); albumIsModified = true; } else { d->mSelectAlbumIdFromTitleQuery.finish(); d->mInsertAlbumQuery.bindValue(QStringLiteral(":albumId"), d->mAlbumId); d->mInsertAlbumQuery.bindValue(QStringLiteral(":title"), newAlbum.title()); d->mInsertAlbumQuery.bindValue(QStringLiteral(":artistId"), insertArtist(newAlbum.artist())); d->mInsertAlbumQuery.bindValue(QStringLiteral(":coverFileName"), newAlbum.albumArtURI()); d->mInsertAlbumQuery.bindValue(QStringLiteral(":tracksCount"), newAlbum.tracksCount()); d->mInsertAlbumQuery.bindValue(QStringLiteral(":isSingleDiscAlbum"), newAlbum.isSingleDiscAlbum()); result = d->mInsertAlbumQuery.exec(); if (result && d->mInsertAlbumQuery.isActive()) { newAddedAlbums.push_back(d->mAlbumId); albumIsNew = true; ++d->mAlbumId; } else { qDebug() << "DatabaseInterface::insertTracksList" << "error during album insert"; qDebug() << "DatabaseInterface::insertTracksList" << d->mInsertAlbumQuery.lastQuery(); qDebug() << "DatabaseInterface::insertTracksList" << d->mInsertAlbumQuery.boundValues(); qDebug() << "DatabaseInterface::insertTracksList" << d->mInsertAlbumQuery.lastError(); d->mInsertAlbumQuery.finish(); rollBackTransaction(); return; } d->mInsertAlbumQuery.finish(); result = d->mSelectAlbumIdFromTitleQuery.exec(); if (!result || !d->mSelectAlbumIdFromTitleQuery.isSelect() || !d->mSelectAlbumIdFromTitleQuery.isActive()) { qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectAlbumIdFromTitleQuery.lastQuery(); qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectAlbumIdFromTitleQuery.boundValues(); qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectAlbumIdFromTitleQuery.lastError(); d->mSelectAlbumIdFromTitleQuery.finish(); rollBackTransaction(); return; } if (d->mSelectAlbumIdFromTitleQuery.next()) { albumId = d->mSelectAlbumIdFromTitleQuery.record().value(0).toULongLong(); maximumAlbumId = std::max(maximumAlbumId, d->mSelectAlbumIdFromTitleQuery.record().value(0).toULongLong()); } d->mSelectAlbumIdFromTitleQuery.finish(); } for(const auto &track : album) { QString artistName = track.artist(); if (artistName.isEmpty()) { artistName = newAlbum.artist(); continue; } d->mSelectTrackIdFromTitleAlbumIdArtistQuery.bindValue(QStringLiteral(":title"), track.title()); d->mSelectTrackIdFromTitleAlbumIdArtistQuery.bindValue(QStringLiteral(":album"), albumId); d->mSelectTrackIdFromTitleAlbumIdArtistQuery.bindValue(QStringLiteral(":artist"), artistName); result = d->mSelectTrackIdFromTitleAlbumIdArtistQuery.exec(); if (!result || !d->mSelectTrackIdFromTitleAlbumIdArtistQuery.isSelect() || !d->mSelectTrackIdFromTitleAlbumIdArtistQuery.isActive()) { qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectTrackIdFromTitleAlbumIdArtistQuery.lastQuery(); qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectTrackIdFromTitleAlbumIdArtistQuery.boundValues(); qDebug() << "DatabaseInterface::insertTracksList" << d->mSelectTrackIdFromTitleAlbumIdArtistQuery.lastError(); rollBackTransaction(); return; } if (d->mSelectTrackIdFromTitleAlbumIdArtistQuery.next()) { d->mTrackId = std::max(d->mTrackId, d->mSelectTrackIdFromTitleAlbumIdArtistQuery.record().value(0).toULongLong() + 1); d->mSelectTrackIdFromTitleAlbumIdArtistQuery.finish(); continue; } else { d->mSelectTrackIdFromTitleAlbumIdArtistQuery.finish(); d->mInsertTrackQuery.bindValue(QStringLiteral(":trackId"), d->mTrackId); d->mInsertTrackQuery.bindValue(QStringLiteral(":title"), track.title()); d->mInsertTrackQuery.bindValue(QStringLiteral(":album"), albumId); d->mInsertTrackQuery.bindValue(QStringLiteral(":artistId"), insertArtist(artistName)); d->mInsertTrackQuery.bindValue(QStringLiteral(":fileName"), track.resourceURI().toString()); d->mInsertTrackQuery.bindValue(QStringLiteral(":trackNumber"), track.trackNumber()); d->mInsertTrackQuery.bindValue(QStringLiteral(":discNumber"), track.discNumber()); d->mInsertTrackQuery.bindValue(QStringLiteral(":trackDuration"), QVariant::fromValue(track.duration().msecsSinceStartOfDay())); result = d->mInsertTrackQuery.exec(); if (result && d->mInsertTrackQuery.isActive()) { newTracks.push_back(d->mTrackId); Q_EMIT trackAdded(internalTrackFromDatabaseId(d->mTrackId)); ++d->mTrackId; } else { qDebug() << "DatabaseInterface::insertTracksList" << d->mInsertTrackQuery.lastQuery(); qDebug() << "DatabaseInterface::insertTracksList" << d->mInsertTrackQuery.boundValues(); qDebug() << "DatabaseInterface::insertTracksList" << d->mInsertTrackQuery.lastError(); rollBackTransaction(); return; } d->mInsertTrackQuery.finish(); } } if (albumIsNew) { Q_EMIT albumAdded(internalAlbumFromId(albumId)); } if (albumIsModified) { Q_EMIT albumModified(internalAlbumFromId(albumId)); } } transactionResult = finishTransaction(); if (!transactionResult) { return; } } void DatabaseInterface::removeTracksList(const QList removedTracks) { auto transactionResult = startTransaction(); if (!transactionResult) { return; } QList willRemoveTask; for (auto removedTrackFileName : removedTracks) { d->mSelectTrackFromFilePathQuery.bindValue(QStringLiteral(":filePath"), removedTrackFileName.toString()); auto result = d->mSelectTrackFromFilePathQuery.exec(); if (!result || !d->mSelectTrackFromFilePathQuery.isSelect() || !d->mSelectTrackFromFilePathQuery.isActive()) { qDebug() << "DatabaseInterface::removeTracksList" << d->mSelectTrackFromFilePathQuery.lastQuery(); qDebug() << "DatabaseInterface::removeTracksList" << d->mSelectTrackFromFilePathQuery.boundValues(); qDebug() << "DatabaseInterface::removeTracksList" << d->mSelectTrackFromFilePathQuery.lastError(); continue; } while (d->mSelectTrackFromFilePathQuery.next()) { MusicAudioTrack removedTrack; removedTrack.setDatabaseId(d->mSelectTrackFromFilePathQuery.record().value(0).toULongLong()); removedTrack.setTitle(d->mSelectTrackFromFilePathQuery.record().value(1).toString()); removedTrack.setParentId(d->mSelectTrackFromFilePathQuery.record().value(2).toString()); removedTrack.setArtist(d->mSelectTrackFromFilePathQuery.record().value(3).toString()); removedTrack.setAlbumArtist(d->mSelectTrackFromFilePathQuery.record().value(4).toString()); removedTrack.setResourceURI(d->mSelectTrackFromFilePathQuery.record().value(5).toUrl()); removedTrack.setTrackNumber(d->mSelectTrackFromFilePathQuery.record().value(6).toInt()); removedTrack.setDiscNumber(d->mSelectTrackFromFilePathQuery.record().value(7).toInt()); removedTrack.setDuration(QTime::fromMSecsSinceStartOfDay(d->mSelectTrackFromFilePathQuery.record().value(8).toInt())); removedTrack.setAlbumName(d->mSelectTrackFromFilePathQuery.record().value(9).toString()); removedTrack.setValid(true); willRemoveTask.push_back(removedTrack); } d->mSelectTrackFromFilePathQuery.finish(); } for (auto oneRemovedTrack : willRemoveTask) { removeTrackInDatabase(oneRemovedTrack.databaseId()); Q_EMIT trackRemoved(oneRemovedTrack); const auto &modifiedAlbum = internalAlbumFromTitle(oneRemovedTrack.albumName()); const auto &allArtistTracks = internalTracksFromAuthor(oneRemovedTrack.artist()); const auto &removedArtistId = internalArtistIdFromName(oneRemovedTrack.artist()); const auto &removedArtist = internalArtistFromId(removedArtistId); if (modifiedAlbum.isValid() && !modifiedAlbum.isEmpty()) { Q_EMIT albumModified(internalAlbumFromId(modifiedAlbum.databaseId())); } if (modifiedAlbum.isValid() && modifiedAlbum.isEmpty()) { removeAlbumInDatabase(modifiedAlbum.databaseId()); Q_EMIT albumRemoved(modifiedAlbum); } if (allArtistTracks.isEmpty()) { removeArtistInDatabase(removedArtistId); Q_EMIT artistRemoved(removedArtist); } } transactionResult = finishTransaction(); if (!transactionResult) { return; } } +void DatabaseInterface::modifyTracksList(const QList modifiedTracks) +{ + auto transactionResult = startTransaction(); + if (!transactionResult) { + return; + } + + for (const auto &oneModifiedTrack : modifiedTracks) { + auto originTrackId = internalTrackIdFromTitleAlbumArtist(oneModifiedTrack.title(), oneModifiedTrack.albumName(), oneModifiedTrack.artist()); + + if (originTrackId == 0) { + continue; + } + + auto originTrack = internalTrackFromDatabaseId(originTrackId); + + if (!originTrack.isValid()) { + continue; + } + + d->mSelectAlbumIdFromTitleQuery.bindValue(QStringLiteral(":title"), oneModifiedTrack.albumName()); + d->mSelectAlbumIdFromTitleQuery.bindValue(QStringLiteral(":artistId"), insertArtist(oneModifiedTrack.albumArtist())); + + auto result = d->mSelectAlbumIdFromTitleQuery.exec(); + + if (!result || !d->mSelectAlbumIdFromTitleQuery.isSelect() || !d->mSelectAlbumIdFromTitleQuery.isActive()) { + qDebug() << "DatabaseInterface::modifyTracksList" << d->mSelectAlbumIdFromTitleQuery.lastQuery(); + qDebug() << "DatabaseInterface::modifyTracksList" << d->mSelectAlbumIdFromTitleQuery.boundValues(); + qDebug() << "DatabaseInterface::modifyTracksList" << d->mSelectAlbumIdFromTitleQuery.lastError(); + + continue; + } + + if (!d->mSelectAlbumIdFromTitleQuery.next()) { + continue; + } + + auto albumId = d->mSelectAlbumIdFromTitleQuery.record().value(0).toULongLong(); + d->mSelectAlbumIdFromTitleQuery.finish(); + + removeTrackInDatabase(originTrackId); + + d->mInsertTrackQuery.bindValue(QStringLiteral(":trackId"), originTrackId); + d->mInsertTrackQuery.bindValue(QStringLiteral(":title"), oneModifiedTrack.title()); + d->mInsertTrackQuery.bindValue(QStringLiteral(":album"), albumId); + d->mInsertTrackQuery.bindValue(QStringLiteral(":artistId"), insertArtist(oneModifiedTrack.artist())); + d->mInsertTrackQuery.bindValue(QStringLiteral(":fileName"), oneModifiedTrack.resourceURI().toString()); + d->mInsertTrackQuery.bindValue(QStringLiteral(":trackNumber"), oneModifiedTrack.trackNumber()); + d->mInsertTrackQuery.bindValue(QStringLiteral(":discNumber"), oneModifiedTrack.discNumber()); + d->mInsertTrackQuery.bindValue(QStringLiteral(":trackDuration"), QVariant::fromValue(oneModifiedTrack.duration().msecsSinceStartOfDay())); + + result = d->mInsertTrackQuery.exec(); + + if (result && d->mInsertTrackQuery.isActive()) { + d->mInsertTrackQuery.finish(); + + Q_EMIT trackModified(internalTrackFromDatabaseId(originTrackId)); + } else { + d->mInsertTrackQuery.finish(); + + qDebug() << "DatabaseInterface::modifyTracksList" << d->mInsertTrackQuery.lastQuery(); + qDebug() << "DatabaseInterface::modifyTracksList" << d->mInsertTrackQuery.boundValues(); + qDebug() << "DatabaseInterface::modifyTracksList" << d->mInsertTrackQuery.lastError(); + + continue; + } + } + + transactionResult = finishTransaction(); + if (!transactionResult) { + return; + } +} + bool DatabaseInterface::startTransaction() const { auto result = false; auto transactionResult = d->mTracksDatabase.transaction(); if (!transactionResult) { qDebug() << "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) { qDebug() << "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) { qDebug() << "commit failed" << d->mTracksDatabase.lastError() << d->mTracksDatabase.lastError().nativeErrorCode(); return result; } result = true; return result; } void DatabaseInterface::initDatabase() const { auto transactionResult = startTransaction(); if (!transactionResult) { return; } if (!d->mTracksDatabase.tables().contains(QStringLiteral("DiscoverSource"))) { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DiscoverSource` (`ID` INTEGER PRIMARY KEY NOT NULL, " "`UUID` VARCHAR(55) NOT NULL, " "UNIQUE (`UUID`))")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createSchemaQuery.lastError() << createSchemaQuery.lastError().nativeErrorCode(); } } if (!d->mTracksDatabase.tables().contains(QStringLiteral("Artists"))) { 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) { qDebug() << "DatabaseInterface::initDatabase" << createSchemaQuery.lastQuery(); qDebug() << "DatabaseInterface::initDatabase" << createSchemaQuery.lastError(); } } if (!d->mTracksDatabase.tables().contains(QStringLiteral("Albums"))) { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Albums` (" "`ID` INTEGER PRIMARY KEY NOT NULL, " "`Title` VARCHAR(55) NOT NULL, " "`ArtistID` INTEGER NOT NULL, " "`CoverFileName` VARCHAR(255) NOT NULL, " "`TracksCount` INTEGER NOT NULL, " "`IsSingleDiscAlbum` BOOLEAN NOT NULL, " "`AlbumInternalID` VARCHAR(55), " "UNIQUE (`Title`, `ArtistID`), " "CONSTRAINT fk_albums_artist FOREIGN KEY (`ArtistID`) REFERENCES `Artists`(`ID`))")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createSchemaQuery.lastError(); } } if (!d->mTracksDatabase.tables().contains(QStringLiteral("Tracks"))) { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Tracks` (" "`ID` INTEGER PRIMARY KEY NOT NULL, " "`Title` VARCHAR(85) NOT NULL, " "`AlbumID` INTEGER NOT NULL, " "`ArtistID` INTEGER NOT NULL, " "`FileName` VARCHAR(255) NOT NULL, " "`TrackNumber` INTEGER NOT NULL, " "`DiscNumber` INTEGER, " "`Duration` INTEGER NOT NULL, " "UNIQUE (`Title`, `AlbumID`, `ArtistID`), " "CONSTRAINT fk_tracks_album FOREIGN KEY (`AlbumID`) REFERENCES `Albums`(`ID`), " "CONSTRAINT fk_tracks_artist FOREIGN KEY (`ArtistID`) REFERENCES `Artists`(`ID`))")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createSchemaQuery.lastError(); } } if (!d->mTracksDatabase.tables().contains(QStringLiteral("TracksMapping"))) { QSqlQuery createSchemaQuery(d->mTracksDatabase); const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `TracksMapping` (" "`TrackID` INTEGER NOT NULL, " "`DiscoverID` INTEGER NOT NULL, " "PRIMARY KEY (`TrackID`, `DiscoverID`), " "CONSTRAINT fk_tracksmapping_trackID FOREIGN KEY (`TrackID`) REFERENCES `Tracks`(`ID`), " "CONSTRAINT fk_tracksmapping_discoverID FOREIGN KEY (`DiscoverID`) REFERENCES `DiscoverSource`(`ID`))")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createSchemaQuery.lastError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksAlbumIndex` ON `Tracks` " "(`AlbumID`)")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createTrackIndex.lastError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`AlbumsArtistIndex` ON `Albums` " "(`ArtistID`)")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createTrackIndex.lastError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksFileNameIndex` ON `Tracks` " "(`FileName`)")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createTrackIndex.lastError(); } } { QSqlQuery createTrackIndex(d->mTracksDatabase); const auto &result = createTrackIndex.exec(QStringLiteral("CREATE INDEX " "IF NOT EXISTS " "`TracksArtistIDAlbumIDFileNameIndex` ON `Tracks` " "(`ArtistID`, `AlbumID`, `FileName`)")); if (!result) { qDebug() << "DatabaseInterface::initDatabase" << createTrackIndex.lastError(); } } transactionResult = finishTransaction(); if (!transactionResult) { return; } } void DatabaseInterface::initRequest() { auto transactionResult = startTransaction(); if (!transactionResult) { return; } { auto selectAlbumQueryText = QStringLiteral("SELECT " "album.`ID`, " "album.`Title`, " "album.`AlbumInternalID`, " "artist.`Name`, " "album.`CoverFileName`, " "album.`TracksCount`, " "album.`IsSingleDiscAlbum` " "FROM `Albums` album, `Artists` artist " "WHERE " "album.`ID` = :albumId AND " "artist.`ID` = album.`ArtistID`"); auto result = d->mSelectAlbumQuery.prepare(selectAlbumQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectAlbumQuery.lastError(); } } { auto selectAllAlbumsText = QStringLiteral("SELECT album.`ID`, " "album.`Title`, " "album.`AlbumInternalID`, " "artist.`Name`, " "album.`CoverFileName`, " "album.`TracksCount`, " "album.`IsSingleDiscAlbum` " "FROM `Albums` album, `Artists` artist " "WHERE " "artist.`ID` = album.`ArtistID` " "ORDER BY album.`Title`"); auto result = d->mSelectAllAlbumsQuery.prepare(selectAllAlbumsText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << selectAllAlbumsText << d->mSelectAllAlbumsQuery.lastError(); qDebug() << d->mTracksDatabase.lastError(); } } { auto selectAllArtistsWithFilterText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Artists`"); auto result = d->mSelectAllArtistsQuery.prepare(selectAllArtistsWithFilterText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << selectAllArtistsWithFilterText << d->mSelectAllArtistsQuery.lastError(); qDebug() << d->mTracksDatabase.lastError(); } } { auto selectArtistByNameText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Artists` " "WHERE " "`Name` = :name"); auto result = d->mSelectArtistByNameQuery.prepare(selectArtistByNameText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << selectArtistByNameText << d->mSelectArtistByNameQuery.lastError(); qDebug() << d->mTracksDatabase.lastError(); } } { auto insertArtistsText = QStringLiteral("INSERT INTO `Artists` (`ID`, `Name`) " "VALUES (:artistId, :name)"); auto result = d->mInsertArtistsQuery.prepare(insertArtistsText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << insertArtistsText << d->mInsertArtistsQuery.lastError(); qDebug() << d->mTracksDatabase.lastError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "tracks.`AlbumID`, " "artist.`Name`, " "artistAlbum.`Name`, " "tracks.`FileName`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration` " "FROM `Tracks` tracks, `Artists` artist, `Artists` artistAlbum, `Albums` album " "WHERE " "tracks.`AlbumID` = :albumId AND " "artist.`ID` = tracks.`ArtistID` AND " "album.`ID` = :albumId AND " "artistAlbum.`ID` = album.`ArtistID` " "ORDER BY tracks.`DiscNumber` ASC, " "tracks.`TrackNumber` ASC"); auto result = d->mSelectTrackQuery.prepare(selectTrackQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectTrackQuery.lastError(); qDebug() << "DatabaseInterface::initRequest" << d->mSelectTrackQuery.lastQuery(); } } { auto selectTrackFromIdQueryText = QStringLiteral("SELECT " "tracks.`Id`, " "tracks.`Title`, " "tracks.`AlbumID`, " "artist.`Name`, " "artistAlbum.`Name`, " "tracks.`FileName`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration` " "FROM `Tracks` tracks, `Artists` artist, `Artists` artistAlbum, `Albums` album " "WHERE " "tracks.`ID` = :trackId AND " "artist.`ID` = tracks.`ArtistID` AND " "artistAlbum.`ID` = album.`ArtistID` AND " "tracks.`AlbumID` = album.`ID`"); auto result = d->mSelectTrackFromIdQuery.prepare(selectTrackFromIdQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectTrackFromIdQuery.lastError(); } } { auto selectCountAlbumsQueryText = QStringLiteral("SELECT count(*) " "FROM `Albums` album, `Artists` artist " "WHERE artist.`Name` = :artistName AND " "artist.`ID` = album.`ArtistID`"); const auto result = d->mSelectCountAlbumsForArtistQuery.prepare(selectCountAlbumsQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectCountAlbumsForArtistQuery.lastError(); } } { auto selectAlbumIdFromTitleQueryText = QStringLiteral("SELECT `ID` FROM `Albums` " "WHERE " "`Title` = :title"); auto result = d->mSelectAlbumIdFromTitleQuery.prepare(selectAlbumIdFromTitleQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectAlbumIdFromTitleQuery.lastError(); } } { auto insertAlbumQueryText = QStringLiteral("INSERT INTO Albums (`ID`, `Title`, `ArtistID`, `CoverFileName`, `TracksCount`, `IsSingleDiscAlbum`) " "VALUES (:albumId, :title, :artistId, :coverFileName, :tracksCount, :isSingleDiscAlbum)"); auto result = d->mInsertAlbumQuery.prepare(insertAlbumQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mInsertAlbumQuery.lastError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.ID " "FROM `Tracks` tracks, `Artists` artist " "WHERE " "tracks.`Title` = :title AND " "tracks.`AlbumID` = :album AND " "artist.`Name` = :artist AND " "artist.`ID` = tracks.`ArtistID`"); auto result = d->mSelectTrackIdFromTitleAlbumIdArtistQuery.prepare(selectTrackQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleAlbumIdArtistQuery.lastError(); } auto insertTrackQueryText = QStringLiteral("INSERT INTO `Tracks` (`ID`, `Title`, `AlbumID`, `ArtistID`, `FileName`, `TrackNumber`, `DiscNumber`, `Duration`) " "VALUES (:trackId, :title, :album, :artistId, :fileName, :trackNumber, :discNumber, :trackDuration)"); result = d->mInsertTrackQuery.prepare(insertTrackQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mInsertTrackQuery.lastError(); } } { auto selectTrackQueryText = QStringLiteral("SELECT " "tracks.ID " "FROM `Tracks` tracks, `Albums` albums, `Artists` artist " "WHERE " "tracks.`Title` = :title AND " "tracks.`AlbumID` = albums.`ID` AND " "albums.`Title` = :album AND " "artist.`ID` = tracks.`ArtistID` AND " "artist.`Name` = :artist"); auto result = d->mSelectTrackIdFromTitleAlbumArtistQuery.prepare(selectTrackQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectTrackIdFromTitleAlbumArtistQuery.lastError(); } } { auto selectAlbumTrackCountQueryText = QStringLiteral("SELECT `TracksCount` " "FROM `Albums`" "WHERE " "`ID` = :albumId"); auto result = d->mSelectAlbumTrackCountQuery.prepare(selectAlbumTrackCountQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectAlbumTrackCountQuery.lastError(); } } { auto updateAlbumQueryText = QStringLiteral("UPDATE `Albums` " "SET `TracksCount`=:tracksCount " "WHERE " "`ID` = :albumId"); auto result = d->mUpdateAlbumQuery.prepare(updateAlbumQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mUpdateAlbumQuery.lastError(); } } { auto selectTracksFromArtistQueryText = QStringLiteral("SELECT " "tracks.`Title`, " "tracks.`ID`, " "artist.`Name`, " "tracks.`FileName`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "albums.`CoverFileName`, " "albums.`Title`, " "albumArtist.`Name` " "FROM `Tracks` tracks, `Albums` albums, `Artists` artist, `Artists` albumArtist " "WHERE " "artist.`Name` = :artistName AND " "tracks.`AlbumID` = albums.`ID` AND " "artist.`ID` = tracks.`ArtistID` AND " "albumArtist.`ID` = albums.`ArtistID` " "ORDER BY tracks.`Title` ASC, " "albums.`Title` ASC"); auto result = d->mSelectTracksFromArtist.prepare(selectTracksFromArtistQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectTracksFromArtist.lastError(); } } { auto selectArtistQueryText = QStringLiteral("SELECT `ID`, " "`Name` " "FROM `Artists` " "WHERE " "`ID` = :artistId"); auto result = d->mSelectArtistQuery.prepare(selectArtistQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << selectArtistQueryText << d->mSelectArtistQuery.lastError(); qDebug() << d->mTracksDatabase.lastError(); } } { auto selectTrackFromFilePathQueryText = QStringLiteral("SELECT " "tracks.`ID`, " "tracks.`Title`, " "tracks.`AlbumID`, " "artist.`Name`, " "artistAlbum.`Name`, " "tracks.`FileName`, " "tracks.`TrackNumber`, " "tracks.`DiscNumber`, " "tracks.`Duration`, " "album.`Title` " "FROM `Tracks` tracks, `Artists` artist, `Artists` artistAlbum, `Albums` album " "WHERE " "tracks.`AlbumID` = album.`ID` AND " "artist.`ID` = tracks.`ArtistID` AND " "artistAlbum.`ID` = album.`ArtistID` AND " "tracks.`FileName` = :filePath"); auto result = d->mSelectTrackFromFilePathQuery.prepare(selectTrackFromFilePathQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mSelectTrackFromFilePathQuery.lastError(); qDebug() << "DatabaseInterface::initRequest" << d->mSelectTrackFromFilePathQuery.lastQuery(); } } { auto removeTrackQueryText = QStringLiteral("DELETE FROM `Tracks` " "WHERE " "`ID` = :trackId"); auto result = d->mRemoveTrackQuery.prepare(removeTrackQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mRemoveTrackQuery.lastError(); qDebug() << "DatabaseInterface::initRequest" << d->mRemoveTrackQuery.lastQuery(); } } { auto removeAlbumQueryText = QStringLiteral("DELETE FROM `Albums` " "WHERE " "`ID` = :albumId"); auto result = d->mRemoveAlbumQuery.prepare(removeAlbumQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mRemoveAlbumQuery.lastError(); qDebug() << "DatabaseInterface::initRequest" << d->mRemoveAlbumQuery.lastQuery(); } } { auto removeAlbumQueryText = QStringLiteral("DELETE FROM `Artists` " "WHERE " "`ID` = :artistId"); auto result = d->mRemoveArtistQuery.prepare(removeAlbumQueryText); if (!result) { qDebug() << "DatabaseInterface::initRequest" << d->mRemoveArtistQuery.lastError(); qDebug() << "DatabaseInterface::initRequest" << d->mRemoveArtistQuery.lastQuery(); } } transactionResult = finishTransaction(); d->mInitFinished = true; Q_EMIT requestsInitDone(); } qulonglong DatabaseInterface::insertArtist(QString name) { auto result = qulonglong(0); if (name.isEmpty()) { return result; } d->mSelectArtistByNameQuery.bindValue(QStringLiteral(":name"), name); auto queryResult = d->mSelectArtistByNameQuery.exec(); if (!queryResult || !d->mSelectArtistByNameQuery.isSelect() || !d->mSelectArtistByNameQuery.isActive()) { qDebug() << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.lastQuery(); qDebug() << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.boundValues(); qDebug() << "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 = d->mInsertArtistsQuery.exec(); if (!queryResult || !d->mInsertArtistsQuery.isActive()) { qDebug() << "DatabaseInterface::insertArtist" << d->mInsertArtistsQuery.lastQuery(); qDebug() << "DatabaseInterface::insertArtist" << d->mInsertArtistsQuery.boundValues(); qDebug() << "DatabaseInterface::insertArtist" << d->mInsertArtistsQuery.lastError(); d->mInsertArtistsQuery.finish(); return result; } result = d->mArtistId; ++d->mArtistId; d->mInsertArtistsQuery.finish(); Q_EMIT artistAdded(internalArtistFromId(d->mArtistId - 1)); return result; } qulonglong DatabaseInterface::internalArtistIdFromName(QString name) { auto result = qulonglong(0); if (name.isEmpty()) { return result; } d->mSelectArtistByNameQuery.bindValue(QStringLiteral(":name"), name); auto queryResult = d->mSelectArtistByNameQuery.exec(); if (!queryResult || !d->mSelectArtistByNameQuery.isSelect() || !d->mSelectArtistByNameQuery.isActive()) { qDebug() << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.lastQuery(); qDebug() << "DatabaseInterface::insertArtist" << d->mSelectArtistByNameQuery.boundValues(); qDebug() << "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 = d->mRemoveTrackQuery.exec(); if (!result || !d->mRemoveTrackQuery.isActive()) { qDebug() << "DatabaseInterface::removeTrackInDatabase" << d->mRemoveTrackQuery.lastQuery(); qDebug() << "DatabaseInterface::removeTrackInDatabase" << d->mRemoveTrackQuery.boundValues(); qDebug() << "DatabaseInterface::removeTrackInDatabase" << d->mRemoveTrackQuery.lastError(); } d->mRemoveTrackQuery.finish(); } void DatabaseInterface::removeAlbumInDatabase(qulonglong albumId) { d->mRemoveAlbumQuery.bindValue(QStringLiteral(":albumId"), albumId); auto result = d->mRemoveAlbumQuery.exec(); if (!result || !d->mRemoveAlbumQuery.isActive()) { qDebug() << "DatabaseInterface::removeAlbumInDatabase" << d->mRemoveAlbumQuery.lastQuery(); qDebug() << "DatabaseInterface::removeAlbumInDatabase" << d->mRemoveAlbumQuery.boundValues(); qDebug() << "DatabaseInterface::removeAlbumInDatabase" << d->mRemoveAlbumQuery.lastError(); } d->mRemoveAlbumQuery.finish(); } void DatabaseInterface::removeArtistInDatabase(qulonglong artistId) { d->mRemoveArtistQuery.bindValue(QStringLiteral(":artistId"), artistId); auto result = d->mRemoveArtistQuery.exec(); if (!result || !d->mRemoveArtistQuery.isActive()) { qDebug() << "DatabaseInterface::removeArtistInDatabase" << d->mRemoveArtistQuery.lastQuery(); qDebug() << "DatabaseInterface::removeArtistInDatabase" << d->mRemoveArtistQuery.boundValues(); qDebug() << "DatabaseInterface::removeArtistInDatabase" << d->mRemoveArtistQuery.lastError(); } d->mRemoveArtistQuery.finish(); } QMap DatabaseInterface::fetchTracks(qulonglong albumId) const { auto allTracks = QMap(); d->mSelectTrackQuery.bindValue(QStringLiteral(":albumId"), albumId); auto result = d->mSelectTrackQuery.exec(); if (!result || !d->mSelectTrackQuery.isSelect() || !d->mSelectTrackQuery.isActive()) { qDebug() << "DatabaseInterface::fetchTracks" << d->mSelectTrackQuery.lastQuery(); qDebug() << "DatabaseInterface::fetchTracks" << d->mSelectTrackQuery.boundValues(); qDebug() << "DatabaseInterface::fetchTracks" << d->mSelectTrackQuery.lastError(); } while (d->mSelectTrackQuery.next()) { MusicAudioTrack newTrack; newTrack.setDatabaseId(d->mSelectTrackQuery.record().value(0).toULongLong()); newTrack.setTitle(d->mSelectTrackQuery.record().value(1).toString()); newTrack.setParentId(d->mSelectTrackQuery.record().value(2).toString()); newTrack.setArtist(d->mSelectTrackQuery.record().value(3).toString()); newTrack.setAlbumArtist(d->mSelectTrackQuery.record().value(4).toString()); newTrack.setResourceURI(d->mSelectTrackQuery.record().value(5).toUrl()); newTrack.setTrackNumber(d->mSelectTrackQuery.record().value(6).toInt()); newTrack.setDiscNumber(d->mSelectTrackQuery.record().value(7).toInt()); newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(d->mSelectTrackQuery.record().value(8).toInt())); newTrack.setValid(true); allTracks[newTrack.databaseId()] = newTrack; } d->mSelectTrackQuery.finish(); updateTracksCount(albumId, allTracks.size()); return allTracks; } QList DatabaseInterface::fetchTrackIds(qulonglong albumId) const { auto allTrackIds = QList(); d->mSelectTrackQuery.bindValue(QStringLiteral(":albumId"), albumId); auto result = d->mSelectTrackQuery.exec(); if (!result || !d->mSelectTrackQuery.isSelect() || !d->mSelectTrackQuery.isActive()) { qDebug() << "DatabaseInterface::fetchTrackIds" << d->mSelectTrackQuery.lastQuery(); qDebug() << "DatabaseInterface::fetchTrackIds" << d->mSelectTrackQuery.boundValues(); qDebug() << "DatabaseInterface::fetchTrackIds" << d->mSelectTrackQuery.lastError(); } while (d->mSelectTrackQuery.next()) { allTrackIds.push_back(d->mSelectTrackQuery.record().value(0).toULongLong()); } d->mSelectTrackQuery.finish(); return allTrackIds; } void DatabaseInterface::updateTracksCount(qulonglong albumId, int tracksCount) const { d->mSelectAlbumTrackCountQuery.bindValue(QStringLiteral(":albumId"), albumId); auto result = d->mSelectAlbumTrackCountQuery.exec(); if (!result || !d->mSelectAlbumTrackCountQuery.isSelect() || !d->mSelectAlbumTrackCountQuery.isActive()) { qDebug() << "DatabaseInterface::updateTracksCount" << d->mSelectAlbumTrackCountQuery.lastQuery(); qDebug() << "DatabaseInterface::updateTracksCount" << d->mSelectAlbumTrackCountQuery.boundValues(); qDebug() << "DatabaseInterface::updateTracksCount" << d->mSelectAlbumTrackCountQuery.lastError(); d->mSelectAlbumTrackCountQuery.finish(); return; } if (!d->mSelectAlbumTrackCountQuery.next()) { d->mSelectAlbumTrackCountQuery.finish(); return; } auto oldTracksCount = d->mSelectAlbumTrackCountQuery.record().value(0).toInt(); d->mSelectAlbumTrackCountQuery.finish(); if (oldTracksCount != tracksCount) { d->mUpdateAlbumQuery.bindValue(QStringLiteral(":tracksCount"), tracksCount); d->mUpdateAlbumQuery.bindValue(QStringLiteral(":albumId"), albumId); result = d->mUpdateAlbumQuery.exec(); if (!result || !d->mUpdateAlbumQuery.isActive()) { qDebug() << "DatabaseInterface::updateTracksCount" << d->mUpdateAlbumQuery.lastQuery(); qDebug() << "DatabaseInterface::updateTracksCount" << d->mUpdateAlbumQuery.boundValues(); qDebug() << "DatabaseInterface::updateTracksCount" << d->mUpdateAlbumQuery.lastError(); d->mUpdateAlbumQuery.finish(); return; } d->mUpdateAlbumQuery.finish(); } } MusicAlbum DatabaseInterface::internalAlbumFromId(qulonglong albumId) const { auto retrievedAlbum = MusicAlbum(); d->mSelectAlbumQuery.bindValue(QStringLiteral(":albumId"), albumId); auto result = d->mSelectAlbumQuery.exec(); if (!result || !d->mSelectAlbumQuery.isSelect() || !d->mSelectAlbumQuery.isActive()) { qDebug() << "DatabaseInterface::internalAlbumFromId" << d->mSelectAlbumQuery.lastQuery(); qDebug() << "DatabaseInterface::internalAlbumFromId" << d->mSelectAlbumQuery.boundValues(); qDebug() << "DatabaseInterface::internalAlbumFromId" << d->mSelectAlbumQuery.lastError(); d->mSelectAlbumQuery.finish(); return retrievedAlbum; } if (!d->mSelectAlbumQuery.next()) { d->mSelectAlbumQuery.finish(); return retrievedAlbum; } retrievedAlbum.setDatabaseId(d->mSelectAlbumQuery.record().value(0).toULongLong()); retrievedAlbum.setTitle(d->mSelectAlbumQuery.record().value(1).toString()); retrievedAlbum.setId(d->mSelectAlbumQuery.record().value(2).toString()); retrievedAlbum.setArtist(d->mSelectAlbumQuery.record().value(3).toString()); retrievedAlbum.setAlbumArtURI(d->mSelectAlbumQuery.record().value(4).toUrl()); retrievedAlbum.setTracksCount(d->mSelectAlbumQuery.record().value(5).toInt()); retrievedAlbum.setIsSingleDiscAlbum(d->mSelectAlbumQuery.record().value(6).toBool()); retrievedAlbum.setTracks(fetchTracks(retrievedAlbum.databaseId())); retrievedAlbum.setTrackIds(fetchTrackIds(retrievedAlbum.databaseId())); retrievedAlbum.setValid(true); d->mSelectAlbumQuery.finish(); return retrievedAlbum; } MusicAlbum DatabaseInterface::internalAlbumFromTitle(QString title) const { auto result = MusicAlbum(); d->mSelectAlbumIdFromTitleQuery.bindValue(QStringLiteral(":title"), title); auto queryResult = d->mSelectAlbumIdFromTitleQuery.exec(); if (!queryResult || !d->mSelectAlbumIdFromTitleQuery.isSelect() || !d->mSelectAlbumIdFromTitleQuery.isActive()) { qDebug() << "DatabaseInterface::albumFromTitleAndAuthor" << d->mSelectAlbumIdFromTitleQuery.lastQuery(); qDebug() << "DatabaseInterface::albumFromTitleAndAuthor" << d->mSelectAlbumIdFromTitleQuery.boundValues(); qDebug() << "DatabaseInterface::albumFromTitleAndAuthor" << d->mSelectAlbumIdFromTitleQuery.lastError(); d->mSelectAlbumIdFromTitleQuery.finish(); return result; } if (!d->mSelectAlbumIdFromTitleQuery.next()) { d->mSelectAlbumIdFromTitleQuery.finish(); return result; } auto albumId = d->mSelectAlbumIdFromTitleQuery.record().value(0).toULongLong(); d->mSelectAlbumIdFromTitleQuery.finish(); result = internalAlbumFromId(albumId); return result; } MusicAudioTrack DatabaseInterface::internalTrackFromDatabaseId(qulonglong id) const { auto result = MusicAudioTrack(); if (!d || !d->mTracksDatabase.isValid() || !d->mInitFinished) { return result; } d->mSelectTrackFromIdQuery.bindValue(QStringLiteral(":trackId"), id); auto queryResult = d->mSelectTrackFromIdQuery.exec(); if (!queryResult || !d->mSelectTrackFromIdQuery.isSelect() || !d->mSelectTrackFromIdQuery.isActive()) { qDebug() << "DatabaseInterface::internalTrackFromDatabaseId" << d->mSelectAlbumQuery.lastQuery(); qDebug() << "DatabaseInterface::internalTrackFromDatabaseId" << d->mSelectAlbumQuery.boundValues(); qDebug() << "DatabaseInterface::internalTrackFromDatabaseId" << d->mSelectAlbumQuery.lastError(); d->mSelectTrackFromIdQuery.finish(); return result; } if (!d->mSelectTrackFromIdQuery.next()) { d->mSelectTrackFromIdQuery.finish(); return result; } result.setDatabaseId(d->mSelectTrackFromIdQuery.record().value(0).toULongLong()); result.setAlbumName(internalAlbumDataFromId(d->mSelectTrackFromIdQuery.record().value(2).toULongLong(), DatabaseInterface::AlbumData::Title).toString()); result.setArtist(d->mSelectTrackFromIdQuery.record().value(3).toString()); result.setAlbumArtist(d->mSelectTrackFromIdQuery.record().value(4).toString()); result.setDuration(QTime::fromMSecsSinceStartOfDay(d->mSelectTrackFromIdQuery.record().value(8).toLongLong())); result.setResourceURI(d->mSelectTrackFromIdQuery.record().value(5).toUrl()); result.setAlbumCover(internalAlbumDataFromId(d->mSelectTrackFromIdQuery.record().value(2).toULongLong(), DatabaseInterface::AlbumData::Image).toUrl()); result.setTitle(d->mSelectTrackFromIdQuery.record().value(1).toString()); result.setTrackNumber(d->mSelectTrackFromIdQuery.record().value(6).toInt()); result.setDiscNumber(d->mSelectTrackFromIdQuery.record().value(7).toInt()); result.setValid(true); d->mSelectTrackFromIdQuery.finish(); return result; } +qulonglong DatabaseInterface::internalTrackIdFromTitleAlbumArtist(QString title, QString album, QString artist) const +{ + auto result = qulonglong(0); + + if (!d) { + return result; + } + + d->mSelectTrackIdFromTitleAlbumArtistQuery.bindValue(QStringLiteral(":title"), title); + d->mSelectTrackIdFromTitleAlbumArtistQuery.bindValue(QStringLiteral(":album"), album); + d->mSelectTrackIdFromTitleAlbumArtistQuery.bindValue(QStringLiteral(":artist"), artist); + + auto queryResult = d->mSelectTrackIdFromTitleAlbumArtistQuery.exec(); + + if (!queryResult || !d->mSelectTrackIdFromTitleAlbumArtistQuery.isSelect() || !d->mSelectTrackIdFromTitleAlbumArtistQuery.isActive()) { + qDebug() << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumArtistQuery.lastQuery(); + qDebug() << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumArtistQuery.boundValues(); + qDebug() << "DatabaseInterface::trackIdFromTitleAlbumArtist" << d->mSelectTrackIdFromTitleAlbumArtistQuery.lastError(); + + d->mSelectTrackIdFromTitleAlbumArtistQuery.finish(); + + return result; + } + + if (d->mSelectTrackIdFromTitleAlbumArtistQuery.next()) { + result = d->mSelectTrackIdFromTitleAlbumArtistQuery.record().value(0).toInt(); + } + + d->mSelectTrackIdFromTitleAlbumArtistQuery.finish(); + + return result; +} + QVariant DatabaseInterface::internalAlbumDataFromId(qulonglong albumId, DatabaseInterface::AlbumData dataType) const { auto result = QVariant(); auto currentAlbum = internalAlbumFromId(albumId); if (!currentAlbum.isValid()) { return result; } switch(dataType) { case DatabaseInterface::AlbumData::Id: result = currentAlbum.id(); break; case DatabaseInterface::AlbumData::Image: result = currentAlbum.albumArtURI(); break; case DatabaseInterface::AlbumData::Title: result = currentAlbum.title(); break; case DatabaseInterface::AlbumData::Artist: result = currentAlbum.artist(); break; case DatabaseInterface::AlbumData::TracksCount: result = currentAlbum.tracksCount(); break; } return result; } QVector DatabaseInterface::internalTracksFromAuthor(QString artistName) const { auto allTracks = QVector(); d->mSelectTracksFromArtist.bindValue(QStringLiteral(":artistName"), artistName); auto result = d->mSelectTracksFromArtist.exec(); if (!result || !d->mSelectTracksFromArtist.isSelect() || !d->mSelectTracksFromArtist.isActive()) { qDebug() << "DatabaseInterface::tracksFromAuthor" << d->mSelectTracksFromArtist.lastQuery(); qDebug() << "DatabaseInterface::tracksFromAuthor" << d->mSelectTracksFromArtist.boundValues(); qDebug() << "DatabaseInterface::tracksFromAuthor" << d->mSelectTracksFromArtist.lastError(); return allTracks; } while (d->mSelectTracksFromArtist.next()) { MusicAudioTrack newTrack; newTrack.setDatabaseId(d->mSelectTracksFromArtist.record().value(1).toULongLong()); newTrack.setTitle(d->mSelectTracksFromArtist.record().value(0).toString()); newTrack.setAlbumName(d->mSelectTracksFromArtist.record().value(8).toString()); newTrack.setAlbumArtist(d->mSelectTracksFromArtist.record().value(9).toString()); newTrack.setArtist(d->mSelectTracksFromArtist.record().value(2).toString()); newTrack.setResourceURI(d->mSelectTracksFromArtist.record().value(3).toUrl()); newTrack.setAlbumCover(d->mSelectTracksFromArtist.record().value(7).toUrl()); newTrack.setTrackNumber(d->mSelectTracksFromArtist.record().value(4).toInt()); newTrack.setDiscNumber(d->mSelectTracksFromArtist.record().value(5).toInt()); newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(d->mSelectTracksFromArtist.record().value(6).toInt())); newTrack.setValid(true); allTracks.push_back(newTrack); } d->mSelectTracksFromArtist.finish(); return allTracks; } #include "moc_databaseinterface.cpp" diff --git a/src/databaseinterface.h b/src/databaseinterface.h index 1329bb01..90579d60 100644 --- a/src/databaseinterface.h +++ b/src/databaseinterface.h @@ -1,141 +1,145 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef DATABASEINTERFACE_H #define DATABASEINTERFACE_H #include "musicalbum.h" #include "musicaudiotrack.h" #include "musicartist.h" #include #include #include #include #include #include class DatabaseInterfacePrivate; class QMutex; class DatabaseInterface : public QObject { Q_OBJECT public: enum class AlbumData { Title, Artist, Image, TracksCount, Id, }; explicit DatabaseInterface(QObject *parent = 0); virtual ~DatabaseInterface(); Q_INVOKABLE void init(const QString &dbName); MusicAlbum albumFromTitle(QString title) const; QVector allAlbums() const; QVector allArtists() const; QVector tracksFromAuthor(QString artistName) const; MusicAudioTrack trackFromDatabaseId(qulonglong id) const; qulonglong trackIdFromTitleAlbumArtist(QString title, QString album, QString artist) const; Q_SIGNALS: void artistAdded(MusicArtist newArtist); void albumAdded(MusicAlbum newAlbum); void trackAdded(MusicAudioTrack newTrack); void artistRemoved(MusicArtist newArtist); void albumRemoved(MusicAlbum newAlbum); void trackRemoved(MusicAudioTrack removedTrack); void artistModified(MusicArtist newArtist); void albumModified(MusicAlbum newAlbum); void trackModified(MusicAudioTrack modifiedTrack); void requestsInitDone(); public Q_SLOTS: void insertTracksList(QHash > tracks, QHash covers); void removeTracksList(const QList removedTracks); + void modifyTracksList(const QList modifiedTracks); + private: bool startTransaction() const; bool finishTransaction() const; bool rollBackTransaction() const; QMap fetchTracks(qulonglong albumId) const; QList fetchTrackIds(qulonglong albumId) const; void updateTracksCount(qulonglong albumId, int tracksCount) const; MusicArtist internalArtistFromId(qulonglong artistId) const; MusicAlbum internalAlbumFromId(qulonglong albumId) const; MusicAlbum internalAlbumFromTitle(QString title) const; MusicAudioTrack internalTrackFromDatabaseId(qulonglong id) const; + qulonglong internalTrackIdFromTitleAlbumArtist(QString title, QString album, QString artist) const; + QVariant internalAlbumDataFromId(qulonglong albumId, AlbumData dataType) const; QVector internalTracksFromAuthor(QString artistName) const; void initDatabase() const; void initRequest(); qulonglong insertArtist(QString name); qulonglong internalArtistIdFromName(QString name); void removeTrackInDatabase(qulonglong trackId); void removeAlbumInDatabase(qulonglong albumId); void removeArtistInDatabase(qulonglong artistId); DatabaseInterfacePrivate *d; }; #endif // DATABASEINTERFACE_H diff --git a/src/musicalbum.cpp b/src/musicalbum.cpp index e8af7c01..1b97aa10 100644 --- a/src/musicalbum.cpp +++ b/src/musicalbum.cpp @@ -1,301 +1,306 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "musicalbum.h" #include #include #include #include #include #include class MusicAlbumPrivate { public: qulonglong mDatabaseId = 0; QString mId; QString mParentId; QString mTitle; QString mArtist; QUrl mAlbumArtURI; QUrl mResourceURI; QMap mTracks; QList mTrackIds; int mTracksCount = 0; bool mIsValid = false; bool mIsSingleDiscAlbum = true; }; MusicAlbum::MusicAlbum() : d(new MusicAlbumPrivate) { } MusicAlbum::MusicAlbum(MusicAlbum &&other) : d(other.d) { other.d = nullptr; } MusicAlbum::MusicAlbum(const MusicAlbum &other) : d(new MusicAlbumPrivate(*other.d)) { } MusicAlbum& MusicAlbum::operator=(MusicAlbum &&other) { if (&other != this) { delete d; d = other.d; other.d = nullptr; } return *this; } MusicAlbum& MusicAlbum::operator=(const MusicAlbum &other) { if (&other != this) { (*d) = (*other.d); } return *this; } MusicAlbum::~MusicAlbum() { delete d; } void MusicAlbum::setValid(bool value) { d->mIsValid = value; } bool MusicAlbum::isValid() const { return d->mIsValid; } void MusicAlbum::setDatabaseId(qulonglong value) { d->mDatabaseId = value; } qulonglong MusicAlbum::databaseId() const { return d->mDatabaseId; } void MusicAlbum::setId(const QString &value) { d->mId = value; } QString MusicAlbum::id() const { return d->mId; } void MusicAlbum::setParentId(const QString &value) { d->mParentId = value; } QString MusicAlbum::parentId() const { return d->mParentId; } void MusicAlbum::setTracksCount(int count) { d->mTracksCount = count; } int MusicAlbum::tracksCount() const { const auto realTracksCount = d->mTracks.size(); return (realTracksCount == 0 ? d->mTracksCount : realTracksCount); } void MusicAlbum::setTitle(const QString &value) { d->mTitle = value; } QString MusicAlbum::title() const { return d->mTitle; } void MusicAlbum::setArtist(const QString &value) { d->mArtist = value; } QString MusicAlbum::artist() const { return d->mArtist; } void MusicAlbum::setAlbumArtURI(const QUrl &value) { d->mAlbumArtURI = value; } QUrl MusicAlbum::albumArtURI() const { return d->mAlbumArtURI; } void MusicAlbum::setResourceURI(const QUrl &value) { d->mResourceURI = value; } QUrl MusicAlbum::resourceURI() const { return d->mResourceURI; } void MusicAlbum::setIsSingleDiscAlbum(bool value) { d->mIsSingleDiscAlbum = value; } bool MusicAlbum::isSingleDiscAlbum() const { return d->mIsSingleDiscAlbum; } void MusicAlbum::setTracks(const QMap &allTracks) { d->mTracks = allTracks; } QList MusicAlbum::tracksKeys() const { return d->mTracks.keys(); } MusicAudioTrack MusicAlbum::trackFromIndex(int index) const { return d->mTracks[d->mTrackIds[index]]; } void MusicAlbum::setTrackIds(const QList &allTracksIds) { d->mTrackIds = allTracksIds; } qulonglong MusicAlbum::trackIdFromIndex(int index) const { return d->mTrackIds[index]; } int MusicAlbum::trackIndexFromId(qulonglong id) const { int result = -1; auto itTrack = std::find(d->mTrackIds.begin(), d->mTrackIds.end(), id); if (itTrack == d->mTrackIds.end()) { return result; } result = itTrack - d->mTrackIds.begin(); return result; } QStringList MusicAlbum::allArtists() const { auto result = QList(); for (const auto &oneTrack : d->mTracks) { result.push_back(oneTrack.artist()); } std::sort(result.begin(), result.end()); result.erase(std::unique(result.begin(), result.end()), result.end()); return result; } QStringList MusicAlbum::allTracksTitle() const { auto result = QList(); for (const auto &oneTrack : d->mTracks) { result.push_back(oneTrack.title()); } std::sort(result.begin(), result.end()); result.erase(std::unique(result.begin(), result.end()), result.end()); return result; } bool MusicAlbum::isEmpty() const { return d->mTrackIds.isEmpty(); } void MusicAlbum::removeTrackFromIndex(int index) { if (index < 0 || index >= tracksCount()) { return; } --d->mTracksCount; d->mTracks.remove(d->mTrackIds.at(index)); d->mTrackIds.removeAt(index); } void MusicAlbum::insertTrack(MusicAudioTrack newTrack, int index) { d->mTrackIds.insert(index, newTrack.databaseId()); d->mTracks[newTrack.databaseId()] = newTrack; ++d->mTracksCount; } +void MusicAlbum::updateTrack(MusicAudioTrack modifiedTrack, int index) +{ + d->mTracks[d->mTrackIds[index]] = modifiedTrack; +} + QDebug& operator<<(QDebug &stream, const MusicAlbum &data) { stream << data.title() << " " << data.artist(); return stream; } bool operator==(const MusicAlbum &album1, const MusicAlbum &album2) { return album1.artist() == album2.artist() && album1.title() == album2.title(); } diff --git a/src/musicalbum.h b/src/musicalbum.h index 9a9ff3fa..86fcd26c 100644 --- a/src/musicalbum.h +++ b/src/musicalbum.h @@ -1,125 +1,127 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef MUSICALBUM_H #define MUSICALBUM_H #include "musicaudiotrack.h" #include #include #include #include #include class MusicAlbumPrivate; class QDebug; class MusicAlbum { public: MusicAlbum(); MusicAlbum(MusicAlbum &&other); MusicAlbum(const MusicAlbum &other); MusicAlbum& operator=(MusicAlbum &&other); MusicAlbum& operator=(const MusicAlbum &other); ~MusicAlbum(); void setValid(bool value); bool isValid() const; void setDatabaseId(qulonglong value); qulonglong databaseId() const; void setId(const QString &value); QString id() const; void setParentId(const QString &value); QString parentId() const; void setTracksCount(int count); int tracksCount() const; void setTitle(const QString &value); QString title() const; void setArtist(const QString &value); QString artist() const; void setAlbumArtURI(const QUrl &value); QUrl albumArtURI() const; void setResourceURI(const QUrl &value); QUrl resourceURI() const; void setIsSingleDiscAlbum(bool value); bool isSingleDiscAlbum() const; void setTracks(const QMap &allTracks); QList tracksKeys() const; MusicAudioTrack trackFromIndex(int index) const; void setTrackIds(const QList &allTracksIds); qulonglong trackIdFromIndex(int index) const; int trackIndexFromId(qulonglong id) const; QStringList allArtists() const; QStringList allTracksTitle() const; bool isEmpty() const; void removeTrackFromIndex(int index); void insertTrack(MusicAudioTrack newTrack, int index); + void updateTrack(MusicAudioTrack modifiedTrack, int index); + private: MusicAlbumPrivate *d; }; QDebug& operator<<(QDebug &stream, const MusicAlbum &data); bool operator==(const MusicAlbum &album1, const MusicAlbum &album2); Q_DECLARE_METATYPE(MusicAlbum) #endif // MUSICALBUM_H