From d8fa9e7bb393212ea40dc3332ef2e0568052b630 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Fri, 21 Jun 2024 19:00:04 -0700 Subject: [PATCH] service/mpris: add properties for common track metadata This was done to work around bad player implementations sending weird metadata, such as removing the art url halfway through a song. --- src/services/mpris/player.cpp | 84 +++++++++++++++++++++++++++++++++-- src/services/mpris/player.hpp | 37 ++++++++++++++- 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/src/services/mpris/player.cpp b/src/services/mpris/player.cpp index 31ef5c6..3c221c2 100644 --- a/src/services/mpris/player.cpp +++ b/src/services/mpris/player.cpp @@ -1,4 +1,5 @@ #include "player.hpp" +#include #include #include @@ -255,6 +256,11 @@ void MprisPlayer::setVolume(qreal volume) { } QVariantMap MprisPlayer::metadata() const { return this->pMetadata.get(); } +QString MprisPlayer::trackTitle() const { return this->mTrackTitle; } +QString MprisPlayer::trackAlbum() const { return this->mTrackAlbum; } +QString MprisPlayer::trackAlbumArtist() const { return this->mTrackAlbumArtist; } +QVector MprisPlayer::trackArtists() const { return this->mTrackArtists; } +QString MprisPlayer::trackArtUrl() const { return this->mTrackArtUrl; } void MprisPlayer::onMetadataChanged() { emit this->metadataChanged(); @@ -274,7 +280,7 @@ void MprisPlayer::onMetadataChanged() { auto trackidVariant = this->pMetadata.get().value("mpris:trackid"); if (trackidVariant.isValid() && trackidVariant.canConvert()) { - auto trackId = trackidVariant.value(); + auto trackId = trackidVariant.toString(); if (trackId != this->mTrackId) { this->mTrackId = trackId; @@ -285,14 +291,49 @@ void MprisPlayer::onMetadataChanged() { // Helps to catch players without trackid. auto urlVariant = this->pMetadata.get().value("xesam:url"); if (urlVariant.isValid() && urlVariant.canConvert()) { - auto url = urlVariant.value(); + auto url = urlVariant.toString(); - if (url != this->mUrl) { - this->mUrl = url; + if (url != this->mTrackUrl) { + this->mTrackUrl = url; trackChanged = true; } } + auto trackTitle = this->pMetadata.get().value("xesam:title"); + if (trackTitle.isValid() && trackTitle.canConvert()) { + this->setTrackTitle(trackTitle.toString()); + } else if (trackChanged) { + this->setTrackTitle("Unknown Track"); + } + + auto trackAlbum = this->pMetadata.get().value("xesam:album"); + if (trackAlbum.isValid() && trackAlbum.canConvert()) { + this->setTrackAlbum(trackAlbum.toString()); + } else if (trackChanged) { + this->setTrackAlbum("Unknown Album"); + } + + auto trackAlbumArtist = this->pMetadata.get().value("xesam:albumArtist"); + if (trackAlbumArtist.isValid() && trackAlbumArtist.canConvert()) { + this->setTrackAlbumArtist(trackAlbumArtist.toString()); + } else if (trackChanged) { + this->setTrackAlbumArtist("Unknown Artist"); + } + + auto trackArtists = this->pMetadata.get().value("xesam:artist"); + if (trackArtists.isValid() && trackArtists.canConvert>()) { + this->setTrackArtists(trackArtists.value>()); + } else if (trackChanged) { + this->setTrackArtists({}); + } + + auto trackArtUrl = this->pMetadata.get().value("mpris:artUrl"); + if (trackArtUrl.isValid() && trackArtUrl.canConvert()) { + this->setTrackArtUrl(trackArtUrl.toString()); + } else if (trackChanged) { + this->setTrackArtUrl(""); + } + if (trackChanged) { // Some players don't seem to send position updates or seeks on track change. this->pPosition.update(); @@ -300,6 +341,41 @@ void MprisPlayer::onMetadataChanged() { } } +void MprisPlayer::setTrackTitle(QString title) { + if (title == this->mTrackTitle) return; + + this->mTrackTitle = std::move(title); + emit this->trackTitleChanged(); +} + +void MprisPlayer::setTrackAlbum(QString album) { + if (album == this->mTrackAlbum) return; + + this->mTrackAlbum = std::move(album); + emit this->trackAlbumChanged(); +} + +void MprisPlayer::setTrackAlbumArtist(QString albumArtist) { + if (albumArtist == this->mTrackAlbumArtist) return; + + this->mTrackAlbumArtist = std::move(albumArtist); + emit this->trackAlbumArtistChanged(); +} + +void MprisPlayer::setTrackArtists(QVector artists) { + if (artists == this->mTrackArtists) return; + + this->mTrackArtists = std::move(artists); + emit this->trackArtistsChanged(); +} + +void MprisPlayer::setTrackArtUrl(QString artUrl) { + if (artUrl == this->mTrackArtUrl) return; + + this->mTrackArtUrl = std::move(artUrl); + emit this->trackArtUrlChanged(); +} + MprisPlaybackState::Enum MprisPlayer::playbackState() const { return this->mPlaybackState; } void MprisPlayer::setPlaybackState(MprisPlaybackState::Enum playbackState) { diff --git a/src/services/mpris/player.hpp b/src/services/mpris/player.hpp index 94062bc..5de6909 100644 --- a/src/services/mpris/player.hpp +++ b/src/services/mpris/player.hpp @@ -122,7 +122,21 @@ class MprisPlayer: public QObject { /// /// A map of common properties is available [here](https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata). /// Do not count on any of them actually being present. + /// + /// Note that the `trackTitle`, `trackAlbum`, `trackAlbumArtist`, `trackArtists` and `trackArtUrl` + /// 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); + /// 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. + Q_PROPERTY(QString trackAlbum READ trackAlbum NOTIFY trackAlbumChanged); + /// The current track's album artist, or "Unknown Artist" if none was provided. + Q_PROPERTY(QString trackAlbumArtist READ trackAlbumArtist NOTIFY trackAlbumArtistChanged); + /// The current track's artists, or an empty list if none were provided. + Q_PROPERTY(QVector trackArtists READ trackArtists NOTIFY trackArtistsChanged); + /// The current track's art url, or `""` if none was provided. + Q_PROPERTY(QString trackArtUrl READ trackArtUrl NOTIFY trackArtUrlChanged); /// The playback state of the media player. /// /// - If `canPlay` is false, you cannot assign the `Playing` state. @@ -234,6 +248,11 @@ public: void setVolume(qreal volume); [[nodiscard]] QVariantMap metadata() const; + [[nodiscard]] QString trackTitle() const; + [[nodiscard]] QString trackAlbum() const; + [[nodiscard]] QString trackAlbumArtist() const; + [[nodiscard]] QVector trackArtists() const; + [[nodiscard]] QString trackArtUrl() const; [[nodiscard]] MprisPlaybackState::Enum playbackState() const; void setPlaybackState(MprisPlaybackState::Enum playbackState); @@ -280,6 +299,11 @@ signals: void volumeChanged(); void volumeSupportedChanged(); void metadataChanged(); + void trackTitleChanged(); + void trackAlbumChanged(); + void trackAlbumArtistChanged(); + void trackArtistsChanged(); + void trackArtUrlChanged(); void playbackStateChanged(); void loopStateChanged(); void loopSupportedChanged(); @@ -302,6 +326,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); + // clang-format off dbus::DBusPropertyGroup appProperties; dbus::DBusProperty pIdentity {this->appProperties, "Identity"}; @@ -340,7 +370,12 @@ private: DBusMprisPlayerApp* app = nullptr; DBusMprisPlayer* player = nullptr; QString mTrackId; - QString mUrl; + QString mTrackUrl; + QString mTrackTitle; + QString mTrackAlbum; + QString mTrackAlbumArtist; + QVector mTrackArtists; + QString mTrackArtUrl; }; } // namespace qs::service::mpris