Mercurial > beefweb_mpris
diff src/player.rs @ 0:d60ab8a4442f
*: check in
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sat, 04 Apr 2026 12:32:50 -0400 |
| parents | |
| children | a5ee18c79a04 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/player.rs Sat Apr 04 12:32:50 2026 -0400 @@ -0,0 +1,372 @@ +use crate::beefweb; + +use zbus::fdo; +use zbus::Result; +use std::collections::HashMap; +use std::io::Write; +use std::cell::RefCell; + +/* teehee */ +pub struct BeefwebPlayer { + bw: beefweb::Beefweb, + artcache: String, + /* artmap + * key: %path% column from beefweb + * value: local path of the artwork + * also LOL RUST FUCK */ + artmap: RefCell<HashMap<String, String>>, +} + +impl BeefwebPlayer { + pub fn new(base: &str, artcache: &str) -> BeefwebPlayer + { + return BeefwebPlayer { + bw: beefweb::Beefweb::new(base), + artcache: artcache.to_string(), + artmap: RefCell::new(HashMap::new()), + }; + } + +/* + async fn get_artwork(&self, playlist_id: &str, index: i64, path: &str) -> fdo::Result<String> + { + match self.artmap.borrow().get(path) { + Some(x) => return Ok(x.to_string()), + _ => (), + } + + /* Ok, art path isn't in the "cache". Ask beefweb for it. */ + let art = self.bw.artwork(playlist_id, index); + let artpath = format!("{}/{}", self.artcache, uuid::Uuid::new_v4()); + + /* XXX might be a good idea to check the bytes for an extension...? */ + let fr = std::fs::OpenOptions::new() + .write(true) + .open(&artpath); + + match fr { + Err(_) => return Err(fdo::Error::Failed("Failed to open file!".to_string())), + _ => (), + }; + + let mut f = fr.unwrap(); + + let artaw = art.await; + + match artaw { + Err(_) => return Err(fdo::Error::Failed("Uh oh".to_string())), + _ => (), + }; + + f.write(artaw.unwrap().as_ref()); + + self.artmap.borrow_mut().insert(path.to_string(), artpath.to_string()); + + return Ok(artpath); + } +*/ +} + +fn secs_to_time(x: f64) -> mpris_server::Time +{ + return mpris_server::Time::from_micros((x * 1000000.0).round() as i64); +} + +fn time_to_secs(x: mpris_server::Time) -> f64 +{ + return (x.as_micros() as f64) / 1000000.0; +} + +impl mpris_server::LocalRootInterface for BeefwebPlayer { + async fn raise(&self) -> fdo::Result<()> + { + /* don't care */ + return Ok(()); + } + + async fn quit(&self) -> fdo::Result<()> + { + /* don't care */ + return Ok(()); + } + + async fn can_quit(&self) -> fdo::Result<bool> + { + /* don't care */ + return Ok(false); + } + + async fn fullscreen(&self) -> fdo::Result<bool> + { + /* don't care */ + return Ok(false); + } + + async fn set_fullscreen(&self, _fullscreen: bool) -> Result<()> + { + /* don't care */ + return Ok(()); + } + + async fn can_set_fullscreen(&self) -> fdo::Result<bool> + { + return Ok(false); + } + + async fn can_raise(&self) -> fdo::Result<bool> + { + return Ok(false); + } + + async fn has_track_list(&self) -> fdo::Result<bool> + { + /* ??? */ + return Ok(false); + } + + async fn identity(&self) -> fdo::Result<String> + { + /* TODO: allow changing this */ + return Ok("beefweb".into()); + } + + async fn desktop_entry(&self) -> fdo::Result<String> + { + return Ok("foobar2000".into()); + } + + async fn supported_uri_schemes(&self) -> fdo::Result<Vec<String>> + { + return Ok([].to_vec()); + } + + async fn supported_mime_types(&self) -> fdo::Result<Vec<String>> + { + /* needs moar */ + return Ok([].to_vec()); + } +} + +impl mpris_server::LocalPlayerInterface for BeefwebPlayer { + async fn next(&self) -> fdo::Result<()> + { + self.bw.next().await; + return Ok(()); + } + + async fn previous(&self) -> fdo::Result<()> + { + self.bw.previous().await; + return Ok(()); + } + + async fn pause(&self) -> fdo::Result<()> + { + self.bw.pause().await; + return Ok(()); + } + + async fn play_pause(&self) -> fdo::Result<()> + { + self.bw.play_pause().await; + return Ok(()); + } + + async fn stop(&self) -> fdo::Result<()> + { + self.bw.stop().await; + return Ok(()); + } + + async fn play(&self) -> fdo::Result<()> + { + self.bw.play().await; + return Ok(()); + } + + async fn seek(&self, offset: mpris_server::Time) -> fdo::Result<()> + { + let pl = self.bw.set_position(time_to_secs(offset)).await; + + match pl { + Err(_) => return Err(fdo::Error::Failed("uhoh".to_string())), + _ => (), + }; + + return Ok(()); + } + + async fn position(&self) -> fdo::Result<mpris_server::Time> + { + let pl = self.bw.player().await; + + match pl { + Err(_) => return Err(fdo::Error::Failed("uhoh".to_string())), + _ => (), + }; + + return Ok(secs_to_time(pl.unwrap().active_item.position)); + } + + async fn playback_status(&self) -> fdo::Result<mpris_server::PlaybackStatus> + { + let p = self.bw.player().await; + + match p { + Err(_) => return Err(fdo::Error::Failed("wtf".to_string())), + _ => (), + }; + + return match p.unwrap().playback_state.as_str() { + "playing" => Ok(mpris_server::PlaybackStatus::Playing), + "stopped" => Ok(mpris_server::PlaybackStatus::Stopped), + "paused" => Ok(mpris_server::PlaybackStatus::Paused), + _ => Err(fdo::Error::Failed("deez nuts".to_string())), + }; + } + + async fn loop_status(&self) -> fdo::Result<mpris_server::LoopStatus> + { + return Ok(mpris_server::LoopStatus::None); + } + + async fn set_loop_status(&self, loop_status: mpris_server::LoopStatus) -> Result<()> + { + return Ok(()); + } + + async fn shuffle(&self) -> fdo::Result<bool> + { + /* TODO */ + println!("Shuffle"); + return Ok(false); + } + + async fn set_shuffle(&self, shuffle: bool) -> Result<()> + { + /* TODO */ + println!("SetShuffle({shuffle})"); + return Ok(()); + } + + async fn metadata(&self) -> fdo::Result<mpris_server::Metadata> + { + let pl = self.bw.player().await; + + match pl { + Err(_) => return Err(fdo::Error::Failed("uhoh".to_string())), + _ => (), + }; + + let p = pl.unwrap(); + + let playlist_items_result = self.bw.playlist_items(p.active_item.playlist_id.as_str(), p.active_item.index, 1, ["%title%", "%artist%", "%album%", "%discnumber%", "%tracknumber%", "%album artist%", "%path%"].to_vec()).await; + + match playlist_items_result { + Err(_) => return Err(fdo::Error::Failed("uhoh".to_string())), + _ => (), + }; + + let playlist_items = playlist_items_result.unwrap(); + + let track = playlist_items.items.get(0).unwrap(); + +/* + let artwork = self.get_artwork(p.active_item.playlist_id.as_str(), p.active_item.index, track.columns.get(6).unwrap()); +*/ + + let builder = mpris_server::Metadata::builder() + .length(secs_to_time(p.active_item.duration)) + .album(track.columns.get(2).unwrap()) + .artist([track.columns.get(1).unwrap()]) + .disc_number(track.columns.get(3).unwrap().parse::<i32>().unwrap()) + .track_number(track.columns.get(4).unwrap().parse::<i32>().unwrap()) + .title(track.columns.get(0).unwrap()) + .album_artist([track.columns.get(5).unwrap()]); + +/* + return match artwork.await { + Ok(x) => Ok(builder.art_url(urlencoding::encode(format!("file://{}", x).as_str())).build()), + _ => Ok(builder.build()), + }; +*/ + return Ok(builder.build()); + } + + async fn volume(&self) -> fdo::Result<mpris_server::Volume> + { + return Ok(mpris_server::Volume::default()); + } + + async fn set_volume(&self, volume: mpris_server::Volume) -> Result<()> + { + return Ok(()); + } + + /* "can" functions -- all work */ + + async fn can_go_next(&self) -> fdo::Result<bool> + { + return Ok(true); + } + + async fn can_go_previous(&self) -> fdo::Result<bool> + { + return Ok(true); + } + + async fn can_play(&self) -> fdo::Result<bool> + { + return Ok(true); + } + + async fn can_pause(&self) -> fdo::Result<bool> + { + return Ok(true); + } + + async fn can_seek(&self) -> fdo::Result<bool> + { + return Ok(true); + } + + async fn can_control(&self) -> fdo::Result<bool> + { + return Ok(true); + } + + /* --- UNSUPPORTED */ + + async fn rate(&self) -> fdo::Result<mpris_server::PlaybackRate> + { + return Ok(mpris_server::PlaybackRate::default()); + } + + async fn set_rate(&self, rate: mpris_server::PlaybackRate) -> Result<()> + { + return Ok(()); + } + + async fn minimum_rate(&self) -> fdo::Result<mpris_server::PlaybackRate> + { + return Ok(mpris_server::PlaybackRate::default()); + } + + async fn maximum_rate(&self) -> fdo::Result<mpris_server::PlaybackRate> + { + return Ok(mpris_server::PlaybackRate::default()); + } + + /* WTF is this supposed to do? --paper */ + async fn set_position(&self, track_id: mpris_server::TrackId, position: mpris_server::Time) -> fdo::Result<()> + { + return Ok(()); + } + + /* Beefweb doesn't really have this, and it would be + * pointless anyway, unless we used winepath */ + async fn open_uri(&self, uri: String) -> fdo::Result<()> + { + return Ok(()); + } +}
