From abc0201f6e3d2b751403521574beac455cbf8a2b Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Mon, 29 Jul 2024 01:34:44 -0700 Subject: [PATCH] service/mpris: add uniqueId property to detect track changes Also emits all property changes after trackChanged --- src/core/types.cpp | 19 ++++++++++ src/core/types.hpp | 26 +++++++++++++ src/services/mpris/player.cpp | 70 ++++++++++++++++++++--------------- src/services/mpris/player.hpp | 23 +++++++++--- 4 files changed, 104 insertions(+), 34 deletions(-) diff --git a/src/core/types.cpp b/src/core/types.cpp index 5ed63a02..d9c025fb 100644 --- a/src/core/types.cpp +++ b/src/core/types.cpp @@ -21,3 +21,22 @@ Qt::Edges Edges::toQt(Edges::Flags edges) { return Qt::Edges(edges.toInt()); } bool Edges::isOpposing(Edges::Flags edges) { return edges.testFlags(Edges::Top | Edges::Bottom) || edges.testFlags(Edges::Left | Edges::Right); } + +DropEmitter::DropEmitter(DropEmitter&& other) noexcept: object(other.object), signal(other.signal) { + other.object = nullptr; +} + +DropEmitter& DropEmitter::operator=(DropEmitter&& other) noexcept { + this->object = other.object; + this->signal = other.signal; + other.object = nullptr; + return *this; +} + +DropEmitter::~DropEmitter() { this->call(); } + +void DropEmitter::call() { + if (!this->object) return; + this->signal(this->object); + this->object = nullptr; +} diff --git a/src/core/types.hpp b/src/core/types.hpp index 43224d82..a23ff085 100644 --- a/src/core/types.hpp +++ b/src/core/types.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -68,3 +69,28 @@ bool isOpposing(Flags edges); }; // namespace Edges Q_DECLARE_OPERATORS_FOR_FLAGS(Edges::Flags); + +// NOLINTBEGIN +#define DROP_EMIT(object, func) \ + DropEmitter(object, static_cast([](typeof(object) o) { o->func(); })) +// NOLINTEND + +class DropEmitter { +public: + template + DropEmitter(O* object, void (*signal)(O*)) + : object(object) + , signal(*reinterpret_cast(signal)) {} // NOLINT + + DropEmitter() = default; + DropEmitter(DropEmitter&& other) noexcept; + DropEmitter& operator=(DropEmitter&& other) noexcept; + ~DropEmitter(); + Q_DISABLE_COPY(DropEmitter); + + void call(); + +private: + void* object = nullptr; + void (*signal)(void*) = nullptr; +}; diff --git a/src/services/mpris/player.cpp b/src/services/mpris/player.cpp index 3c221c25..8a06319c 100644 --- a/src/services/mpris/player.cpp +++ b/src/services/mpris/player.cpp @@ -12,6 +12,7 @@ #include #include +#include "../../core/types.hpp" #include "../../dbus/properties.hpp" #include "dbus_player.h" #include "dbus_player_app.h" @@ -255,6 +256,7 @@ void MprisPlayer::setVolume(qreal volume) { this->pVolume.write(); } +quint32 MprisPlayer::uniqueId() const { return this->mUniqueId; } QVariantMap MprisPlayer::metadata() const { return this->pMetadata.get(); } QString MprisPlayer::trackTitle() const { return this->mTrackTitle; } QString MprisPlayer::trackAlbum() const { return this->mTrackAlbum; } @@ -271,10 +273,7 @@ void MprisPlayer::onMetadataChanged() { length = lengthVariant.value(); } - if (length != this->mLength) { - this->mLength = length; - emit this->lengthChanged(); - } + auto emitLengthChanged = this->setLength(length); auto trackChanged = false; @@ -299,81 +298,94 @@ void MprisPlayer::onMetadataChanged() { } } + DropEmitter emitTrackTitle; auto trackTitle = this->pMetadata.get().value("xesam:title"); if (trackTitle.isValid() && trackTitle.canConvert()) { - this->setTrackTitle(trackTitle.toString()); + emitTrackTitle = this->setTrackTitle(trackTitle.toString()); } else if (trackChanged) { - this->setTrackTitle("Unknown Track"); + emitTrackTitle = this->setTrackTitle("Unknown Track"); } + DropEmitter emitTrackAlbum; auto trackAlbum = this->pMetadata.get().value("xesam:album"); if (trackAlbum.isValid() && trackAlbum.canConvert()) { - this->setTrackAlbum(trackAlbum.toString()); + emitTrackAlbum = this->setTrackAlbum(trackAlbum.toString()); } else if (trackChanged) { - this->setTrackAlbum("Unknown Album"); + emitTrackAlbum = this->setTrackAlbum("Unknown Album"); } + DropEmitter emitTrackAlbumArtist; auto trackAlbumArtist = this->pMetadata.get().value("xesam:albumArtist"); if (trackAlbumArtist.isValid() && trackAlbumArtist.canConvert()) { - this->setTrackAlbumArtist(trackAlbumArtist.toString()); + emitTrackAlbumArtist = this->setTrackAlbumArtist(trackAlbumArtist.toString()); } else if (trackChanged) { - this->setTrackAlbumArtist("Unknown Artist"); + emitTrackAlbumArtist = this->setTrackAlbumArtist("Unknown Artist"); } + DropEmitter emitTrackArtists; auto trackArtists = this->pMetadata.get().value("xesam:artist"); if (trackArtists.isValid() && trackArtists.canConvert>()) { - this->setTrackArtists(trackArtists.value>()); + emitTrackArtists = this->setTrackArtists(trackArtists.value>()); } else if (trackChanged) { - this->setTrackArtists({}); + emitTrackArtists = this->setTrackArtists({}); } + DropEmitter emitTrackArtUrl; auto trackArtUrl = this->pMetadata.get().value("mpris:artUrl"); if (trackArtUrl.isValid() && trackArtUrl.canConvert()) { - this->setTrackArtUrl(trackArtUrl.toString()); + emitTrackArtUrl = this->setTrackArtUrl(trackArtUrl.toString()); } else if (trackChanged) { - this->setTrackArtUrl(""); + emitTrackArtUrl = this->setTrackArtUrl(""); } if (trackChanged) { + this->mUniqueId++; // Some players don't seem to send position updates or seeks on track change. this->pPosition.update(); emit this->trackChanged(); } } -void MprisPlayer::setTrackTitle(QString title) { - if (title == this->mTrackTitle) return; +DropEmitter MprisPlayer::setLength(qlonglong length) { + if (length == this->mLength) return DropEmitter(); + + this->mLength = length; + return DROP_EMIT(this, lengthChanged); +} + +DropEmitter MprisPlayer::setTrackTitle(QString title) { + if (title == this->mTrackTitle) return DropEmitter(); this->mTrackTitle = std::move(title); - emit this->trackTitleChanged(); + return DROP_EMIT(this, trackTitleChanged); } -void MprisPlayer::setTrackAlbum(QString album) { - if (album == this->mTrackAlbum) return; +DropEmitter MprisPlayer::setTrackAlbum(QString album) { + if (album == this->mTrackAlbum) return DropEmitter(); this->mTrackAlbum = std::move(album); - emit this->trackAlbumChanged(); + return DROP_EMIT(this, trackAlbumChanged); } -void MprisPlayer::setTrackAlbumArtist(QString albumArtist) { - if (albumArtist == this->mTrackAlbumArtist) return; +DropEmitter MprisPlayer::setTrackAlbumArtist(QString albumArtist) { + if (albumArtist == this->mTrackAlbumArtist) return DropEmitter(); this->mTrackAlbumArtist = std::move(albumArtist); - emit this->trackAlbumArtistChanged(); + return DROP_EMIT(this, trackAlbumArtistChanged); } -void MprisPlayer::setTrackArtists(QVector artists) { - if (artists == this->mTrackArtists) return; +DropEmitter MprisPlayer::setTrackArtists(QVector artists) { + if (artists == this->mTrackArtists) return DropEmitter(); this->mTrackArtists = std::move(artists); - emit this->trackArtistsChanged(); + return DROP_EMIT(this, trackArtistsChanged); } -void MprisPlayer::setTrackArtUrl(QString artUrl) { - if (artUrl == this->mTrackArtUrl) return; +DropEmitter MprisPlayer::setTrackArtUrl(QString artUrl) { + if (artUrl == this->mTrackArtUrl) return DropEmitter(); this->mTrackArtUrl = std::move(artUrl); - emit this->trackArtUrlChanged(); + return DROP_EMIT(this, trackArtUrlChanged); } MprisPlaybackState::Enum MprisPlayer::playbackState() const { return this->mPlaybackState; } diff --git a/src/services/mpris/player.hpp b/src/services/mpris/player.hpp index 4f3154d5..627512f1 100644 --- a/src/services/mpris/player.hpp +++ b/src/services/mpris/player.hpp @@ -7,6 +7,7 @@ #include #include "../../core/doc.hpp" +#include "../../core/types.hpp" #include "../../dbus/properties.hpp" #include "dbus_player.h" #include "dbus_player_app.h" @@ -128,6 +129,11 @@ class MprisPlayer: public QObject { /// properties have extra logic to guard against bad players sending weird metadata, and should /// be used over grabbing the properties directly from the metadata. Q_PROPERTY(QVariantMap metadata READ metadata NOTIFY metadataChanged); + /// An opaque identifier for the current track unique within the current player. + /// + /// > [!WARNING] This is NOT `mpris:trackid` as that is sometimes missing or nonunique + /// > in some players. + Q_PROPERTY(quint32 uniqueId READ uniqueId NOTIFY trackChanged); /// The title of the current track, or "Unknown Track" if none was provided. Q_PROPERTY(QString trackTitle READ trackTitle NOTIFY trackTitleChanged); /// The current track's album, or "Unknown Album" if none was provided. @@ -248,6 +254,7 @@ public: [[nodiscard]] bool volumeSupported() const; void setVolume(qreal volume); + [[nodiscard]] quint32 uniqueId() const; [[nodiscard]] QVariantMap metadata() const; [[nodiscard]] QString trackTitle() const; [[nodiscard]] QString trackAlbum() const; @@ -278,6 +285,10 @@ public: [[nodiscard]] QList supportedMimeTypes() const; signals: + /// The track has changed. + /// + /// All track info change signalss will fire immediately after if applicable, + /// but their values will be updated before the signal fires. void trackChanged(); QSDOC_HIDE void ready(); @@ -327,11 +338,12 @@ private slots: void onLoopStatusChanged(); private: - void setTrackTitle(QString title); - void setTrackAlbum(QString album); - void setTrackAlbumArtist(QString albumArtist); - void setTrackArtists(QVector artists); - void setTrackArtUrl(QString artUrl); + DropEmitter setLength(qlonglong length); + DropEmitter setTrackTitle(QString title); + DropEmitter setTrackAlbum(QString album); + DropEmitter setTrackAlbumArtist(QString albumArtist); + DropEmitter setTrackArtists(QVector artists); + DropEmitter setTrackArtUrl(QString artUrl); // clang-format off dbus::DBusPropertyGroup appProperties; @@ -370,6 +382,7 @@ private: DBusMprisPlayerApp* app = nullptr; DBusMprisPlayer* player = nullptr; + quint32 mUniqueId = 0; QString mTrackId; QString mTrackUrl; QString mTrackTitle;