/* Copyright 2023, Savanni D'Gerinel This file is part of the Luminescent Dreams Tools. Luminescent Dreams Tools is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Luminescent Dreams Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Lumeto. If not, see . */ use dbus::ffidisp::Connection; use mpris::{FindingError, PlaybackStatus, Player, PlayerFinder, ProgressTick}; use serde::Serialize; use std::{ path::PathBuf, sync::mpsc::{channel, Receiver, Sender, TryRecvError}, thread, time::Duration, }; use thiserror::Error; pub enum Message { Quit, } #[derive(Clone, Debug)] pub enum Event { Paused(Track, Duration), Playing(Track, Duration), Stopped, Position(Track, Duration), } #[derive(Debug)] pub struct DeviceInformation { pub address: String, pub name: String, } pub fn list_devices(conn: Connection) -> Result, FindingError> { Ok(PlayerFinder::for_connection(conn) .find_all()? .into_iter() .map(|player| DeviceInformation { address: player.unique_name().to_owned(), name: player.identity().to_owned(), }) .collect()) } #[derive(Debug, Error)] pub enum AudioError { #[error("DBus device was not found")] DeviceNotFound, #[error("DBus connection lost or otherwise failed")] ConnectionLost, #[error("Specified media cannot be found")] MediaNotFound, #[error("Play was ordered, but nothing is in the queue")] NothingInQueue, #[error("Unknown dbus error")] DbusError(dbus::Error), #[error("Unknown problem with mpris")] MprisError(mpris::DBusError), #[error("url parse error {0}")] UrlError(url::ParseError), } impl From for AudioError { fn from(err: dbus::Error) -> Self { Self::DbusError(err) } } impl From for AudioError { fn from(err: mpris::DBusError) -> Self { Self::MprisError(err) } } impl From for AudioError { fn from(err: url::ParseError) -> Self { Self::UrlError(err) } } #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)] pub struct TrackId(String); impl Default for TrackId { fn default() -> Self { Self(uuid::Uuid::new_v4().as_hyphenated().to_string()) } } impl From for TrackId { fn from(id: String) -> Self { Self(id) } } impl AsRef for TrackId { fn as_ref<'a>(&'a self) -> &'a String { &self.0 } } #[derive(Clone, Debug)] pub struct TrackInfo { pub track_number: Option, pub name: Option, pub album: Option, pub artist: Option, } #[derive(Clone, Debug, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Track { pub id: TrackId, pub track_number: Option, pub name: Option, pub album: Option, pub artist: Option, } /* impl From<&mpris::Metadata> for Track { fn from(data: &mpris::Metadata) -> Self { Self { id: data.track_id().unwrap(), track_number: data.track_number(), name: data.title().map(|s| s.to_owned()), album: data.album_name().map(|s| s.to_owned()), artist: None, } } } impl From for Track { fn from(data: mpris::Metadata) -> Self { Self::from(&data) } } */ #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub enum State { Playing(Track), Paused(Track), Stopped, } pub struct CurrentlyPlaying { track: Track, position: Duration, } #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Capabilities { pub can_control: bool, pub can_pause: bool, pub can_play: bool, pub supports_track_lists: bool, } pub trait AudioPlayer { fn capabilities(&self) -> Result; fn state(&self) -> Result; fn play(&self, trackid: url::Url) -> Result; fn play_pause(&self) -> Result; } pub struct GStreamerPlayer { url: url::Url, } impl AudioPlayer for GStreamerPlayer { fn capabilities(&self) -> Result { unimplemented!() } fn state(&self) -> Result { unimplemented!() } fn play(&self, trackid: url::Url) -> Result { unimplemented!() } fn play_pause(&self) -> Result { unimplemented!() } } /* pub struct MprisDevice { device_id: String, player: Player, state: State, } impl MprisDevice { pub fn new(device_id: String) -> Result { let connection = Connection::new_session()?; Ok(MprisDevice { device_id: device_id.clone(), player: mpris::Player::new(connection, device_id, 1000)?, state: State::Stopped, }) } pub fn monitor(&mut self, control_channel: Receiver) { let (tx, rx) = channel(); { let device_id = self.device_id.clone(); let tx = tx.clone(); thread::spawn(move || { MprisDevice::new(device_id) .expect("connect to bus") .monitor_progress(tx); }); }; loop { match control_channel.try_recv() { Ok(Message::Quit) => return, Err(TryRecvError::Empty) => {} Err(TryRecvError::Disconnected) => return, } let event = rx.recv().expect("receive should never fail"); println!("event received: {:?}", event); } } pub fn monitor_progress(&self, tx: Sender) { let mut tracker = self .player .track_progress(1000) .expect("can get an event stream"); loop { let ProgressTick { progress, .. } = tracker.tick(); match progress.playback_status() { PlaybackStatus::Playing => { tx.send(Event::Playing( Track::from(progress.metadata()), progress.position(), )) .expect("send to succeed"); } PlaybackStatus::Paused => { tx.send(Event::Paused( Track::from(progress.metadata()), progress.position(), )) .expect("send to succeed"); } PlaybackStatus::Stopped => { tx.send(Event::Stopped).expect("send to succeed"); } } } } } impl AudioPlayer for MprisDevice { fn capabilities(&self) -> Result { Ok(Capabilities { can_control: self.player.can_control()?, can_pause: self.player.can_pause()?, can_play: self.player.can_play()?, supports_track_lists: self.player.supports_track_lists(), }) } fn state(&self) -> Result { println!( "supports track lists: {:?}", self.player.supports_track_lists() ); let metadata = self.player.get_metadata()?; println!("{:?}", metadata); unimplemented!("AudioPlayer state") } fn play(&self, track_id: String) -> Result { println!("playing: {}", track_id); self.player .go_to(&mpris::TrackID::from(dbus::Path::from(track_id)))?; self.player.play(); Ok(State::Stopped) } fn play_pause(&self) -> Result { self.player.play_pause()?; Ok(State::Stopped) } } */