diff --git a/Cargo.toml b/Cargo.toml index 4b5e0fa..928a7e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ tokio-core = "0.1" tokio-timer = "0.2" unicode-width = "0.1.5" dbus = { version = "0.6.4", optional = true } -dbus-tokio = { version = "0.3.0", optional = true } [dependencies.librespot] git = "https://github.com/librespot-org/librespot.git" @@ -42,5 +41,5 @@ features = ["pancurses-backend"] [features] pulseaudio_backend = ["librespot/pulseaudio-backend"] portaudio_backend = ["librespot/portaudio-backend"] -mpris = ["dbus", "dbus-tokio"] +mpris = ["dbus"] default = ["pulseaudio_backend", "mpris"] diff --git a/src/main.rs b/src/main.rs index 23851c4..76e256b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,6 @@ extern crate unicode_width; #[cfg(feature = "mpris")] extern crate dbus; -#[cfg(feature = "mpris")] -extern crate dbus_tokio; #[macro_use] extern crate serde_derive; @@ -53,9 +51,6 @@ use commands::CommandManager; use events::{Event, EventManager}; use spotify::PlayerEvent; -#[cfg(feature = "mpris")] -use mpris::run_dbus_server; - fn init_logger(content: TextContent, write_to_file: bool) { let mut builder = env_logger::Builder::from_default_env(); { @@ -147,13 +142,7 @@ fn main() { ))); #[cfg(feature = "mpris")] - { - let spotify = spotify.clone(); - let queue = queue.clone(); - std::thread::spawn(move || { - run_dbus_server(spotify, queue); - }); - } + let mpris_manager = mpris::MprisManager::new(spotify.clone(), queue.clone()); let search = ui::search::SearchView::new(spotify.clone(), queue.clone()); @@ -347,6 +336,9 @@ fn main() { queue.lock().expect("could not lock queue").next(); } spotify.update_status(state); + + #[cfg(feature = "mpris")] + mpris_manager.update(); } Event::Playlist(event) => playlists.handle_ev(&mut cursive, event), Event::Command(cmd) => { @@ -356,6 +348,9 @@ fn main() { v.set_error(e); }); } + + #[cfg(feature = "mpris")] + mpris_manager.update(); } Event::ScreenChange(name) => match name.as_ref() { "playlists" => playlists.repopulate(&mut cursive), diff --git a/src/mpris.rs b/src/mpris.rs index 59d31c1..9cf7f76 100644 --- a/src/mpris.rs +++ b/src/mpris.rs @@ -1,26 +1,71 @@ use std::collections::HashMap; use std::rc::Rc; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, mpsc}; +use dbus::{Path, SignalArgs}; use dbus::arg::{Variant, RefArg}; -use dbus::tree::{Access}; -use dbus_tokio::AConnection; -use dbus_tokio::tree::{AFactory, ATree, ATreeServer}; - -use tokio::reactor::Handle; -use tokio::runtime::current_thread::Runtime; -use futures::Stream; +use dbus::tree::{Access, Factory}; +use dbus::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged; use queue::Queue; use spotify::{PlayerEvent, Spotify}; -pub fn run_dbus_server(spotify: Arc, queue: Arc>) { +fn get_playbackstatus(spotify: Arc) -> String { + match spotify.get_current_status() { + PlayerEvent::Playing => "Playing", + PlayerEvent::Paused => "Paused", + _ => "Stopped" + }.to_string() +} + +fn get_metadata(queue: Arc>) -> HashMap>> { + let mut hm: HashMap>> = HashMap::new(); + + let queue = queue.lock().expect("could not lock queue"); + let track = queue.get_current(); + + hm.insert("mpris:trackid".to_string(), Variant(Box::new( + track.map(|t| format!("spotify:track:{}", t.id.to_base62())).unwrap_or("".to_string()) + ))); + hm.insert("mpris:length".to_string(), Variant(Box::new( + track.map(|t| t.duration * 1_000_000).unwrap_or(0) + ))); + hm.insert("mpris:artUrl".to_string(), Variant(Box::new( + track.map(|t| t.cover_url.clone()).unwrap_or("".to_string()) + ))); + + hm.insert("xesam:album".to_string(), Variant(Box::new( + track.map(|t| t.album.clone()).unwrap_or("".to_string()) + ))); + hm.insert("xesam:albumArtist".to_string(), Variant(Box::new( + track.map(|t| t.album_artists.clone()).unwrap_or(Vec::new()) + ))); + hm.insert("xesam:artist".to_string(), Variant(Box::new( + track.map(|t| t.artists.clone()).unwrap_or(Vec::new()) + ))); + hm.insert("xesam:discNumber".to_string(), Variant(Box::new( + track.map(|t| t.disc_number).unwrap_or(0) + ))); + hm.insert("xesam:title".to_string(), Variant(Box::new( + track.map(|t| t.title.clone()).unwrap_or("".to_string()) + ))); + hm.insert("xesam:trackNumber".to_string(), Variant(Box::new( + track.map(|t| t.track_number).unwrap_or(0) + ))); + hm.insert("xesam:url".to_string(), Variant(Box::new( + track.map(|t| t.url.clone()).unwrap_or("".to_string()) + ))); + + hm +} + +fn run_dbus_server(spotify: Arc, queue: Arc>, rx: mpsc::Receiver<()>) { let conn = Rc::new(dbus::Connection::get_private(dbus::BusType::Session) .expect("Failed to connect to dbus")); conn.register_name("org.mpris.MediaPlayer2.ncspot", dbus::NameFlag::ReplaceExisting as u32) .expect("Failed to register dbus player name"); - let f = AFactory::new_afn::<()>(); + let f = Factory::new_fn::<()>(); let property_canquit = f.property::("CanQuit", ()) .access(Access::Read) @@ -72,7 +117,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { }); // https://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html - let interface = f.interface("org.mpris.MediaPlayer", ()) + let interface = f.interface("org.mpris.MediaPlayer2", ()) .add_p(property_canquit) .add_p(property_canraise) .add_p(property_cansetfullscreen) @@ -86,11 +131,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { f.property::("PlaybackStatus", ()) .access(Access::Read) .on_get(move |iter, _| { - let status = match spotify.get_current_status() { - PlayerEvent::Playing => "Playing", - PlayerEvent::Paused => "Paused", - _ => "Stopped" - }.to_string(); + let status = get_playbackstatus(spotify.clone()); iter.append(status); Ok(()) }) @@ -108,42 +149,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { f.property::>>, _>("Metadata", ()) .access(Access::Read) .on_get(move |iter, _| { - let mut hm: HashMap>> = HashMap::new(); - - let queue = queue.lock().expect("could not lock queue"); - let track = queue.get_current(); - - hm.insert("mpris:trackid".to_string(), Variant(Box::new( - track.map(|t| format!("spotify:track:{}", t.id.to_base62())).unwrap_or("".to_string()) - ))); - hm.insert("mpris:length".to_string(), Variant(Box::new( - track.map(|t| t.duration * 1_000_000).unwrap_or(0) - ))); - hm.insert("mpris:artUrl".to_string(), Variant(Box::new( - track.map(|t| t.cover_url.clone()).unwrap_or("".to_string()) - ))); - - hm.insert("xesam:album".to_string(), Variant(Box::new( - track.map(|t| t.album.clone()).unwrap_or("".to_string()) - ))); - hm.insert("xesam:albumArtist".to_string(), Variant(Box::new( - track.map(|t| t.album_artists.join(", ")).unwrap_or("".to_string()) - ))); - hm.insert("xesam:artist".to_string(), Variant(Box::new( - track.map(|t| t.artists.join(", ")).unwrap_or("".to_string()) - ))); - hm.insert("xesam:discNumber".to_string(), Variant(Box::new( - track.map(|t| t.disc_number).unwrap_or(0) - ))); - hm.insert("xesam:title".to_string(), Variant(Box::new( - track.map(|t| t.title.clone()).unwrap_or("".to_string()) - ))); - hm.insert("xesam:trackNumber".to_string(), Variant(Box::new( - track.map(|t| t.track_number).unwrap_or(0) - ))); - hm.insert("xesam:url".to_string(), Variant(Box::new( - track.map(|t| t.url.clone()).unwrap_or("".to_string()) - ))); + let hm = get_metadata(queue.clone()); iter.append(hm); Ok(()) @@ -232,7 +238,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { let method_playpause = { let spotify = spotify.clone(); - f.amethod("PlayPause", (), move |m| { + f.method("PlayPause", (), move |m| { spotify.toggleplayback(); Ok(vec![m.msg.method_return()]) }) @@ -240,7 +246,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { let method_play = { let spotify = spotify.clone(); - f.amethod("Play", (), move |m| { + f.method("Play", (), move |m| { spotify.play(); Ok(vec![m.msg.method_return()]) }) @@ -248,7 +254,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { let method_pause = { let spotify = spotify.clone(); - f.amethod("Pause", (), move |m| { + f.method("Pause", (), move |m| { spotify.pause(); Ok(vec![m.msg.method_return()]) }) @@ -256,7 +262,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { let method_stop = { let spotify = spotify.clone(); - f.amethod("Stop", (), move |m| { + f.method("Stop", (), move |m| { spotify.stop(); Ok(vec![m.msg.method_return()]) }) @@ -264,7 +270,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { let method_next = { let queue = queue.clone(); - f.amethod("Next", (), move |m| { + f.method("Next", (), move |m| { queue.lock().expect("failed to lock queue").next(); Ok(vec![m.msg.method_return()]) }) @@ -272,7 +278,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { let method_previous = { let queue = queue.clone(); - f.amethod("Previous", (), move |m| { + f.method("Previous", (), move |m| { queue.lock().expect("failed to lock queue").previous(); Ok(vec![m.msg.method_return()]) }) @@ -303,7 +309,7 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { .add_m(method_next) .add_m(method_previous); - let tree = f.tree(ATree::new()) + let tree = f.tree(()) .add(f.object_path("/org/mpris/MediaPlayer2", ()).introspectable() .add(interface) .add(interface_player) @@ -311,14 +317,47 @@ pub fn run_dbus_server(spotify: Arc, queue: Arc>) { tree.set_registered(&conn, true).expect("failed to register tree"); - let mut rt = Runtime::new().unwrap(); - let aconn = AConnection::new(conn.clone(), Handle::default(), &mut rt).unwrap(); - let server = ATreeServer::new(conn.clone(), &tree, aconn.messages().unwrap()); + conn.add_handler(tree); + loop { + if let Some(m) = conn.incoming(200).next() { + warn!("Unhandled dbus message: {:?}", m); + } - let server = server.for_each(|m| { - warn!("Unhandled dbus message: {:?}", m); - Ok(()) - }); - rt.block_on(server).unwrap(); - rt.run().unwrap(); + if let Ok(_) = rx.try_recv() { + let mut changed: PropertiesPropertiesChanged = Default::default(); + changed.interface_name = "org.mpris.MediaPlayer2.Player".to_string(); + changed.changed_properties.insert( + "Metadata".to_string(), + Variant(Box::new(get_metadata(queue.clone()))) + ); + changed.changed_properties.insert( + "PlaybackStatus".to_string(), + Variant(Box::new(get_playbackstatus(spotify.clone()))) + ); + + conn.send(changed.to_emit_message(&Path::new("/org/mpris/MediaPlayer2".to_string()).unwrap())).unwrap(); + } + } +} + +pub struct MprisManager { + tx: mpsc::Sender<()> +} + +impl MprisManager { + pub fn new(spotify: Arc, queue: Arc>) -> Self { + let (tx, rx) = mpsc::channel::<()>(); + + std::thread::spawn(move || { + run_dbus_server(spotify, queue, rx); + }); + + MprisManager { + tx: tx + } + } + + pub fn update(&self) { + self.tx.send(()).unwrap(); + } }