// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: AGPL-3.0-or-later use canary_music_player::*; use canary_magpie::client::MagpieClient; use canary_magpie::protocol::{CreatePanel, MagpieServerMsg}; use mpris::PlayerFinder; pub struct MetadataTracker { pub album: AlbumInfo, pub track: TrackInfo, } impl From for MetadataTracker { fn from(metadata: mpris::Metadata) -> Self { let album = AlbumInfo { title: metadata.album_name().map(ToString::to_string), artists: metadata .album_artists() .unwrap_or(Vec::new()) .iter() .map(ToString::to_string) .collect(), }; let track = TrackInfo { title: metadata.title().map(ToString::to_string), artists: metadata .artists() .unwrap_or(Vec::new()) .iter() .map(ToString::to_string) .collect(), track_number: metadata.track_number(), }; Self { album, track } } } impl MetadataTracker { pub fn new(magpie: &mut MagpieClient, metadata: mpris::Metadata) -> Self { let new: Self = metadata.into(); magpie.send_json_message(0, &InMsg::AlbumChanged(new.album.clone())); magpie.send_json_message(0, &InMsg::TrackChanged(new.track.clone())); new } pub fn update(&mut self, messenger: &mut MagpieClient, metadata: mpris::Metadata) { let new: Self = metadata.into(); if self.album != new.album { messenger.send_json_message(0, &InMsg::AlbumChanged(new.album.clone())); } if self.track != new.track { messenger.send_json_message(0, &InMsg::TrackChanged(new.track.clone())); } *self = new; } } fn main() { let args: Vec = std::env::args().collect(); let module_path = args .get(1) .expect("Please pass a path to a Canary script!") .to_owned(); let player_finder = PlayerFinder::new().expect("Could not connect to D-Bus"); let mut magpie = MagpieClient::new().unwrap(); let script = std::path::PathBuf::from(&module_path); let msg = CreatePanel { id: 0, script }; let msg = MagpieServerMsg::CreatePanel(msg); magpie.messenger.send(&msg).unwrap(); let mut first_loop = true; let mut connected = false; loop { if !first_loop { let wait = std::time::Duration::from_secs(1); std::thread::sleep(wait); } first_loop = false; if connected { println!("Disconnected from MPRIS"); let msg = InMsg::Disconnected; magpie.send_json_message(0, &msg); connected = false; } println!("Connecting to MPRIS..."); let player = match player_finder.find_active() { Ok(player) => player, Err(err) => { eprintln!("Couldn't find player: {:?}", err); continue; } }; println!( "Connected to \"{}\" ({})", player.identity(), player.bus_name() ); connected = true; magpie.send_json_message(0, &InMsg::Connected); let metadata = player.get_metadata().unwrap(); let mut metadata_tracker = MetadataTracker::new(&mut magpie, metadata); let mut events = match player.events() { Ok(events) => events, Err(err) => { eprintln!("Player events D-Bus error: {:?}", err); continue; } }; loop { let event = match events.next() { None => break, Some(Ok(e)) => e, Some(Err(err)) => { eprintln!("D-Bus error while reading player events: {:?}", err); continue; } }; use mpris::Event::*; let in_msg = match event { Playing => Some(InMsg::PlaybackStatusChanged(PlaybackStatus::Playing)), Paused => Some(InMsg::PlaybackStatusChanged(PlaybackStatus::Paused)), Stopped => Some(InMsg::PlaybackStatusChanged(PlaybackStatus::Stopped)), LoopingChanged(status) => { use mpris::LoopStatus::*; let status = match status { None => LoopStatus::None, Track => LoopStatus::Track, Playlist => LoopStatus::Playlist, }; Some(InMsg::LoopingChanged(status)) } ShuffleToggled(shuffle) => Some(InMsg::ShuffleChanged { shuffle }), VolumeChanged(volume) => Some(InMsg::VolumeChanged { volume: volume as f32, }), PlayerShutDown => None, TrackChanged(metadata) => { metadata_tracker.update(&mut magpie, metadata); None } _ => { eprintln!("Unhandled MPRIS message: {:?}", event); None } }; if let Some(msg) = in_msg { magpie.send_json_message(0, &msg); } } } }