Fix MPRIS player duplication during internet radio (#1048) (#1069)

* fix(mpris): disable WebKit media session so radio doesn't duplicate the player

Internet radio plays through an HTML <audio> element, for which WebKitGTK
auto-registers its own MPRIS player (org.webkit.*) alongside the app's
souvlaki one. On Linux desktops that list every player, now-playing then
showed twice during radio (issue #1048: one "psysonic", one "Psysonic").

Disable the WebKit media session at main-window setup (enable-media-session,
set by GObject property name since the pinned binding has no typed setter,
guarded by find_property) so souvlaki stays the single now-playing source;
radio metadata still reaches it via mpris_set_metadata. The navigator.media
Session push in useRadioMprisSync is kept as a fallback in case a WebKitGTK
version still registers the player, so issue #816 cannot regress.

* docs(changelog): MPRIS radio duplication fix (PR #1069)
This commit is contained in:
Psychotoxical
2026-06-11 23:58:01 +02:00
committed by GitHub
parent 3de7b57cc5
commit 4902c0e25b
4 changed files with 35 additions and 1 deletions
+6
View File
@@ -191,6 +191,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Fixed ## Fixed
### Internet radio — no more duplicate now-playing on Linux
**By [@Psychotoxical](https://github.com/Psychotoxical), reported by agriffit79, PR [#1069](https://github.com/Psychotoxical/psysonic/pull/1069)**
* On Linux, playing an internet radio station showed the track twice in the desktop "now playing" overlay — once from the app's own media controls and once from a second player the web engine registered for the stream. The extra player is now suppressed, so radio shows a single entry like regular tracks.
### Fullscreen player — title cleanup ### Fullscreen player — title cleanup
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1068](https://github.com/Psychotoxical/psysonic/pull/1068)** **By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1068](https://github.com/Psychotoxical/psysonic/pull/1068)**
+3
View File
@@ -434,6 +434,9 @@ pub fn run() {
let _ = win.set_decorations(false); let _ = win.set_decorations(false);
let _ = linux_webkit_apply_wayland_gpu_font_tuning(&win); let _ = linux_webkit_apply_wayland_gpu_font_tuning(&win);
let _ = linux_webkit_reapply_cached_wayland_text_render_profile(&win); let _ = linux_webkit_reapply_cached_wayland_text_render_profile(&win);
// Suppress WebKit's own MPRIS player so radio (HTML <audio>)
// doesn't duplicate the souvlaki one (issue #1048).
let _ = linux_webkit_disable_media_session(&win);
} }
} }
+2 -1
View File
@@ -27,7 +27,8 @@ pub(crate) use platform::{
}; };
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub(crate) use platform::{ pub(crate) use platform::{
linux_webkit_apply_wayland_gpu_font_tuning, linux_webkit_reapply_cached_wayland_text_render_profile, linux_webkit_apply_wayland_gpu_font_tuning, linux_webkit_disable_media_session,
linux_webkit_reapply_cached_wayland_text_render_profile,
sync_wayland_text_profile_cache_from_disk, sync_wayland_text_profile_cache_from_disk,
}; };
pub(crate) use integration::{ pub(crate) use integration::{
@@ -136,6 +136,30 @@ pub(crate) fn linux_webkit_apply_wayland_gpu_font_tuning(win: &tauri::WebviewWin
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
} }
/// WebKitGTK auto-registers its **own** MPRIS player whenever an HTML
/// `<audio>`/`<video>` element plays — internet radio uses one (`radioPlayer.ts`).
/// That duplicates the app's souvlaki MPRIS player on Linux desktops that list
/// every player (issue #1048: one feed as "psysonic", one as "Psysonic").
/// Disabling the WebKit media session stops that registration, leaving souvlaki
/// as the single now-playing source; radio metadata still reaches it through
/// `mpris_set_metadata`.
#[cfg(target_os = "linux")]
pub(crate) fn linux_webkit_disable_media_session(win: &tauri::WebviewWindow) -> Result<(), String> {
win.with_webview(|platform| {
use webkit2gtk::glib::prelude::ObjectExt;
use webkit2gtk::WebViewExt;
if let Some(settings) = platform.inner().settings() {
// `enable-media-session` (WebKitGTK 2.40+) has no typed setter in the
// pinned binding; set it by GObject property name. The find_property
// guard keeps older runtimes from panicking on an unknown property.
if settings.find_property("enable-media-session").is_some() {
settings.set_property("enable-media-session", false);
}
}
})
.map_err(|e| e.to_string())
}
/// Toggle native window decorations at runtime (Linux custom title bar opt-out). /// Toggle native window decorations at runtime (Linux custom title bar opt-out).
/// Tauri command: true when theme animations may be costly on this setup — /// Tauri command: true when theme animations may be costly on this setup —
/// Linux with the Nvidia WebKit quirk active (recorded once at startup) or /// Linux with the Nvidia WebKit quirk active (recorded once at startup) or