Skip to content

Commit

Permalink
service/mpris: hack around more non-compliant players
Browse files Browse the repository at this point in the history
Mpris is currently winning the competition for least compliant clients.
  • Loading branch information
outfoxxed committed Feb 21, 2025
1 parent 1eabf5b commit e6a37b9
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 2 deletions.
32 changes: 32 additions & 0 deletions src/services/mpris/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,24 @@ void MprisPlayer::onMetadataChanged() {
}
}

// Some players (Jellyfin) specify xesam:url or mpris:trackid
// and DON'T ACTUALLY CHANGE THEM WHEN THE TRACK CHANGES.
auto titleVariant = this->bpMetadata.value().value("xesam:title");
if (titleVariant.isValid() && titleVariant.canConvert<QString>()) {
auto title = titleVariant.toString();

if (title != this->mTrackTitle) {
this->mTrackTitle = title;
trackChanged = true;
}
}

Qt::beginPropertyUpdateGroup();

if (trackChanged) {
emit this->trackChanged();
this->bUniqueId = this->bUniqueId + 1;
this->trackChangedBeforeState = true;

// Some players don't seem to send position updates or seeks on track change.
this->pPosition.requestUpdate();
Expand Down Expand Up @@ -386,6 +399,25 @@ void MprisPlayer::setPlaying(bool playing) {
this->togglePlaying();
}

void MprisPlayer::onPlaybackStatusUpdated() {
// Insurance - have not yet seen a player where this particular check is required that doesn't
// require the late query below.
this->pPosition.requestUpdate();

// For exceptionally bad players that update playback timestamps at an indeterminate time AFTER
// updating playback state. (Youtube)
QTimer::singleShot(100, this, [&]() {
this->pPosition.requestUpdate();
});

// For exceptionally bad players that don't update length (or other metadata) until a new track actually
// starts playing, and then don't trigger a metadata update when they do. (Jellyfin)
if (this->trackChangedBeforeState) {
this->trackChangedBeforeState = false;
this->pMetadata.requestUpdate();
}
}

bool MprisPlayer::loopSupported() const { return this->pLoopStatus.exists(); }

void MprisPlayer::setLoopState(MprisLoopState::Enum loopState) {
Expand Down
7 changes: 5 additions & 2 deletions src/services/mpris/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class MprisPlayer: public QObject {
/// Equivalent to calling @@play() if not playing or @@pause() if playing.
///
/// May only be called if @@canTogglePlaying is true, which is equivalent to
/// @@canPlay or @@canPause() depending on the current playback state.
/// @@canPlay or @@canPause depending on the current playback state.
Q_INVOKABLE void togglePlaying();

[[nodiscard]] bool isValid() const;
Expand Down Expand Up @@ -391,6 +391,7 @@ private slots:
private:
void onMetadataChanged();
void onPositionUpdated();
void onPlaybackStatusUpdated();
// call instead of setting bpPosition
void setPosition(qlonglong position);
void requestPositionUpdate() { this->pPosition.requestUpdate(); };
Expand Down Expand Up @@ -462,7 +463,7 @@ private slots:
QS_DBUS_PROPERTY_BINDING(MprisPlayer, qlonglong, pPosition, bpPosition, onPositionUpdated, playerProperties, "Position", false);
QS_DBUS_PROPERTY_BINDING(MprisPlayer, pVolume, bVolume, playerProperties, "Volume", false);
QS_DBUS_PROPERTY_BINDING(MprisPlayer, pMetadata, bpMetadata, playerProperties, "Metadata");
QS_DBUS_PROPERTY_BINDING(MprisPlayer, pPlaybackStatus, bpPlaybackStatus, playerProperties, "PlaybackStatus");
QS_DBUS_PROPERTY_BINDING(MprisPlayer, void, pPlaybackStatus, bpPlaybackStatus, onPlaybackStatusUpdated, playerProperties, "PlaybackStatus", true);
QS_DBUS_PROPERTY_BINDING(MprisPlayer, pLoopStatus, bpLoopStatus, playerProperties, "LoopStatus", false);
QS_DBUS_PROPERTY_BINDING(MprisPlayer, pRate, bRate, playerProperties, "Rate", false);
QS_DBUS_PROPERTY_BINDING(MprisPlayer, pMinRate, bMinRate, playerProperties, "MinimumRate", false);
Expand All @@ -477,6 +478,8 @@ private slots:
DBusMprisPlayer* player = nullptr;
QString mTrackId;
QString mTrackUrl;
QString mTrackTitle;
bool trackChangedBeforeState = false;
};

} // namespace qs::service::mpris

0 comments on commit e6a37b9

Please sign in to comment.