/* 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 serde::{Serialize, Serializer}; use std::time::Duration; use thiserror::Error; pub enum Message { Quit, } #[derive(Clone, Debug)] pub enum Event { Paused(TrackId, Duration), Playing(TrackId, Duration), Stopped, Position(TrackId, Duration), } #[derive(Debug)] pub struct DeviceInformation { pub address: String, pub name: String, } #[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("url parse error {0}")] UrlError(url::ParseError), } 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, PartialEq, Serialize)] pub struct TrackInfo { pub id: TrackId, pub track_number: Option, pub name: Option, pub album: Option, pub artist: Option, pub duration: Option, #[serde(serialize_with = "serialize_mime")] pub filetype: mime::Mime, } fn serialize_mime(val: &mime::Mime, s: S) -> Result where S: Serializer, { s.serialize_str(val.essence_str()) } #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub enum State { Playing(TrackInfo), Paused(TrackInfo), Stopped, } /* pub struct CurrentlyPlaying { track: TrackInfo, 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) } } */