monorepo/music-player/server/src/media.rs

264 lines
6.9 KiB
Rust

/*
Copyright 2023, Savanni D'Gerinel <savanni@luminescent-dreams.com>
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 <https://www.gnu.org/licenses/>.
*/
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<url::ParseError> for AudioError {
fn from(err: url::ParseError) -> Self {
Self::UrlError(err)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
pub struct TrackId(String);
impl TrackId {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Default for TrackId {
fn default() -> Self {
Self(uuid::Uuid::new_v4().as_hyphenated().to_string())
}
}
impl From<String> for TrackId {
fn from(id: String) -> Self {
Self(id)
}
}
impl AsRef<String> 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<i32>,
pub name: Option<String>,
pub album: Option<String>,
pub artist: Option<String>,
pub duration: Option<u32>,
#[serde(serialize_with = "serialize_mime")]
pub filetype: mime::Mime,
}
fn serialize_mime<S>(val: &mime::Mime, s: S) -> Result<S::Ok, S::Error>
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<Capabilities, AudioError>;
fn state(&self) -> Result<State, AudioError>;
fn play(&self, trackid: url::Url) -> Result<State, AudioError>;
fn play_pause(&self) -> Result<State, AudioError>;
}
/*
pub struct GStreamerPlayer {
url: url::Url,
}
impl AudioPlayer for GStreamerPlayer {
fn capabilities(&self) -> Result<Capabilities, AudioError> {
unimplemented!()
}
fn state(&self) -> Result<State, AudioError> {
unimplemented!()
}
fn play(&self, trackid: url::Url) -> Result<State, AudioError> {
unimplemented!()
}
fn play_pause(&self) -> Result<State, AudioError> {
unimplemented!()
}
}
*/
/*
pub struct MprisDevice {
device_id: String,
player: Player,
state: State,
}
impl MprisDevice {
pub fn new(device_id: String) -> Result<MprisDevice, AudioError> {
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<Message>) {
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<Event>) {
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<Capabilities, AudioError> {
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<State, AudioError> {
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<State, AudioError> {
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<State, AudioError> {
self.player.play_pause()?;
Ok(State::Stopped)
}
}
*/