Skip to content

fix: export one coherent track to mpris while the now-playing text is held - #568

Merged
Tiaxi merged 2 commits into
mainfrom
fix/mpris-coherent-track-during-hold
Nov 10, 2026
Merged

Tiaxi merged 2 commits into
mainfrom
fix/mpris-coherent-track-during-hold

Conversation

@Tiaxi

@Tiaxi Tiaxi commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Closes #565

What changes

Since #563, BridgeClient holds the now-playing text (title, artist, album, genre, year, number) of the track before until the new track's queue row or metadata arrives. During that hold, MPRIS exported the new track's mpris:trackid, URL and length next to the old track's text. This PR uses the issue's first option: MPRIS exports the track the now-playing text describes, and always exports it whole.

  • BridgeClient::currentTrackTextPath() exposes the track the text describes (m_currentTrackTextPath).
  • Text-path changes are metadata changes: a change of m_currentTrackTextPath now sets the track-metadata change, even when the two tracks' tags read the same. Before, the path changed silently, and nothing told MPRIS that the hold had ended.
  • BridgeClient::currentTrackCoverTrackPath() records, where the cover is chosen, which track the cover belongs to. Since fix: keep the previous cover until the next track's cover is known #523, the bridge keeps the previous track's cover until the new track's own is known, which can be after its text resolves (from a queue row with no cover, or a stop). A change of this track also counts as a metadata change, so the same image becoming the new track's own is published.
  • MprisController exports exportedTrackPath(): the text's track, or the playing track when the text describes none (the first track after start-up). The rules:
    • During the hold, the metadata last built for the track before stays whole: ID, URL, length, art, album artist and text. Rebuild triggers (identity, metadata, length, art links) leave it alone.
    • When the text resolves, everything switches in one PropertiesChanged. mpris:artUrl is exported only when the bridge's cover belongs to the exported track; until then it is left out, never the previous track's. The album artist already comes only from the track's own metadata.
    • A controller that never exported the held track (one created during a hold) exports that track's ID, URL and text, without a length or art.
    • A stop, a second track change, an untagged track and Repeat-One need no extra code here: the export follows the bridge's text path. A stop resolves the text to the stopped track. A second change keeps the first track's export until the third track's text resolves, and the skipped track is never exported. Metadata with empty tags still ends the hold. With Repeat-One, the path and ID stay the same.

Playback commands

  • SetPosition accepts the exported ID and the playing track's ID, and seeks the playing track, clamped to its duration. The MPRIS spec ignores a call whose ID is not the current track's as stale. A client can only learn the current track from Metadata, so during the hold a correct client sends the exported ID (the track before's), and it means the playing track. Rejecting that call would break seeking from the applet, the lock screen or KDE Connect for the length of the hold. Accepting the playing track's own ID as well means that a client that knows it is never refused either. Any other ID is still stale.
  • Position and CanSeek stay the playing track's, as before.
  • Seeked: the old rule (a new play of the same exported track) is generalized: a client takes a new trackid to start at 0, and the same trackid to keep playing on. So Seeked is now sent when exactly one of "a new play started" and "the export moved to another track" happens, unless playback is stopped:
    • The next track starts during the hold: the ID stays the track before's, but the position starts over. Previously this happened only on a Repeat-One replay, which is still covered.
    • The hold ends after the track has been playing: the new ID arrives at a position other than 0, so Seeked follows the PropertiesChanged with the real position.
    • A normal track change changes both at once, so no Seeked is sent, as before.

Known trade-offs of option 1

The hold usually lasts only until the metadata reader's first result.

  • Position can exceed the exported length: during the hold, Position (the playing track's) is shown against the exported length (the track before's). Seeked keeps the position itself right.
  • A delayed SetPosition can land on the next track: a SetPosition carrying the previous track's ID, sent while that track was really playing but arriving after the next one started, seeks the next track. During the hold it can't be told apart from a valid call by a client that read the exported metadata, and that call must not be refused.

Tests

All in ui/tests/tst_bridge_client.cpp, through BridgeClient::processBinarySnapshot and MprisController with a signal sink:

  • mprisExportsTheTrackBeforeWholeWhileTheTextIsHeld: during the hold, the metadata stays the track before's (whole map, including length) across identity, heartbeat and new-length signals, and no Metadata change is published. The switch is one PropertiesChanged with the new track whole. Every published Metadata is checked for an ID paired with another track's URL, title, artist, album or length. The test also covers the Seeked behaviour and Position.
  • mprisSetPositionDuringTheHoldSeeksThePlayingTrack: SetPosition with the exported ID (past the old track's length, within the new one's) and with the playing ID both seek. An unrelated ID is ignored, and a relative Seek works.
  • mprisHoldFollowsASecondTrackChangeAndEndsOnStop: a second change during the hold, an untagged track, and a stop during the hold. No published metadata names the skipped track or mixes two tracks, and the stop is not reported as a seek.
  • mprisExportsNoArtOfTheTrackBeforeAfterTheRowEndsTheHold: the tracks have distinct art. The next track's queue row (without a cover) ends the hold before its metadata arrives. MPRIS exports the new track without art or album artist, then with its own once its metadata arrives.
  • mprisExportsNoArtOfTheTrackBeforeAfterAStopEndsTheHold: a stop during the hold exports the stopped track under its file name, without the previous track's art or album artist.
  • Coherence check: it now also checks mpris:artUrl and xesam:albumArtist (either may be left out, but must never be another track's).
  • mprisHoldEndsForATrackWhoseTextReadsTheSame: the bridge reports the text-path change, and MPRIS exports the new ID.
  • Fix check: with exportedTrackPath() reverted to the playing path, the four hold tests fail.
  • Validation: ./scripts/run-tests.sh --ui-only passes: 102/102 CTest tests, including the no-private-Qt-API checks, with no new compiler warnings. No Rust changes.

@Tiaxi
Tiaxi merged commit d6f693f into main Nov 10, 2026
5 checks passed
@Tiaxi
Tiaxi deleted the fix/mpris-coherent-track-during-hold branch November 10, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: export one coherent track to MPRIS while the now-playing text is held

1 participant