diff --git a/dmp-core/src/music_controller/connections.rs b/dmp-core/src/music_controller/connections.rs index 28ae104..9ada884 100644 --- a/dmp-core/src/music_controller/connections.rs +++ b/dmp-core/src/music_controller/connections.rs @@ -1,12 +1,11 @@ #![allow(while_true)] -use std::time::Duration; +use std::{thread::sleep, time::{Duration, SystemTime, UNIX_EPOCH}}; use chrono::TimeDelta; -use crossbeam::scope; +use crossbeam::{scope, select}; use crossbeam_channel::{bounded, Receiver}; -use discord_presence::models::{Activity, ActivityTimestamps, ActivityType}; +use discord_presence::Client; use prismriver::State as PrismState; -use rayon::spawn; use crate::music_storage::library::{Song, Tag}; @@ -68,67 +67,98 @@ impl Controller { } fn discord_rpc(client_id: u64, song_tx: Receiver, state_tx: Receiver) { - spawn(move || { + // TODO: Handle seeking position change + std::thread::spawn(move || { let mut client = discord_presence::Client::new(client_id); client.start(); - client.block_until_event(discord_presence::Event::Connected).unwrap(); - client.set_activity(|_| - Activity::new() - ).unwrap(); + while !Client::is_ready() { sleep(Duration::from_millis(100)); } println!("discord connected"); let mut state = "Started".to_string(); let mut song: Option = None; + let mut now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards?").as_secs(); while true { - let state_res = state_tx.recv_timeout(Duration::from_secs(5)); - let song_res = song_tx.recv_timeout(Duration::from_millis(100)); - let state = &mut state; let song = &mut song; - - if let Ok(state_) = state_res { - *state = match state_ { - PrismState::Playing => "Playing", - PrismState::Paused => "Paused", - PrismState::Stopped => "Stopped", - _ => "I'm Scared, Boss" - }.to_string() - } - if let Ok(song_) = song_res { - *song = Some(song_); + select! { + recv(state_tx) -> res => { + if let Ok(state_) = res { + *state = match state_ { + PrismState::Playing => "Playing", + PrismState::Paused => "Paused", + PrismState::Stopped => "Stopped", + _ => "I'm Scared, Boss" + }.to_string(); + } + }, + recv(song_tx) -> res => { + if let Ok(song_) = res { + *song = Some(song_); + now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards?").as_secs(); + } + } + default(Duration::from_millis(4500)) => () } client.set_activity(|activity| { - activity.state( - state.clone() + let a = activity.state( + song.as_ref().map_or(String::new(), |s| format!( + "{}{}{}", + s.get_tag(&Tag::Artist).map_or(String::new(), |album| album.clone()), + if s.get_tag(&Tag::Album).is_some() && s.get_tag(&Tag::Artist).is_some() { " - " } else { "" }, + s.get_tag(&Tag::Album).map_or(String::new(), |album| album.clone()) + ) + ) )._type(discord_presence::models::ActivityType::Listening) .details( if let Some(song) = song { - format!( - "{} - {}\n{}", - song.get_tag(&Tag::Title).map_or(String::from("No Title"), |title| title.clone()), - song.get_tag(&Tag::Artist).map_or(String::from("No Artist"), |artist| artist.clone()), - song.get_tag(&Tag::Album).map_or(String::from("No Album"), |album| album.clone()) - ) + song.get_tag(&Tag::Title).map_or(String::from("Unknown Title"), |title| title.clone()) } else { String::new() } - ) - // if let Some(song) = song { - // a.timestamps(|timestamp| { - // ActivityTimestamps::new() - // .start(timestamp.start.unwrap_or_default()) - // .end( - // song.duration.as_millis().clamp(u64::MIN as u128, u64::MAX as u128) as u64 - // ) - // }) - // } else { - // a - // } + ); + if let Some(s) = song { + if state.as_str() == "Playing" { + a.timestamps(|timestamps| { + timestamps.start(now) + .end(now + s.duration.as_secs()) + }) + } else { + a + } + } else { + a + }.assets(|a| { + a.large_text(state.clone()) + }) }).unwrap(); - println!("Changed Status"); + println!("Updated Discord Status"); } }); } +} + +#[cfg(test)] +mod test_super { + use std::thread::sleep; + + use crossbeam_channel::unbounded; + + use crate::config::tests::read_config_lib; + + use super::*; + + #[test] + fn discord_test() { + let client_id = std::env!("DISCORD_CLIENT_ID").parse::().unwrap(); + let (song_rx, song_tx) = unbounded(); + let (_, state_tx) = unbounded(); + + let (_, lib ) = read_config_lib(); + song_rx.send(lib.library[0].clone()).unwrap(); + + Controller::discord_rpc(client_id, song_tx, state_tx); + sleep(Duration::from_secs(150)); + } } \ No newline at end of file diff --git a/dmp-core/src/music_controller/controller.rs b/dmp-core/src/music_controller/controller.rs index 823c63a..315c7db 100644 --- a/dmp-core/src/music_controller/controller.rs +++ b/dmp-core/src/music_controller/controller.rs @@ -3,7 +3,6 @@ //! other functions #![allow(while_true)] -use async_channel::{bounded, unbounded}; use chrono::TimeDelta; use crossbeam::atomic::AtomicCell; use crossbeam_channel::{Receiver, Sender}; @@ -14,12 +13,11 @@ use prismriver::{Error as PrismError, Prismriver, State as PrismState, Volume}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; use serde_json::to_string_pretty; -use std::collections::HashMap; use std::error::Error; use std::fs::OpenOptions; use std::io::Write; use std::path::{Path, PathBuf}; -use std::sync::{Arc}; +use std::sync::Arc; use std::time::Duration; use thiserror::Error; use uuid::Uuid; @@ -278,6 +276,7 @@ impl Controller { let a = scope.spawn({ let queue_mail = queue_mail.clone(); + let _notifications_rx = notifications_rx.clone(); move || { futures::executor::block_on(async { moro::async_scope!(|scope| { @@ -292,6 +291,7 @@ impl Controller { player_mail.1, _queue_mail, _lib_mail, + _notifications_rx, state, ) .await @@ -354,6 +354,7 @@ impl Controller { player_mail: MailMan, queue_mail: MailMan, lib_mail: MailMan, + notify_connections_: Sender, mut state: ControllerState, ) -> Result<(), ()> { player.set_volume(Volume::new(state.volume)); @@ -444,6 +445,7 @@ impl Controller { state.now_playing = np_song.song.uuid; _ = state.write_file(); + notify_connections_.send(ConnectionsNotification::SongChange(np_song.song)).unwrap(); } QueueResponse::Item(Err(e)) => { player_mail.send(PlayerResponse::NowPlaying(Err(e.into()))).await.unwrap(); } @@ -469,6 +471,7 @@ impl Controller { state.now_playing = np_song.song.uuid; _ = state.write_file(); + notify_connections_.send(ConnectionsNotification::SongChange(np_song.song)).unwrap(); } QueueResponse::Item(Err(e)) => { player_mail.send(PlayerResponse::NowPlaying(Err(e.into()))).await.unwrap(); @@ -485,10 +488,14 @@ impl Controller { match queue_mail.recv().await.unwrap() { QueueResponse::Item(Ok(item)) => { match item.item { - QueueItemType::Single(song) => { - let prism_uri = prismriver::utils::path_to_uri(&song.song.primary_uri().unwrap().0.as_path().unwrap()).unwrap(); + QueueItemType::Single(np_song) => { + let prism_uri = prismriver::utils::path_to_uri(&np_song.song.primary_uri().unwrap().0.as_path().unwrap()).unwrap(); player.load_new(&prism_uri).unwrap(); player.play(); + + state.now_playing = np_song.song.uuid; + _ = state.write_file(); + notify_connections_.send(ConnectionsNotification::SongChange(np_song.song)).unwrap(); } _ => unimplemented!(), } @@ -504,7 +511,7 @@ impl Controller { PlayerCommand::PlayNow(uuid, location) => { // TODO: This assumes the uuid doesn't point to an album. we've been over this. lib_mail.send(LibraryCommand::Song(uuid)).await.unwrap(); - let LibraryResponse::Song(song, index) = lib_mail.recv().await.unwrap() else { + let LibraryResponse::Song(np_song, index) = lib_mail.recv().await.unwrap() else { unreachable!() }; queue_mail.send(QueueCommand::Clear).await.unwrap(); @@ -516,7 +523,7 @@ impl Controller { } _ => unreachable!() } - queue_mail.send(QueueCommand::Append(QueueItem::from_item_type(QueueItemType::Single(QueueSong { song: song.clone(), location })), true)).await.unwrap(); + queue_mail.send(QueueCommand::Append(QueueItem::from_item_type(QueueItemType::Single(QueueSong { song: np_song.clone(), location })), true)).await.unwrap(); match queue_mail.recv().await.unwrap() { QueueResponse::Empty(Ok(())) => (), QueueResponse::Empty(Err(e)) => { @@ -527,7 +534,7 @@ impl Controller { } // TODO: Handle non Local URIs here, and whenever `load_new()` or `load_gapless()` is called - let prism_uri = prismriver::utils::path_to_uri(&song.primary_uri().unwrap().0.as_path().unwrap()).unwrap(); + let prism_uri = prismriver::utils::path_to_uri(&np_song.primary_uri().unwrap().0.as_path().unwrap()).unwrap(); player.load_new(&prism_uri).unwrap(); player.play(); @@ -548,7 +555,7 @@ impl Controller { let LibraryResponse::ExternalPlaylist(list) = lib_mail.recv().await.unwrap() else { unreachable!() }; - let index = list.get_index(song.uuid).unwrap(); + let index = list.get_index(np_song.uuid).unwrap(); (list.tracks, index) } _ => todo!("Got Location other than Library or Playlist") @@ -572,7 +579,11 @@ impl Controller { } } // ^ This be my solution for now ^ - player_mail.send(PlayerResponse::NowPlaying(Ok(song.clone()))).await.unwrap(); + player_mail.send(PlayerResponse::NowPlaying(Ok(np_song.clone()))).await.unwrap(); + + state.now_playing = np_song.uuid; + _ = state.write_file(); + notify_connections_.send(ConnectionsNotification::SongChange(np_song)).unwrap(); } } } else { diff --git a/src/App.tsx b/src/App.tsx index 14d1e80..7ca9321 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -340,9 +340,9 @@ function PlayBar({ playing, setPlaying }: PlayBarProps) { invoke('set_volume', { volume: volume.target.value }).then(() => {}) }} />

- { Math.round(+duration / 60).toString().padStart(2, "0") }: + { Math.floor(+position / 60).toString().padStart(2, "0") }: { (+position % 60).toString().padStart(2, "0") }/ - { Math.round(+position / 60).toString().padStart(2, "0") }: + { Math.floor(+duration / 60).toString().padStart(2, "0") }: { (+duration % 60).toString().padStart(2, "0") }