Skip to content

feat(linux): add MPRIS media controls - #653

Open
wiktorekdev wants to merge 4 commits into
NuvioMedia:Devfrom
wiktorekdev:feature/linux-mpris-integration
Open

wiktorekdev wants to merge 4 commits into
NuvioMedia:Devfrom
wiktorekdev:feature/linux-mpris-integration

Conversation

@wiktorekdev

@wiktorekdev wiktorekdev commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Expose the Linux player through MPRIS so desktop media controls and tools such as playerctl can read playback state and control playback, seeking, volume, speed, and episode navigation.

PR type

  • Reproducible bug fix
  • UI glitch/bug fix
  • Behavior bug/regression fix
  • Small maintenance only, with no UI or behavior change
  • Docs accuracy fix
  • Translation/localization only
  • Approved larger or directional change

This is a new feature associated with #565 and does not yet have explicit maintainer approval.

Why

Nuvio currently does not expose its player through the standard Linux media-control interface requested in #565.

Desktop scope

Linux desktop only. The D-Bus implementation and dependencies are included only in Linux builds. Other desktop platforms use a no-op bridge.

Issue or approval

Related to #565. The request is open but does not contain explicit maintainer approval.

UI / behavior impact

  • No UI change
  • No behavior change
  • UI changed only to fix a documented glitch/bug
  • Behavior changed only to fix a documented bug/regression
  • UI change has explicit maintainer approval
  • Behavior change has explicit maintainer approval

Policy check

  • I have read and understood CONTRIBUTING.md.
  • This PR is small, focused, and limited to one problem.
  • This PR is scoped to the desktop app, desktop packaging, desktop documentation, or shared code required for desktop behavior.
  • This PR is not cosmetic-only.
  • Any UI change fixes a linked glitch/bug and includes visual proof, or this PR has no UI change.
  • Any behavior change fixes a linked bug/regression or has explicit approval, or this PR has no behavior change.
  • This PR does not bundle unrelated refactors, cleanups, formatting, or drive-by changes.
  • This PR does not add dependencies, architecture changes, migrations, or product-direction changes without explicit approval.
  • I listed the testing performed below.

Scope boundaries

No UI, URI-handler, taskbar, playback-setting, repeat, or shuffle changes are included.

Testing

  • ./gradlew :composeApp:desktopTest --tests com.nuvio.app.features.player.desktop.MprisBridgeTest --no-daemon
  • Live session D-Bus test with a local video
  • playerctl status, pause, play, and volume checks
  • Verified separate root/player property maps and exported player metadata
  • git diff --check

The focused desktop test suite passed on the current upstream Dev. KDE widgets, lock-screen controls, and Bluetooth media buttons were not tested.

Screenshots / Video

Not a UI change.

Breaking changes

None.

Linked issues

Related to #565.

@KhooLy

KhooLy commented Sep 10, 2026

Copy link
Copy Markdown
Member

Looks promising, but I found three blockers: MprisBridgeTest is in generic desktopTest while both dbus-java and MprisObject only exist on Linux, so non-Linux desktop tests will not compile. Previous dispatches playPreviousEpisode, but there is no handler for that event, so previous-episode navigation is currently broken. Also, the MPRIS track ID is regenerated whenever any PlayerNowPlayingInfo field changes; it should remain stable for the same media item and only change when the actual track/item changes.

@wiktorekdev

Copy link
Copy Markdown
Contributor Author

I’ll take a look

@wiktorekdev

Copy link
Copy Markdown
Contributor Author

Fixed all three blockers:

  • Moved the MPRIS bridge tests to the Linux-only desktop test source set.
  • Wired the previous-episode event into the existing episode playback flow.
  • Made MPRIS track IDs stable for the same media item and rotate only when the item changes.

While testing the real app, I also found that metadata published before the native player attached was being cleared during MPRIS registration. That lifecycle case is fixed and covered by regression tests.

Verified with the full desktop test suite: 950 tests passing. I also tested the flow in Nuvio—sending Previous through MPRIS while paused near the beginning of an episode correctly opened the previous episode.

@KhooLy

KhooLy commented Sep 11, 2026

Copy link
Copy Markdown
Member

The previous three blockers are fixed, thanks. I found one remaining logic issue: CanGoPrevious is derived from the raw episodeItems index, while the actual Previous action resolves episodes after sorting by season/episode. With unsorted addon episode lists, MPRIS can advertise the wrong capability. Please derive the capability from the same episode-ordering logic used by resolvePreviousEpisode(). Also, the new generic previous-episode/autoplay changes live in commonMain while Mobile still has the old shared implementation; please upstream that shared behavior to Mobile first or keep the MPRIS change isolated to the desktop seam.

@wiktorekdev

Copy link
Copy Markdown
Contributor Author

Pushed the follow-up fix.

  • CanGoPrevious now uses the same season/episode ordering as the actual Previous action, so unsorted addon episode lists no longer produce a mismatched capability.
  • The previous-episode ordering/resolution is kept desktop-only.
  • I removed the earlier shared Previous/autoplay changes from commonMain; Mobile behavior is unchanged.
  • The desktop Previous action now passes the resolved original episode index through a small platform-neutral playEpisodeAtIndex seam, reusing the existing autoplay pipeline without duplicating it.

I also re-tested MPRIS live: Previous, Next, Play/Pause, Seek, Volume, Rate and Stop are working, including Previous from S1E2 -> S1E1.

@KhooLy

KhooLy commented Sep 11, 2026

Copy link
Copy Markdown
Member

Thanks, the previous fixes look good. The CanGoPrevious ordering issue and the Linux-only test setup are resolved.

I still see two blockers before merging:

  1. The stable MPRIS track ID fix appears to have been regressed in the latest commit. itemId was removed again, and MprisObject.update() now rotates the track ID whenever PlayerNowPlayingInfo changes. Metadata changes for the same media item should not generate a new MPRIS track ID. Please restore a stable media identity and add a regression test that verifies metadata-only updates keep the same track ID.

  2. playEpisodeAtIndex and its event handler are still added in commonMain. Since NuvioMobile is the canonical upstream for shared player behavior, please either upstream this shared seam to Mobile first or keep the MPRIS previous-episode path entirely desktop-specific.

After those are addressed, please update/rebase the branch onto the current Dev and rerun the desktop/Linux tests.

@wiktorekdev
wiktorekdev force-pushed the feature/linux-mpris-integration branch from 0a5a497 to 55449e8 Compare September 12, 2026 11:07
@wiktorekdev

Copy link
Copy Markdown
Contributor Author

not ready yet

@wiktorekdev

Copy link
Copy Markdown
Contributor Author

I opened NuvioMedia/NuvioMobile#1928 to move playEpisodeAtIndex upstream.

Once that’s merged, I’ll rebase this PR, use the shared action for MPRIS Previous.

The stable track ID fix is already back in place with a regression test.

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.

2 participants