File indexing completed on 2024-04-21 04:49:02

0001 /*
0002    SPDX-FileCopyrightText: 2017 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003 
0004    SPDX-License-Identifier: LGPL-3.0-or-later
0005  */
0006 
0007 #include "trackslistener.h"
0008 
0009 #include "playListLogging.h"
0010 #include "filescanner.h"
0011 #include "filewriter.h"
0012 
0013 #include <QSet>
0014 #include <QList>
0015 
0016 #include <array>
0017 #include <algorithm>
0018 
0019 class TracksListenerPrivate
0020 {
0021 public:
0022 
0023     QSet<qulonglong> mTracksByIdSet;
0024 
0025     QSet<qulonglong> mRadiosByIdSet;
0026 
0027     QList<std::tuple<QString, QString, QString, int, int>> mTracksByNameSet;
0028 
0029     QList<QUrl> mTracksByFileNameSet;
0030 
0031     DatabaseInterface *mDatabase = nullptr;
0032 
0033     FileScanner mFileScanner;
0034 
0035     FileWriter mFileWriter;
0036 };
0037 
0038 TracksListener::TracksListener(DatabaseInterface *database, QObject *parent) : QObject(parent), d(std::make_unique<TracksListenerPrivate>())
0039 {
0040     d->mDatabase = database;
0041 }
0042 
0043 TracksListener::~TracksListener()
0044 = default;
0045 
0046 void TracksListener::tracksAdded(const ListTrackDataType &allTracks)
0047 {
0048     for (const auto &oneTrack : allTracks) {
0049         if (d->mTracksByIdSet.contains(oneTrack.databaseId())) {
0050             Q_EMIT trackHasChanged(oneTrack);
0051         }
0052 
0053         if (d->mTracksByNameSet.isEmpty()) {
0054             return;
0055         }
0056 
0057         for (auto itTrack = d->mTracksByNameSet.cbegin(); itTrack != d->mTracksByNameSet.cend(); ) {
0058             if (!std::get<0>(*itTrack).isEmpty() && std::get<0>(*itTrack) != oneTrack.title()) {
0059                 ++itTrack;
0060                 continue;
0061             }
0062 
0063             if (!std::get<1>(*itTrack).isEmpty() && std::get<1>(*itTrack) != oneTrack.artist()) {
0064                 ++itTrack;
0065                 continue;
0066             }
0067 
0068             if (!std::get<2>(*itTrack).isEmpty() && std::get<2>(*itTrack) != oneTrack.album()) {
0069                 ++itTrack;
0070                 continue;
0071             }
0072 
0073             if (std::get<3>(*itTrack) != oneTrack.trackNumber()) {
0074                 ++itTrack;
0075                 continue;
0076             }
0077 
0078             if (std::get<4>(*itTrack) != oneTrack.discNumber()) {
0079                 ++itTrack;
0080                 continue;
0081             }
0082 
0083             Q_EMIT trackHasChanged(TrackDataType(oneTrack));
0084 
0085             d->mTracksByIdSet.insert(oneTrack.databaseId());
0086             itTrack = d->mTracksByNameSet.erase(itTrack);
0087         }
0088     }
0089 }
0090 
0091 void TracksListener::trackRemoved(qulonglong id)
0092 {
0093     if (d->mTracksByIdSet.contains(id)) {
0094         Q_EMIT trackHasBeenRemoved(id);
0095     }
0096 }
0097 
0098 void TracksListener::trackModified(const TrackDataType &modifiedTrack)
0099 {
0100     if (d->mTracksByIdSet.contains(modifiedTrack.databaseId())) {
0101         Q_EMIT trackHasChanged(modifiedTrack);
0102     }
0103 }
0104 
0105 void TracksListener::trackByNameInList(const QVariant &title, const QVariant &artist, const QVariant &album,
0106                                        const QVariant &trackNumber, const QVariant &discNumber)
0107 {
0108     const auto realTitle = title.toString();
0109     const auto realArtist = artist.toString();
0110     const auto albumIsValid = !album.isNull() && album.isValid() && !album.toString().isEmpty();
0111     auto realAlbum = std::optional<QString>{};
0112     if (albumIsValid) {
0113         realAlbum = album.toString();
0114     }
0115     auto trackNumberIsValid = bool{};
0116     const auto trackNumberValue = trackNumber.toInt(&trackNumberIsValid);
0117     auto realTrackNumber = std::optional<int>{};
0118     if (trackNumberIsValid) {
0119         realTrackNumber = trackNumberValue;
0120     }
0121     auto discNumberIsValid = bool{};
0122     const auto discNumberValue = discNumber.toInt(&discNumberIsValid);
0123     auto realDiscNumber = std::optional<int>{};
0124     if (discNumberIsValid) {
0125         realDiscNumber = discNumberValue;
0126     }
0127 
0128     auto newTrackId = d->mDatabase->trackIdFromTitleAlbumTrackDiscNumber(realTitle, realArtist, realAlbum,
0129                                                                          realTrackNumber, realDiscNumber);
0130     if (newTrackId == 0) {
0131         auto newTrack = std::tuple<QString, QString, QString, int, int>(realTitle, realArtist, album.toString(), trackNumber.toInt(), discNumber.toInt());
0132         d->mTracksByNameSet.push_back(newTrack);
0133 
0134         return;
0135     }
0136 
0137     d->mTracksByIdSet.insert(newTrackId);
0138 
0139     auto newTrack = d->mDatabase->trackDataFromDatabaseId(newTrackId);
0140 
0141     if (!newTrack.isEmpty()) {
0142         Q_EMIT trackHasChanged(newTrack);
0143     }
0144 }
0145 
0146 void TracksListener::trackByFileNameInList(ElisaUtils::PlayListEntryType type, const QUrl &fileName)
0147 {
0148     Q_UNUSED(type)
0149 
0150     if (fileName.isLocalFile() || fileName.scheme().isEmpty()) {
0151         auto newTrackId = d->mDatabase->trackIdFromFileName(fileName);
0152         if (newTrackId == 0) {
0153             auto newTrack = d->mFileScanner.scanOneFile(fileName);
0154 
0155             if (newTrack.isValid()) {
0156                 d->mTracksByFileNameSet.push_back(fileName);
0157 
0158                 Q_EMIT trackHasChanged(newTrack);
0159                 return;
0160             }
0161 
0162             d->mTracksByFileNameSet.push_back(fileName);
0163 
0164             return;
0165         }
0166     } else {
0167         auto newRadioId = d->mDatabase->radioIdFromFileName(fileName);
0168         if (newRadioId) {
0169             auto newRadio = d->mDatabase->radioDataFromDatabaseId(newRadioId);
0170 
0171             if (!newRadio.isEmpty()) {
0172                 Q_EMIT trackHasChanged({newRadio});
0173             }
0174         }
0175     }
0176 }
0177 
0178 void TracksListener::newAlbumInList(qulonglong newDatabaseId, const QString &entryTitle)
0179 {
0180     qCDebug(orgKdeElisaPlayList()) << "TracksListener::newAlbumInList" << newDatabaseId << entryTitle << d->mDatabase->albumData(newDatabaseId);
0181     Q_EMIT tracksListAdded(newDatabaseId, entryTitle, ElisaUtils::Album, d->mDatabase->albumData(newDatabaseId));
0182 }
0183 
0184 void TracksListener::newEntryInList(qulonglong newDatabaseId,
0185                                     const QString &entryTitle,
0186                                     ElisaUtils::PlayListEntryType databaseIdType)
0187 {
0188     qCDebug(orgKdeElisaPlayList()) << "TracksListener::newEntryInList" << newDatabaseId << entryTitle << databaseIdType;
0189     switch (databaseIdType)
0190     {
0191     case ElisaUtils::Track:
0192     {
0193         d->mTracksByIdSet.insert(newDatabaseId);
0194 
0195         auto newTrack = d->mDatabase->trackDataFromDatabaseId(newDatabaseId);
0196         if (!newTrack.isEmpty()) {
0197             Q_EMIT trackHasChanged(newTrack);
0198         }
0199         break;
0200     }
0201     case ElisaUtils::Radio:
0202     {
0203         d->mRadiosByIdSet.insert(newDatabaseId);
0204 
0205         auto newRadio = d->mDatabase->radioDataFromDatabaseId(newDatabaseId);
0206         if (!newRadio.isEmpty()) {
0207             Q_EMIT trackHasChanged(newRadio);
0208         }
0209         break;
0210     }
0211     case ElisaUtils::Artist:
0212         newArtistInList(newDatabaseId, entryTitle);
0213         break;
0214     case ElisaUtils::FileName:
0215         newUrlInList(QUrl::fromLocalFile(entryTitle), ElisaUtils::FileName);
0216         break;
0217     case ElisaUtils::Album:
0218         newAlbumInList(newDatabaseId, entryTitle);
0219         break;
0220     case ElisaUtils::Genre:
0221         newGenreInList(newDatabaseId, entryTitle);
0222         break;
0223     case ElisaUtils::Lyricist:
0224     case ElisaUtils::Composer:
0225     case ElisaUtils::Unknown:
0226     case ElisaUtils::Container:
0227     case ElisaUtils::PlayList:
0228         break;
0229     }
0230 }
0231 
0232 void TracksListener::newUrlInList(const QUrl &entryUrl, ElisaUtils::PlayListEntryType databaseIdType)
0233 {
0234     switch (databaseIdType)
0235     {
0236     case ElisaUtils::Track:
0237     case ElisaUtils::FileName:
0238     {
0239         auto newDatabaseId = d->mDatabase->trackIdFromFileName(entryUrl);
0240 
0241         if (!newDatabaseId)
0242         {
0243             trackByFileNameInList(databaseIdType, entryUrl);
0244             return;
0245         }
0246 
0247         d->mTracksByIdSet.insert(newDatabaseId);
0248 
0249         auto newTrack = d->mDatabase->trackDataFromDatabaseIdAndUrl(newDatabaseId, entryUrl);
0250         if (!newTrack.isEmpty()) {
0251             Q_EMIT trackHasChanged(newTrack);
0252         }
0253         break;
0254     }
0255     case ElisaUtils::Radio:
0256     {
0257         auto newDatabaseId = d->mDatabase->radioIdFromFileName(entryUrl);
0258 
0259         if (!newDatabaseId)
0260         {
0261             return;
0262         }
0263 
0264         d->mRadiosByIdSet.insert(newDatabaseId);
0265 
0266         auto newRadio = d->mDatabase->radioDataFromDatabaseId(newDatabaseId);
0267         if (!newRadio.isEmpty()) {
0268             Q_EMIT trackHasChanged(newRadio);
0269         }
0270         break;
0271     }
0272     case ElisaUtils::Artist:
0273     case ElisaUtils::Album:
0274     case ElisaUtils::Lyricist:
0275     case ElisaUtils::Composer:
0276     case ElisaUtils::Genre:
0277     case ElisaUtils::Unknown:
0278     case ElisaUtils::Container:
0279     case ElisaUtils::PlayList:
0280         break;
0281     }
0282 }
0283 
0284 void TracksListener::newArtistInList(qulonglong newDatabaseId, const QString &artist)
0285 {
0286     auto newTracks = d->mDatabase->tracksDataFromAuthor(artist);
0287     if (newTracks.isEmpty()) {
0288         return;
0289     }
0290 
0291     for (const auto &oneTrack : newTracks) {
0292         d->mTracksByIdSet.insert(oneTrack.databaseId());
0293     }
0294 
0295     Q_EMIT tracksListAdded(newDatabaseId, artist, ElisaUtils::Artist, newTracks);
0296 }
0297 
0298 void TracksListener::newGenreInList(qulonglong newDatabaseId, const QString &entryTitle)
0299 {
0300     auto newTracks = d->mDatabase->tracksDataFromGenre(entryTitle);
0301 
0302     if (newTracks.isEmpty()) {
0303         return;
0304     }
0305 
0306     for (const auto &oneTrack : newTracks) {
0307         d->mTracksByIdSet.insert(oneTrack.databaseId());
0308     }
0309 
0310     Q_EMIT tracksListAdded(newDatabaseId, entryTitle, ElisaUtils::Genre, newTracks);
0311 }
0312 
0313 void TracksListener::updateSingleFileMetaData(const QUrl &url, DataTypes::ColumnsRoles role, const QVariant &data)
0314 {
0315     d->mFileWriter.writeSingleMetaDataToFile(url, role, data);
0316 }
0317 
0318 #include "moc_trackslistener.cpp"