From 31144dff461db47bf37533b02b877d821c669298 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Fri, 3 Nov 2023 18:20:41 +0100 Subject: [PATCH] player: Implement MPRIS --- Cargo.lock | 180 ++++++++++++++++++++++++++++++---------- Cargo.toml | 1 + de.johrpan.musicus.json | 2 + src/player.rs | 62 ++++++++++++-- src/player_bar.rs | 20 +---- src/playlist_item.rs | 15 ++++ 6 files changed, 213 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6f7402..eb6c0eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.0" @@ -85,9 +91,9 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c0466dfa8c0ee78deef390c274ad756801e0a6dbb86c5ef0924a298c5761c4d" dependencies = [ - "bitflags", + "bitflags 2.4.0", "cairo-sys-rs", - "glib", + "glib 0.18.2", "libc", "once_cell", "thiserror", @@ -99,7 +105,7 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ - "glib-sys", + "glib-sys 0.18.1", "libc", "system-deps", ] @@ -146,6 +152,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "dbus" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819" +dependencies = [ + "libc", + "libdbus-sys", +] + [[package]] name = "either" version = "1.9.0" @@ -251,7 +267,7 @@ checksum = "bbc9c2ed73a81d556b65d08879ba4ee58808a6b1927ce915262185d6d547c6f3" dependencies = [ "gdk-pixbuf-sys", "gio", - "glib", + "glib 0.18.2", "libc", "once_cell", ] @@ -263,8 +279,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps", ] @@ -279,7 +295,7 @@ dependencies = [ "gdk-pixbuf", "gdk4-sys", "gio", - "glib", + "glib 0.18.2", "libc", "pango", ] @@ -293,8 +309,8 @@ dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gio-sys", - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "pango-sys", "pkg-config", @@ -343,7 +359,7 @@ dependencies = [ "futures-io", "futures-util", "gio-sys", - "glib", + "glib 0.18.2", "libc", "once_cell", "pin-project-lite", @@ -357,29 +373,49 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps", "winapi", ] +[[package]] +name = "glib" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros 0.15.13", + "glib-sys 0.15.10", + "gobject-sys 0.15.10", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + [[package]] name = "glib" version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c316afb01ce8067c5eaab1fc4f2cd47dc21ce7b6296358605e2ffab23ccbd19" dependencies = [ - "bitflags", + "bitflags 2.4.0", "futures-channel", "futures-core", "futures-executor", "futures-task", "futures-util", "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", + "glib-macros 0.18.2", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "memchr", "once_cell", @@ -387,6 +423,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "glib-macros" +version = "0.15.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "glib-macros" version = "0.18.2" @@ -401,6 +452,16 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "glib-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "glib-sys" version = "0.18.1" @@ -411,13 +472,24 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gobject-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +dependencies = [ + "glib-sys 0.15.10", + "libc", + "system-deps", +] + [[package]] name = "gobject-sys" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ - "glib-sys", + "glib-sys 0.18.1", "libc", "system-deps", ] @@ -428,7 +500,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2228cda1505613a7a956cca69076892cfbda84fc2b7a62b94a41a272c0c401" dependencies = [ - "glib", + "glib 0.18.2", "graphene-sys", "libc", ] @@ -439,7 +511,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4144cee8fc8788f2a9b73dc5f1d4e1189d1f95305c4cb7bd9c1af1cfa31f59" dependencies = [ - "glib-sys", + "glib-sys 0.18.1", "libc", "pkg-config", "system-deps", @@ -453,7 +525,7 @@ checksum = "0d958e351d2f210309b32d081c832d7de0aca0b077aa10d88336c6379bd01f7e" dependencies = [ "cairo-rs", "gdk4", - "glib", + "glib 0.18.2", "graphene-rs", "gsk4-sys", "libc", @@ -468,8 +540,8 @@ checksum = "12bd9e3effea989f020e8f1ff3fa3b8c63ba93d43b899c11a118868853a56d55" dependencies = [ "cairo-sys-rs", "gdk4-sys", - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "graphene-sys", "libc", "pango-sys", @@ -486,7 +558,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "glib", + "glib 0.18.2", "gstreamer-sys", "itertools", "libc", @@ -508,7 +580,7 @@ checksum = "0fe38a6d5c1e516ce3fd6069e972a540d315448ed69fdadad739e6c6c6eb2a01" dependencies = [ "atomic_refcell", "cfg-if", - "glib", + "glib 0.18.2", "gstreamer", "gstreamer-base-sys", "libc", @@ -520,8 +592,8 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4ca701f9078fe115b29b24c80910b577f9cb5b039182f050dbadf5933594b64" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "gstreamer-sys", "libc", "system-deps", @@ -533,7 +605,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e68dc9772932f6133a9517742918b13ab5414db1f47e19daebc3027a1c3d20d2" dependencies = [ - "glib", + "glib 0.18.2", "gstreamer", "gstreamer-player-sys", "gstreamer-video", @@ -546,8 +618,8 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5ef4d00b43d0aa94e9a518e6ef4a4c504b4b855304a0a5f4ed1493d5e5ca66c" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "gstreamer-sys", "gstreamer-video-sys", "libc", @@ -560,8 +632,8 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86bf9de67a6ab7af67ac11588f4939e984a936030437219f269fe969d79ad8c" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps", ] @@ -574,7 +646,7 @@ checksum = "01b4d3141362b3d44a684e697d2bc55fea73d023315449cda83f0f4324531d64" dependencies = [ "cfg-if", "futures-channel", - "glib", + "glib 0.18.2", "gstreamer", "gstreamer-base", "gstreamer-video-sys", @@ -587,8 +659,8 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cdc36baab839921b05d2468524da649f373dccc5f966c75e564029dc135b1c" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "gstreamer-base-sys", "gstreamer-sys", "libc", @@ -607,7 +679,7 @@ dependencies = [ "gdk-pixbuf", "gdk4", "gio", - "glib", + "glib 0.18.2", "graphene-rs", "gsk4", "gtk4-macros", @@ -640,8 +712,8 @@ dependencies = [ "gdk-pixbuf-sys", "gdk4-sys", "gio-sys", - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "graphene-sys", "gsk4-sys", "libc", @@ -746,7 +818,7 @@ dependencies = [ "gdk-pixbuf", "gdk4", "gio", - "glib", + "glib 0.18.2", "gtk4", "libadwaita-sys", "libc", @@ -761,8 +833,8 @@ checksum = "5e10aaa38de1d53374f90deeb4535209adc40cc5dba37f9704724169bceec69a" dependencies = [ "gdk4-sys", "gio-sys", - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "gtk4-sys", "libc", "pango-sys", @@ -775,6 +847,15 @@ version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + [[package]] name = "libsqlite3-sys" version = "0.26.0" @@ -829,6 +910,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mpris-player" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be832ec9171fdaf43609d02bb552f4129ba6eacd184bb25186e2906dbd3cf098" +dependencies = [ + "dbus", + "glib 0.15.12", +] + [[package]] name = "muldiv" version = "1.0.1" @@ -846,6 +937,7 @@ dependencies = [ "gtk4", "libadwaita", "log", + "mpris-player", "once_cell", "rand", "rusqlite", @@ -951,7 +1043,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06a9e54b831d033206160096b825f2070cf5fda7e35167b1c01e9e774f9202d1" dependencies = [ "gio", - "glib", + "glib 0.18.2", "libc", "once_cell", "pango-sys", @@ -963,8 +1055,8 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps", ] @@ -1110,7 +1202,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags", + "bitflags 2.4.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", diff --git a/Cargo.toml b/Cargo.toml index 9022a64..db04ae3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ gettext-rs = { version = "0.7", features = ["gettext-system"] } gstreamer-player = "0.21" gtk = { package = "gtk4", version = "0.7", features = ["v4_12", "blueprint"] } log = "0.4" +mpris-player = "0.6" once_cell = "1" rand = "0.8" rusqlite = { version = "0.29", features = ["bundled"] } diff --git a/de.johrpan.musicus.json b/de.johrpan.musicus.json index aa9467d..96de668 100644 --- a/de.johrpan.musicus.json +++ b/de.johrpan.musicus.json @@ -14,6 +14,8 @@ "--device=dri", "--socket=wayland", "--socket=pulseaudio", + "--talk-name=org.mpris.MediaPlayer2.Player", + "--own-name=org.mpris.MediaPlayer2.de.johrpan.musicus", "--filesystem=host" ], "build-options": { diff --git a/src/player.rs b/src/player.rs index 863afaa..deb9696 100644 --- a/src/player.rs +++ b/src/player.rs @@ -3,13 +3,19 @@ use fragile::Fragile; use gstreamer_player::gst; use gtk::{ gio, - glib::{self, Properties}, + glib::{self, clone, Properties}, prelude::*, subclass::prelude::*, }; -use std::cell::{Cell, OnceCell}; +use mpris_player::{MprisPlayer, PlaybackStatus}; +use std::{ + cell::{Cell, OnceCell}, + sync::Arc, +}; mod imp { + use mpris_player::Metadata; + use super::*; #[derive(Properties, Debug, Default)] @@ -31,6 +37,8 @@ mod imp { pub position: Cell, #[property(get, construct_only)] pub player: OnceCell, + + pub mpris: OnceCell>, } impl MusicusPlayer { @@ -39,21 +47,28 @@ mod imp { if let Some(item) = playlist.item(index) { if let Some(old_item) = playlist.item(self.current_index.get()) { - old_item.downcast::() + old_item + .downcast::() .unwrap() .set_is_playing(false); } - + let item = item.downcast::().unwrap(); + self.mpris.get().unwrap().set_metadata(Metadata { + artist: Some(vec![item.make_title()]), + title: item.make_subtitle(), + ..Default::default() + }); + let uri = glib::filename_to_uri(&item.path(), None) - .expect("track path should be parsable as an URI"); - + .expect("track path should be parsable as an URI"); + let player = self.player.get().unwrap(); player.set_uri(Some(&uri)); if self.playing.get() { player.play(); } - + self.current_index.set(index); item.set_is_playing(true); } @@ -80,6 +95,29 @@ mod imp { fn constructed(&self) { self.parent_constructed(); + let mpris = MprisPlayer::new( + "de.johrpan.musicus".to_string(), + "Musicus".to_string(), + "de.johrpan.musicus.desktop".to_string(), + ); + + mpris.set_can_play(true); + mpris.set_can_pause(true); + mpris.set_can_go_previous(true); + mpris.set_can_go_next(true); + mpris.set_can_seek(false); + mpris.set_can_raise(false); + mpris.set_can_set_fullscreen(false); + + let obj = self.obj(); + mpris.connect_play(clone!(@weak obj => move || obj.play())); + mpris.connect_pause(clone!(@weak obj => move || obj.pause())); + mpris.connect_play_pause(clone!(@weak obj => move || obj.play_pause())); + mpris.connect_previous(clone!(@weak obj => move || obj.previous())); + mpris.connect_next(clone!(@weak obj => move || obj.next())); + + self.mpris.set(mpris).expect("mpris should not be set"); + let player = self.player.get().unwrap(); let mut config = player.config(); @@ -174,14 +212,24 @@ impl MusicusPlayer { } } + pub fn play_pause(&self) { + if self.playing() { + self.pause(); + } else { + self.play(); + } + } + pub fn play(&self) { self.player().play(); self.set_playing(true); + self.imp().mpris.get().unwrap().set_playback_status(PlaybackStatus::Playing); } pub fn pause(&self) { self.player().pause(); self.set_playing(false); + self.imp().mpris.get().unwrap().set_playback_status(PlaybackStatus::Paused); } pub fn current_item(&self) -> Option { diff --git a/src/player_bar.rs b/src/player_bar.rs index 08ad3d1..bc24d0f 100644 --- a/src/player_bar.rs +++ b/src/player_bar.rs @@ -40,17 +40,10 @@ mod imp { impl PlayerBar { fn update(&self) { if let Some(item) = self.player.borrow().current_item() { - let mut title = item.title(); + self.title_label.set_label(&item.make_title()); - if let Some(part_title) = item.part_title() { - title.push_str(": "); - title.push_str(&part_title); - } - - self.title_label.set_label(&title); - - if let Some(performances) = item.performers() { - self.subtitle_label.set_label(&performances); + if let Some(subtitle) = item.make_subtitle() { + self.subtitle_label.set_label(&subtitle); self.subtitle_label.set_visible(true); } else { self.subtitle_label.set_visible(false); @@ -179,12 +172,7 @@ impl PlayerBar { #[template_callback] fn play_pause(&self, _: >k::Button) { - let player = self.player(); - if player.playing() { - player.pause(); - } else { - player.play(); - } + self.player().play_pause(); } } diff --git a/src/playlist_item.rs b/src/playlist_item.rs index 3969df4..9f33432 100644 --- a/src/playlist_item.rs +++ b/src/playlist_item.rs @@ -59,4 +59,19 @@ impl PlaylistItem { .property("path", path.as_ref()) .build() } + + pub fn make_title(&self) -> String { + let mut title = self.title(); + + if let Some(part_title) = self.part_title() { + title.push_str(": "); + title.push_str(&part_title); + } + + title + } + + pub fn make_subtitle(&self) -> Option { + self.performers() + } }