diff --git a/src/mediaplaylist.cpp b/src/mediaplaylist.cpp index 08eec26e..dc57bc67 100644 --- a/src/mediaplaylist.cpp +++ b/src/mediaplaylist.cpp @@ -1,594 +1,613 @@ /* * 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 "mediaplaylist.h" #include "databaseinterface.h" #include "musicaudiotrack.h" #include "musiclistenersmanager.h" #include #include #include #include #include class MediaPlayListPrivate { public: QList mData; QList mTrackData; bool mUseLocalIcons = false; MusicListenersManager* mMusicListenersManager = nullptr; }; MediaPlayList::MediaPlayList(QObject *parent) : QAbstractListModel(parent), d(new MediaPlayListPrivate) { } MediaPlayList::~MediaPlayList() { delete d; } int MediaPlayList::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return d->mData.size(); } QVariant MediaPlayList::data(const QModelIndex &index, int role) const { auto result = QVariant(); if (!index.isValid()) { return result; } if (index.row() < 0 || index.row() >= d->mData.size()) { return result; } if (role < ColumnsRoles::IsValidRole || role > ColumnsRoles::HasAlbumHeader) { return result; } ColumnsRoles convertedRole = static_cast(role); if (d->mData[index.row()].mIsValid) { switch(convertedRole) { case ColumnsRoles::IsValidRole: result = d->mData[index.row()].mIsValid; break; case ColumnsRoles::TitleRole: result = d->mTrackData[index.row()].title(); break; case ColumnsRoles::DurationRole: { const QTime &trackDuration = d->mTrackData[index.row()].duration(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(); } break; } case ColumnsRoles::MilliSecondsDurationRole: result = d->mTrackData[index.row()].duration().msecsSinceStartOfDay(); break; case ColumnsRoles::ArtistRole: result = d->mTrackData[index.row()].artist(); break; case ColumnsRoles::AlbumRole: result = d->mTrackData[index.row()].albumName(); break; case ColumnsRoles::TrackNumberRole: result = d->mTrackData[index.row()].trackNumber(); break; case ColumnsRoles::DiscNumberRole: if (d->mTrackData[index.row()].discNumber() > 0) { result = d->mTrackData[index.row()].discNumber(); } break; case ColumnsRoles::ResourceRole: result = d->mTrackData[index.row()].resourceURI(); break; case ColumnsRoles::ImageRole: { auto albumArt = d->mTrackData[index.row()].albumCover(); if (albumArt.isValid()) { result = albumArt; } else { if (d->mUseLocalIcons) { result = QUrl(QStringLiteral("qrc:/media-optical-audio.svg")); } else { result = QUrl(QStringLiteral("image://icon/media-optical-audio")); } } break; } case ColumnsRoles::HasAlbumHeader: result = rowHasHeader(index.row()); break; case ColumnsRoles::RatingRole: break; case ColumnsRoles::CountRole: break; case ColumnsRoles::CreatorRole: break; case ColumnsRoles::IsPlayingRole: result = d->mData[index.row()].mIsPlaying; break; } } else { switch(convertedRole) { case ColumnsRoles::IsValidRole: result = d->mData[index.row()].mIsValid; break; case ColumnsRoles::TitleRole: result = d->mData[index.row()].mTitle; break; case ColumnsRoles::IsPlayingRole: result = d->mData[index.row()].mIsPlaying; break; case ColumnsRoles::ArtistRole: result = d->mData[index.row()].mArtist; break; case ColumnsRoles::AlbumRole: result = d->mData[index.row()].mAlbum; break; case ColumnsRoles::TrackNumberRole: result = -1; break; case ColumnsRoles::HasAlbumHeader: result = rowHasHeader(index.row()); break; case ColumnsRoles::DurationRole: break; case ColumnsRoles::DiscNumberRole: break; case ColumnsRoles::MilliSecondsDurationRole: break; case ColumnsRoles::ResourceRole: break; case ColumnsRoles::RatingRole: break; case ColumnsRoles::CountRole: break; case ColumnsRoles::CreatorRole: break; case ColumnsRoles::ImageRole: result = QStringLiteral(""); break; } } return result; } bool MediaPlayList::setData(const QModelIndex &index, const QVariant &value, int role) { bool modelModified = false; if (!index.isValid()) { return modelModified; } if (index.row() < 0 || index.row() >= d->mData.size()) { return modelModified; } if (role < ColumnsRoles::IsValidRole || role > ColumnsRoles::HasAlbumHeader) { return modelModified; } ColumnsRoles convertedRole = static_cast(role); switch(convertedRole) { case ColumnsRoles::IsPlayingRole: modelModified = true; d->mData[index.row()].mIsPlaying = value.toBool(); Q_EMIT dataChanged(index, index, {role}); break; default: modelModified = false; } return modelModified; } QHash MediaPlayList::roleNames() const { QHash roles; roles[static_cast(ColumnsRoles::IsValidRole)] = "isValid"; roles[static_cast(ColumnsRoles::TitleRole)] = "title"; roles[static_cast(ColumnsRoles::DurationRole)] = "duration"; roles[static_cast(ColumnsRoles::ArtistRole)] = "artist"; roles[static_cast(ColumnsRoles::AlbumRole)] = "album"; roles[static_cast(ColumnsRoles::TrackNumberRole)] = "trackNumber"; roles[static_cast(ColumnsRoles::DiscNumberRole)] = "discNumber"; roles[static_cast(ColumnsRoles::RatingRole)] = "rating"; roles[static_cast(ColumnsRoles::ImageRole)] = "image"; roles[static_cast(ColumnsRoles::CountRole)] = "count"; roles[static_cast(ColumnsRoles::IsPlayingRole)] = "isPlaying"; roles[static_cast(ColumnsRoles::HasAlbumHeader)] = "hasAlbumHeader"; return roles; } bool MediaPlayList::removeRows(int row, int count, const QModelIndex &parent) { beginRemoveRows(parent, row, row + count - 1); bool hadAlbumHeader = false; if (rowCount() > row + count) { hadAlbumHeader = rowHasHeader(row + count); } for (int i = row, cpt = 0; cpt < count; ++i, ++cpt) { d->mData.removeAt(i); d->mTrackData.removeAt(i); } endRemoveRows(); if (hadAlbumHeader != rowHasHeader(row)) { qDebug() << "dataChanged(index(row, 0), index(row, 0), {ColumnsRoles::HasAlbumHeader});" << row; Q_EMIT dataChanged(index(row, 0), index(row, 0), {ColumnsRoles::HasAlbumHeader}); } Q_EMIT persistentStateChanged(); return false; } void MediaPlayList::enqueue(qulonglong newTrackId) { enqueue(MediaPlayListEntry(newTrackId)); } void MediaPlayList::clearAndEnqueue(qulonglong newTrackId) { clearPlayList(); enqueue(MediaPlayListEntry(newTrackId)); } void MediaPlayList::enqueue(MediaPlayListEntry newEntry) { beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size()); d->mData.push_back(newEntry); d->mTrackData.push_back({}); endInsertRows(); Q_EMIT persistentStateChanged(); if (!newEntry.mIsValid) { Q_EMIT newTrackByNameInList(newEntry.mTitle, newEntry.mArtist, newEntry.mAlbum); } else { Q_EMIT newTrackByIdInList(newEntry.mId); } Q_EMIT trackHasBeenAdded(data(index(d->mData.size() - 1, 0), ColumnsRoles::TitleRole).toString(), data(index(d->mData.size() - 1, 0), ColumnsRoles::ImageRole).toUrl()); if (!newEntry.mIsValid) { Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0), {MediaPlayList::HasAlbumHeader}); } } bool MediaPlayList::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) { if (sourceParent != destinationParent) { return false; } if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild)) { return false; } auto firstMovedTrackHasHeader = rowHasHeader(sourceRow); auto nextTrackHasHeader = rowHasHeader(sourceRow + count); auto futureNextTrackHasHeader = rowHasHeader(destinationChild); if (sourceRow < destinationChild) { nextTrackHasHeader = rowHasHeader(sourceRow + count); } for (auto cptItem = 0; cptItem < count; ++cptItem) { if (sourceRow < destinationChild) { d->mData.move(sourceRow, destinationChild - 1); d->mTrackData.move(sourceRow, destinationChild - 1); } else { d->mData.move(sourceRow, destinationChild); d->mTrackData.move(sourceRow, destinationChild); } } endMoveRows(); if (sourceRow < destinationChild) { if (firstMovedTrackHasHeader != rowHasHeader(destinationChild - count)) { Q_EMIT dataChanged(index(destinationChild - count, 0), index(destinationChild - count, 0), {ColumnsRoles::HasAlbumHeader}); } } else { if (firstMovedTrackHasHeader != rowHasHeader(destinationChild)) { Q_EMIT dataChanged(index(destinationChild, 0), index(destinationChild, 0), {ColumnsRoles::HasAlbumHeader}); } } if (sourceRow < destinationChild) { if (nextTrackHasHeader != rowHasHeader(sourceRow)) { Q_EMIT dataChanged(index(sourceRow, 0), index(sourceRow, 0), {ColumnsRoles::HasAlbumHeader}); } } else { if (nextTrackHasHeader != rowHasHeader(sourceRow + count)) { Q_EMIT dataChanged(index(sourceRow + count, 0), index(sourceRow + count, 0), {ColumnsRoles::HasAlbumHeader}); } } if (sourceRow < destinationChild) { if (futureNextTrackHasHeader != rowHasHeader(destinationChild + count - 1)) { Q_EMIT dataChanged(index(destinationChild + count - 1, 0), index(destinationChild + count - 1, 0), {ColumnsRoles::HasAlbumHeader}); } } else { if (futureNextTrackHasHeader != rowHasHeader(destinationChild + count)) { Q_EMIT dataChanged(index(destinationChild + count, 0), index(destinationChild + count, 0), {ColumnsRoles::HasAlbumHeader}); } } Q_EMIT persistentStateChanged(); return true; } void MediaPlayList::move(int from, int to, int n) { if (from < to) { moveRows({}, from, n, {}, to + 1); } else { moveRows({}, from, n, {}, to); } } void MediaPlayList::enqueue(MusicAlbum album) { const auto allTracksKeys = album.tracksKeys(); for (const auto oneTrackId : allTracksKeys) { enqueue(oneTrackId); } } void MediaPlayList::enqueue(QString artistName) { beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size()); d->mData.push_back(MediaPlayListEntry{artistName}); d->mTrackData.push_back({}); endInsertRows(); Q_EMIT newArtistInList(artistName); } void MediaPlayList::clearAndEnqueue(MusicAlbum album) { clearPlayList(); enqueue(album); } void MediaPlayList::clearAndEnqueue(QString artistName) { clearPlayList(); enqueue(artistName); } void MediaPlayList::clearPlayList() { beginRemoveRows({}, 0, d->mData.count()); d->mData.clear(); d->mTrackData.clear(); endRemoveRows(); } QList MediaPlayList::persistentState() const { auto result = QList(); for (int trackIndex = 0; trackIndex < d->mData.size(); ++trackIndex) { auto oneData = QList(); const auto &oneEntry = d->mData[trackIndex]; if (oneEntry.mIsValid) { const auto &oneTrack = d->mTrackData[trackIndex]; oneData.push_back(oneTrack.title()); oneData.push_back(oneTrack.albumName()); oneData.push_back(oneTrack.artist()); result.push_back(QVariant(oneData)); } } return result; } MusicListenersManager *MediaPlayList::musicListenersManager() const { return d->mMusicListenersManager; } void MediaPlayList::setPersistentState(QList persistentState) { qDebug() << "MediaPlayList::setPersistentState" << persistentState; for (auto &oneData : persistentState) { auto trackData = oneData.toStringList(); if (trackData.size() != 3) { continue; } auto restoredTitle = trackData[0]; auto restoredAlbum = trackData[1]; auto restoredArtist = trackData[2]; enqueue({restoredTitle, restoredAlbum, restoredArtist}); } emit persistentStateChanged(); } void MediaPlayList::removeSelection(QList selection) { std::sort(selection.begin(), selection.end()); std::reverse(selection.begin(), selection.end()); for (auto oneItem : selection) { removeRow(oneItem); } } void MediaPlayList::albumAdded(const QVector &tracks) { for (int playListIndex = 0; playListIndex < d->mData.size(); ++playListIndex) { auto &oneEntry = d->mData[playListIndex]; if (!oneEntry.mIsArtist || oneEntry.mIsValid) { continue; } if (oneEntry.mArtist != tracks.first().artist()) { continue; } d->mTrackData[playListIndex] = tracks.first(); oneEntry.mId = tracks.first().databaseId(); oneEntry.mIsValid = true; oneEntry.mIsArtist = false; Q_EMIT dataChanged(index(playListIndex, 0), index(playListIndex, 0), {}); beginInsertRows(QModelIndex(), playListIndex + 1, playListIndex - 1 + tracks.size()); for (int trackIndex = 1; trackIndex < tracks.size(); ++trackIndex) { d->mData.push_back(MediaPlayListEntry{tracks[trackIndex].databaseId()}); d->mTrackData.push_back(tracks[trackIndex]); } endInsertRows(); Q_EMIT persistentStateChanged(); } } void MediaPlayList::trackChanged(MusicAudioTrack track) { for (int i = 0; i < d->mData.size(); ++i) { auto &oneEntry = d->mData[i]; if (!oneEntry.mIsArtist && oneEntry.mIsValid && !d->mTrackData[i].isValid()) { if (track.databaseId() != oneEntry.mId) { continue; } d->mTrackData[i] = track; Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); break; } else if (!oneEntry.mIsArtist && !oneEntry.mIsValid) { if (track.title() != oneEntry.mTitle) { continue; } if (track.albumName() != oneEntry.mAlbum) { continue; } if (track.artist() != oneEntry.mArtist) { continue; } d->mTrackData[i] = track; oneEntry.mId = track.databaseId(); oneEntry.mIsValid = true; Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); break; } } } +void MediaPlayList::trackRemoved(MusicAudioTrack track) +{ + for (int i = 0; i < d->mData.size(); ++i) { + auto &oneEntry = d->mData[i]; + + if (oneEntry.mIsValid) { + if (oneEntry.mId == track.databaseId()) { + oneEntry.mTitle = track.title(); + oneEntry.mArtist = track.artist(); + oneEntry.mAlbum = track.albumName(); + + oneEntry.mIsValid = false; + + Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); + } + } + } +} + void MediaPlayList::setMusicListenersManager(MusicListenersManager *musicListenersManager) { if (d->mMusicListenersManager == musicListenersManager) { return; } d->mMusicListenersManager = musicListenersManager; if (d->mMusicListenersManager) { d->mMusicListenersManager->subscribeForTracks(this); } Q_EMIT musicListenersManagerChanged(); } bool MediaPlayList::rowHasHeader(int row) const { if (row >= rowCount()) { return false; } if (row < 0) { return false; } if (row - 1 < 0) { return true; } auto currentAlbum = QString(); if (d->mData[row].mIsValid) { qDebug() << d->mData[row].mId; qDebug() << d->mTrackData[row].albumName(); currentAlbum = d->mTrackData[row].albumName(); } else { currentAlbum = d->mData[row].mAlbum; } auto previousAlbum = QString(); if (d->mData[row - 1].mIsValid) { previousAlbum = d->mTrackData[row - 1].albumName(); } else { previousAlbum = d->mData[row - 1].mAlbum; } if (currentAlbum == previousAlbum) { return false; } return true; } #include "moc_mediaplaylist.cpp" diff --git a/src/mediaplaylist.h b/src/mediaplaylist.h index f004c7e9..f26b77fe 100644 --- a/src/mediaplaylist.h +++ b/src/mediaplaylist.h @@ -1,177 +1,179 @@ /* * 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. */ #ifndef MEDIAPLAYLIST_H #define MEDIAPLAYLIST_H #include "musicaudiotrack.h" #include "musicalbum.h" #include #include class MediaPlayListPrivate; class DatabaseInterface; class MusicListenersManager; class MediaPlayListEntry { public: MediaPlayListEntry() { } explicit MediaPlayListEntry(qulonglong id) : mId(id), mIsValid(true) { } MediaPlayListEntry(QString title, QString album, QString artist) : mTitle(title), mAlbum(album), mArtist(artist) { } explicit MediaPlayListEntry(QString artist) : mArtist(artist), mIsArtist(true) { } QString mTitle; QString mAlbum; QString mArtist; qulonglong mId = 0; bool mIsValid = false; bool mIsArtist = false; bool mIsPlaying = false; }; class MediaPlayList : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QList persistentState READ persistentState WRITE setPersistentState NOTIFY persistentStateChanged) Q_PROPERTY(MusicListenersManager* musicListenersManager READ musicListenersManager WRITE setMusicListenersManager NOTIFY musicListenersManagerChanged) public: enum ColumnsRoles { IsValidRole = Qt::UserRole + 1, TitleRole = IsValidRole + 1, DurationRole = TitleRole + 1, MilliSecondsDurationRole = DurationRole + 1, CreatorRole = MilliSecondsDurationRole + 1, ArtistRole = CreatorRole + 1, AlbumRole = ArtistRole + 1, TrackNumberRole = AlbumRole + 1, DiscNumberRole = TrackNumberRole + 1, RatingRole = DiscNumberRole + 1, ImageRole = RatingRole + 1, ResourceRole = ImageRole + 1, CountRole = ResourceRole + 1, IsPlayingRole = CountRole + 1, HasAlbumHeader = IsPlayingRole + 1, }; Q_ENUM(ColumnsRoles) explicit MediaPlayList(QObject *parent = 0); ~MediaPlayList(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; QHash roleNames() const override; Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; Q_INVOKABLE void enqueue(qulonglong newTrackId); Q_INVOKABLE void enqueue(MediaPlayListEntry newEntry); Q_INVOKABLE void enqueue(MusicAlbum album); Q_INVOKABLE void enqueue(QString artistName); Q_INVOKABLE void clearAndEnqueue(qulonglong newTrackId); Q_INVOKABLE void clearAndEnqueue(MusicAlbum album); Q_INVOKABLE void clearAndEnqueue(QString artistName); Q_INVOKABLE bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override; Q_INVOKABLE void move(int from, int to, int n); Q_INVOKABLE void clearPlayList(); QList persistentState() const; MusicListenersManager* musicListenersManager() const; Q_SIGNALS: void newTrackByNameInList(QString title, QString artist, QString album); void newTrackByIdInList(qulonglong newTrackId); void newArtistInList(QString artist); void trackHasBeenAdded(const QString &title, const QUrl &image); void persistentStateChanged(); void musicListenersManagerChanged(); public Q_SLOTS: void setPersistentState(QList persistentState); void removeSelection(QList selection); void albumAdded(const QVector &tracks); void trackChanged(MusicAudioTrack track); + void trackRemoved(MusicAudioTrack track); + void setMusicListenersManager(MusicListenersManager* musicListenersManager); private Q_SLOTS: bool rowHasHeader(int row) const; private: MediaPlayListPrivate *d; }; #endif // MEDIAPLAYLIST_H diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp index c32615a7..6660e173 100644 --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -1,135 +1,137 @@ /* * 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 "musiclistenersmanager.h" #include "config-upnp-qt.h" #include "databaseinterface.h" #include "mediaplaylist.h" #if defined UPNPQT_FOUND && UPNPQT_FOUND #include "upnp/upnplistener.h" #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND #include "baloo/baloolistener.h" #endif #include "file/filelistener.h" #include #include #include "trackslistener.h" class MusicListenersManagerPrivate { public: QThread mDatabaseThread; #if defined UPNPQT_FOUND && UPNPQT_FOUND UpnpListener mUpnpListener; #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND BalooListener mBalooListener; #endif FileListener mFileListener; DatabaseInterface mDatabaseInterface; }; MusicListenersManager::MusicListenersManager(QObject *parent) : QObject(parent), d(new MusicListenersManagerPrivate) { d->mDatabaseThread.start(); d->mDatabaseInterface.moveToThread(&d->mDatabaseThread); connect(&d->mDatabaseInterface, &DatabaseInterface::requestsInitDone, this, &MusicListenersManager::databaseReady); QMetaObject::invokeMethod(&d->mDatabaseInterface, "init", Qt::QueuedConnection, Q_ARG(QString, QStringLiteral("listeners"))); connect(&d->mDatabaseInterface, &DatabaseInterface::artistAdded, this, &MusicListenersManager::artistAdded); connect(&d->mDatabaseInterface, &DatabaseInterface::albumAdded, this, &MusicListenersManager::albumAdded); connect(&d->mDatabaseInterface, &DatabaseInterface::trackAdded, this, &MusicListenersManager::trackAdded); connect(&d->mDatabaseInterface, &DatabaseInterface::artistRemoved, this, &MusicListenersManager::artistRemoved); connect(&d->mDatabaseInterface, &DatabaseInterface::albumRemoved, this, &MusicListenersManager::albumRemoved); connect(&d->mDatabaseInterface, &DatabaseInterface::trackRemoved, this, &MusicListenersManager::trackRemoved); connect(&d->mDatabaseInterface, &DatabaseInterface::artistModified, this, &MusicListenersManager::artistModified); connect(&d->mDatabaseInterface, &DatabaseInterface::albumModified, this, &MusicListenersManager::albumModified); connect(&d->mDatabaseInterface, &DatabaseInterface::trackModified, this, &MusicListenersManager::trackModified); } MusicListenersManager::~MusicListenersManager() { delete d; } DatabaseInterface *MusicListenersManager::viewDatabase() const { return &d->mDatabaseInterface; } void MusicListenersManager::subscribeForTracks(MediaPlayList *client) { auto helper = new TracksListener(&d->mDatabaseInterface); helper->moveToThread(&d->mDatabaseThread); + connect(this, &MusicListenersManager::trackRemoved, client, &MediaPlayList::trackRemoved); + connect(this, &MusicListenersManager::trackAdded, client, &MediaPlayList::trackChanged); connect(helper, &TracksListener::trackChanged, client, &MediaPlayList::trackChanged); connect(helper, &TracksListener::albumAdded, client, &MediaPlayList::albumAdded); connect(client, &MediaPlayList::newTrackByIdInList, helper, &TracksListener::trackByIdInList); connect(client, &MediaPlayList::newTrackByNameInList, helper, &TracksListener::trackByNameInList); connect(client, &MediaPlayList::newArtistInList, helper, &TracksListener::newArtistInList); connect(&d->mDatabaseInterface, &DatabaseInterface::trackAdded, helper, &TracksListener::trackAdded); } void MusicListenersManager::databaseReady() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.setDatabaseInterface(&d->mDatabaseInterface); d->mBalooListener.moveToThread(&d->mDatabaseThread); #endif #if defined UPNPQT_FOUND && UPNPQT_FOUND d->mUpnpListener.setDatabaseInterface(&d->mDatabaseInterface); d->mUpnpListener.moveToThread(&d->mDatabaseThread); #endif d->mFileListener.setDatabaseInterface(&d->mDatabaseInterface); d->mFileListener.moveToThread(&d->mDatabaseThread); } #include "moc_musiclistenersmanager.cpp"