more progress on RPC Integration

This commit is contained in:
MrDulfin 2024-12-30 03:02:11 -05:00
parent 2130af1e4a
commit 31829ad4f5
9 changed files with 234 additions and 70 deletions

1
.gitignore vendored
View file

@ -31,3 +31,4 @@ Cargo.lock
.cargo .cargo
test-config test-config
.txt

View file

@ -39,3 +39,4 @@ ciborium = "0.2.2"
itertools = "0.13.0" itertools = "0.13.0"
prismriver = { git = "https://github.com/Dangoware/prismriver.git"} prismriver = { git = "https://github.com/Dangoware/prismriver.git"}
parking_lot = "0.12.3" parking_lot = "0.12.3"
discord-presence = { version = "1.4.1", features = ["activity_type"] }

View file

@ -9,6 +9,7 @@ pub mod music_storage {
} }
pub mod music_controller { pub mod music_controller {
pub mod connections;
pub mod controller; pub mod controller;
pub mod controller_handle; pub mod controller_handle;
pub mod queue; pub mod queue;

View file

@ -0,0 +1,134 @@
#![allow(while_true)]
use std::time::Duration;
use chrono::TimeDelta;
use crossbeam::scope;
use crossbeam_channel::{bounded, Receiver};
use discord_presence::models::{Activity, ActivityTimestamps, ActivityType};
use prismriver::State as PrismState;
use rayon::spawn;
use crate::music_storage::library::{Song, Tag};
use super::controller::Controller;
#[derive(Debug, Clone)]
pub(super) enum ConnectionsNotification {
Playback {
position: Option<TimeDelta>,
duration: Option<TimeDelta>
},
StateChange(PrismState),
SongChange(Song),
}
#[derive(Debug)]
pub struct ConnectionsInput {
pub discord_rpc_client_id: Option<u64>,
}
pub(super) struct ControllerConnections {
pub notifications_tx: Receiver<ConnectionsNotification>,
pub inner: ConnectionsInput
}
impl Controller {
pub(super) fn handle_connections(ControllerConnections {
notifications_tx,
inner: ConnectionsInput {
discord_rpc_client_id
},
}: ControllerConnections
) {
let (dc_state_rx, dc_state_tx) = bounded::<PrismState>(1);
let (dc_song_rx, dc_song_tx) = bounded::<Song>(1);
scope(|s| {
s.builder().name("Notifications Sorter".to_string()).spawn(|_| {
use ConnectionsNotification::*;
while true {
match notifications_tx.recv().unwrap() {
Playback { position, duration } => { continue; }
StateChange(state) => {
dc_state_rx.send(state.clone()).unwrap();
}
SongChange(song) => {
dc_song_rx.send(song).unwrap();
}
}
}
}).unwrap();
if let Some(client_id) = discord_rpc_client_id {
println!("Discord thingy detected");
s.builder().name("Discord RPC Handler".to_string()).spawn(move |_| {
Controller::discord_rpc(client_id, dc_song_tx, dc_state_tx);
}).unwrap();
};
}).unwrap();
}
fn discord_rpc(client_id: u64, song_tx: Receiver<Song>, state_tx: Receiver<PrismState>) {
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();
println!("discord connected");
let mut state = "Started".to_string();
let mut song: Option<Song> = None;
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_);
}
client.set_activity(|activity| {
activity.state(
state.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())
)
} 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
// }
}).unwrap();
println!("Changed Status");
}
});
}
}

View file

@ -3,13 +3,14 @@
//! other functions //! other functions
#![allow(while_true)] #![allow(while_true)]
use async_channel::unbounded; use async_channel::{bounded, unbounded};
use chrono::TimeDelta; use chrono::TimeDelta;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use kushi::{Queue, QueueItemType}; use kushi::{Queue, QueueItemType};
use kushi::{QueueError, QueueItem}; use kushi::{QueueError, QueueItem};
use prismriver::{Prismriver, Volume, Error as PrismError}; use parking_lot::RwLock;
use prismriver::{Error as PrismError, Prismriver, State as PrismState, Volume};
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::to_string_pretty; use serde_json::to_string_pretty;
@ -18,7 +19,7 @@ use std::error::Error;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock}; use std::sync::{Arc};
use std::time::Duration; use std::time::Duration;
use thiserror::Error; use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
@ -28,6 +29,7 @@ use crate::music_storage::library::Song;
use crate::music_storage::playlist::{ExternalPlaylist, Playlist, PlaylistFolderItem}; use crate::music_storage::playlist::{ExternalPlaylist, Playlist, PlaylistFolderItem};
use crate::{config::Config, music_storage::library::MusicLibrary}; use crate::{config::Config, music_storage::library::MusicLibrary};
use super::connections::{ConnectionsInput, ConnectionsNotification, ControllerConnections};
use super::queue::{QueueAlbum, QueueSong}; use super::queue::{QueueAlbum, QueueSong};
pub struct Controller(); pub struct Controller();
@ -166,6 +168,7 @@ pub struct ControllerInput {
config: Arc<RwLock<Config>>, config: Arc<RwLock<Config>>,
playback_info: Arc<AtomicCell<PlaybackInfo>>, playback_info: Arc<AtomicCell<PlaybackInfo>>,
notify_next_song: Sender<Song>, notify_next_song: Sender<Song>,
connections: Option<ConnectionsInput>
} }
pub struct ControllerHandle { pub struct ControllerHandle {
@ -175,7 +178,7 @@ pub struct ControllerHandle {
} }
impl ControllerHandle { impl ControllerHandle {
pub fn new(library: MusicLibrary, config: Arc<RwLock<Config>>) -> (Self, ControllerInput, Arc<AtomicCell<PlaybackInfo>>, Receiver<Song>) { pub fn new(library: MusicLibrary, config: Arc<RwLock<Config>>, connections: Option<ConnectionsInput>) -> (Self, ControllerInput, Arc<AtomicCell<PlaybackInfo>>, Receiver<Song>) {
let lib_mail = MailMan::double(); let lib_mail = MailMan::double();
let player_mail = MailMan::double(); let player_mail = MailMan::double();
let queue_mail = MailMan::double(); let queue_mail = MailMan::double();
@ -195,6 +198,7 @@ impl ControllerHandle {
config, config,
playback_info: Arc::clone(&playback_info), playback_info: Arc::clone(&playback_info),
notify_next_song: notify_next_song.0, notify_next_song: notify_next_song.0,
connections,
}, },
playback_info, playback_info,
notify_next_song.1 notify_next_song.1
@ -246,6 +250,7 @@ impl Controller {
config, config,
playback_info, playback_info,
notify_next_song, notify_next_song,
connections,
}: ControllerInput }: ControllerInput
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let queue: Queue<QueueSong, QueueAlbum> = Queue { let queue: Queue<QueueSong, QueueAlbum> = Queue {
@ -256,7 +261,7 @@ impl Controller {
}; };
let state = { let state = {
let path = &config.read().unwrap().state_path; let path = &config.read().state_path;
if let Ok(state) = ControllerState::read_file(path) { if let Ok(state) = ControllerState::read_file(path) {
state state
} else { } else {
@ -264,24 +269,26 @@ impl Controller {
} }
}; };
let player = Arc::new(RwLock::new(Prismriver::new()));
std::thread::scope(|scope| { std::thread::scope(|scope| {
let player = Prismriver::new();
let player_state = player.state.clone();
let player_timing = player.get_timing_recv();
let finished_tx = player.get_finished_recv();
let (notifications_rx, notifications_tx) = crossbeam_channel::unbounded::<ConnectionsNotification>();
let a = scope.spawn({ let a = scope.spawn({
let player = Arc::clone(&player);
let queue_mail = queue_mail.clone(); let queue_mail = queue_mail.clone();
move || { move || {
futures::executor::block_on(async { futures::executor::block_on(async {
moro::async_scope!(|scope| { moro::async_scope!(|scope| {
println!("async scope created"); println!("async scope created");
let _player = player.clone();
let _lib_mail = lib_mail.0.clone(); let _lib_mail = lib_mail.0.clone();
let _queue_mail = queue_mail.0.clone(); let _queue_mail = queue_mail.0.clone();
scope scope
.spawn(async move { .spawn(async move {
Controller::player_command_loop( Controller::player_command_loop(
_player, player,
player_mail.1, player_mail.1,
_queue_mail, _queue_mail,
_lib_mail, _lib_mail,
@ -314,14 +321,26 @@ impl Controller {
let c = scope.spawn(|| { let c = scope.spawn(|| {
Controller::player_monitor_loop( Controller::player_monitor_loop(
player, player_state,
player_timing,
finished_tx,
player_mail.0, player_mail.0,
queue_mail.0, queue_mail.0,
playback_info,
notify_next_song, notify_next_song,
notifications_rx,
playback_info,
).unwrap(); ).unwrap();
}); });
if let Some(inner) = connections {
dbg!(&inner);
let d = scope.spawn(|| {
Controller::handle_connections( ControllerConnections {
notifications_tx,
inner,
});
});
}
a.join().unwrap(); a.join().unwrap();
b.join().unwrap(); b.join().unwrap();
c.join().unwrap(); c.join().unwrap();
@ -331,41 +350,42 @@ impl Controller {
} }
async fn player_command_loop( async fn player_command_loop(
player: Arc<RwLock<Prismriver>>, mut player: Prismriver,
player_mail: MailMan<PlayerResponse, PlayerCommand>, player_mail: MailMan<PlayerResponse, PlayerCommand>,
queue_mail: MailMan<QueueCommand, QueueResponse>, queue_mail: MailMan<QueueCommand, QueueResponse>,
lib_mail: MailMan<LibraryCommand, LibraryResponse>, lib_mail: MailMan<LibraryCommand, LibraryResponse>,
mut state: ControllerState, mut state: ControllerState,
) -> Result<(), ()> { ) -> Result<(), ()> {
player.write().unwrap().set_volume(Volume::new(state.volume)); player.set_volume(Volume::new(state.volume));
'outer: while true { 'outer: while true {
let _mail = player_mail.recv().await; let _mail = player_mail.recv().await;
if let Ok(mail) = _mail { if let Ok(mail) = _mail {
match mail { match mail {
PlayerCommand::Play => { PlayerCommand::Play => {
player.write().unwrap().play(); player.play();
player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap(); player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap();
} }
PlayerCommand::Pause => { PlayerCommand::Pause => {
player.write().unwrap().pause(); player.pause();
player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap(); player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap();
} }
PlayerCommand::Stop => { PlayerCommand::Stop => {
player.write().unwrap().stop(); player.stop();
player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap(); player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap();
} }
PlayerCommand::Seek(time) => { PlayerCommand::Seek(time) => {
let res = player.write().unwrap().seek_to(TimeDelta::milliseconds(time)); let res = player.seek_to(TimeDelta::milliseconds(time));
player_mail.send(PlayerResponse::Empty(res.map_err(|e| e.into()))).await.unwrap(); player_mail.send(PlayerResponse::Empty(res.map_err(|e| e.into()))).await.unwrap();
} }
PlayerCommand::SetVolume(volume) => { PlayerCommand::SetVolume(volume) => {
player.write().unwrap().set_volume(Volume::new(volume)); player.set_volume(Volume::new(volume));
player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap(); player_mail.send(PlayerResponse::Empty(Ok(()))).await.unwrap();
// make this async or something
state.volume = volume; state.volume = volume;
_ = state.write_file() _ = state.write_file()
} }
@ -384,8 +404,8 @@ impl Controller {
println!("Playing song at path: {:?}", prism_uri); println!("Playing song at path: {:?}", prism_uri);
// handle error here for unknown formats // handle error here for unknown formats
player.write().unwrap().load_new(&prism_uri).unwrap(); player.load_new(&prism_uri).unwrap();
player.write().unwrap().play(); player.play();
let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")}; let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")};
@ -441,8 +461,8 @@ impl Controller {
}; };
let prism_uri = prismriver::utils::path_to_uri(&uri.as_path().unwrap()).unwrap(); let prism_uri = prismriver::utils::path_to_uri(&uri.as_path().unwrap()).unwrap();
player.write().unwrap().load_new(&prism_uri).unwrap(); player.load_new(&prism_uri).unwrap();
player.write().unwrap().play(); player.play();
let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")}; let QueueItemType::Single(np_song) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")};
player_mail.send(PlayerResponse::NowPlaying(Ok(np_song.song.clone()))).await.unwrap(); player_mail.send(PlayerResponse::NowPlaying(Ok(np_song.song.clone()))).await.unwrap();
@ -467,8 +487,8 @@ impl Controller {
match item.item { match item.item {
QueueItemType::Single(song) => { QueueItemType::Single(song) => {
let prism_uri = prismriver::utils::path_to_uri(&song.song.primary_uri().unwrap().0.as_path().unwrap()).unwrap(); let prism_uri = prismriver::utils::path_to_uri(&song.song.primary_uri().unwrap().0.as_path().unwrap()).unwrap();
player.write().unwrap().load_new(&prism_uri).unwrap(); player.load_new(&prism_uri).unwrap();
player.write().unwrap().play(); player.play();
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
@ -508,8 +528,8 @@ impl Controller {
// TODO: Handle non Local URIs here, and whenever `load_new()` or `load_gapless()` is called // 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(&song.primary_uri().unwrap().0.as_path().unwrap()).unwrap();
player.write().unwrap().load_new(&prism_uri).unwrap(); player.load_new(&prism_uri).unwrap();
player.write().unwrap().play(); player.play();
// how grab all the songs in a certain subset of the library, I reckon? // how grab all the songs in a certain subset of the library, I reckon?
// ... // ...
@ -563,36 +583,33 @@ impl Controller {
} }
fn player_monitor_loop( fn player_monitor_loop(
player: Arc<RwLock<Prismriver>>, playback_state: Arc<std::sync::RwLock<PrismState>>,
playback_time_tx: Receiver<(Option<TimeDelta>, Option<TimeDelta>)>,
finished_recv: Receiver<()>,
player_mail: MailMan<PlayerCommand, PlayerResponse>, player_mail: MailMan<PlayerCommand, PlayerResponse>,
queue_mail: MailMan<QueueCommand, QueueResponse>, queue_mail: MailMan<QueueCommand, QueueResponse>,
player_info: Arc<AtomicCell<PlaybackInfo>>,
notify_next_song: Sender<Song>, notify_next_song: Sender<Song>,
notify_connections_: Sender<ConnectionsNotification>,
playback_info: Arc<AtomicCell<PlaybackInfo>>
) -> Result<(), ()> { ) -> Result<(), ()> {
let finished_recv = player.read().unwrap().get_finished_recv();
std::thread::scope(|s| { std::thread::scope(|s| {
// Thread for timing and metadata // Thread for timing and metadata
let notify_connections = notify_connections_.clone();
s.spawn({ s.spawn({
// let player = Arc::clone(&player);
move || { move || {
println!("playback monitor started");
while true { while true {
let player = player.read().unwrap(); let (position, duration) = playback_time_tx.recv().unwrap();
player_info.store(PlaybackInfo { notify_connections.send(ConnectionsNotification::Playback { position: position.clone(), duration: duration.clone() }).unwrap();
duration: player.duration(), playback_info.store(PlaybackInfo { position, duration });
position: player.position(),
metadata: player.metadata(),
});
drop(player);
std::thread::sleep(Duration::from_millis(100));
} }
} }
}); });
// Thread for End of Track // Thread for End of Track
let notify_connections = notify_connections_.clone();
s.spawn(move || { futures::executor::block_on(async { s.spawn(move || { futures::executor::block_on(async {
println!("EOS monitor started");
while true { while true {
let _ = finished_recv.recv(); let _ = finished_recv.recv();
println!("End of song"); println!("End of song");
@ -602,16 +619,31 @@ impl Controller {
unreachable!() unreachable!()
}; };
if let Ok(song) = res { if let Ok(song) = res {
notify_next_song.send(song).unwrap(); notify_next_song.send(song.clone()).unwrap();
notify_connections.send(ConnectionsNotification::SongChange(song)).unwrap();
} }
} }
std::thread::sleep(Duration::from_millis(100)); std::thread::sleep(Duration::from_millis(100));
});}); });});
let notify_connections = notify_connections_.clone();
s.spawn(move || {
let mut state = PrismState::Stopped;
while true {
let _state = playback_state.read().unwrap().to_owned();
if _state != state {
state = _state;
println!("State Changed to {state:?}");
notify_connections.send(ConnectionsNotification::StateChange(state.clone())).unwrap();
}
std::thread::sleep(Duration::from_millis(100));
}
});
}); });
// Check for duration and spit it out
// Check for end of song to get the next
println!("Monitor Loops Ended");
Ok(()) Ok(())
} }
@ -643,7 +675,7 @@ impl Controller {
lib_mail.send(LibraryResponse::ImportM3UPlayList(uuid, name)).await.unwrap(); lib_mail.send(LibraryResponse::ImportM3UPlayList(uuid, name)).await.unwrap();
} }
LibraryCommand::Save => { LibraryCommand::Save => {
library.save(config.read().unwrap().libraries.get_library(&library.uuid).unwrap().path.clone()).unwrap(); library.save(config.read().libraries.get_library(&library.uuid).unwrap().path.clone()).unwrap();
lib_mail.send(LibraryResponse::Ok).await.unwrap(); lib_mail.send(LibraryResponse::Ok).await.unwrap();
} }
LibraryCommand::Playlists => { LibraryCommand::Playlists => {
@ -716,7 +748,6 @@ impl Controller {
#[derive(Debug, Default, Serialize, Clone)] #[derive(Debug, Default, Serialize, Clone)]
pub struct PlaybackInfo { pub struct PlaybackInfo {
pub duration: Option<TimeDelta>,
pub position: Option<TimeDelta>, pub position: Option<TimeDelta>,
pub metadata: HashMap<String, String>, pub duration: Option<TimeDelta>,
} }

View file

@ -36,7 +36,6 @@ rfd = "0.15.1"
colog = "1.3.0" colog = "1.3.0"
tempfile = "3.14.0" tempfile = "3.14.0"
opener = "0.7.2" opener = "0.7.2"
discord-presence = { version = "1.4.1", features = ["activity_type"] }
parking_lot = "0.12.3" parking_lot = "0.12.3"
[features] [features]

View file

@ -3,8 +3,7 @@
use std::{borrow::BorrowMut, fs, ops::Deref, path::PathBuf, sync::{atomic::Ordering, Arc}, thread::{scope, spawn}, time::Duration}; use std::{borrow::BorrowMut, fs, ops::Deref, path::PathBuf, sync::{atomic::Ordering, Arc}, thread::{scope, spawn}, time::Duration};
use crossbeam::channel::{bounded, unbounded, Receiver, Sender}; use crossbeam::channel::{bounded, unbounded, Receiver, Sender};
use discord_presence::{models::{Activity, ActivityButton, ActivityTimestamps, ActivityType}, Event}; use dmp_core::{config::{Config, ConfigLibrary}, music_controller::{connections::ConnectionsInput, controller::{Controller, ControllerHandle, LibraryResponse, PlaybackInfo}}, music_storage::library::{MusicLibrary, Song, Tag}};
use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{Controller, ControllerHandle, LibraryResponse, PlaybackInfo}, music_storage::library::{MusicLibrary, Song}};
use futures::channel::oneshot; use futures::channel::oneshot;
use parking_lot::RwLock; use parking_lot::RwLock;
use rfd::FileHandle; use rfd::FileHandle;
@ -77,7 +76,10 @@ pub fn run() {
next_song_notification, next_song_notification,
) = ControllerHandle::new( ) = ControllerHandle::new(
library, library,
std::sync::Arc::new(std::sync::RwLock::new(config)) std::sync::Arc::new(RwLock::new(config)),
Some(ConnectionsInput {
discord_rpc_client_id: std::option_env!("DISCORD_CLIENT_ID").map(|id| id.parse::<u64>().unwrap()),
}),
); );
handle_rx.send(handle).unwrap(); handle_rx.send(handle).unwrap();
@ -123,8 +125,8 @@ pub fn run() {
std::thread::Builder::new() std::thread::Builder::new()
.name("PlaybackInfo handler".to_string()) .name("PlaybackInfo handler".to_string())
.spawn(move || { .spawn(move || {
let mut _info = Arc::new(RwLock::new(PlaybackInfo::default())); let mut _info: Arc<RwLock<PlaybackInfo>> = Arc::new(RwLock::new(PlaybackInfo::default()));
let mut _now_playing = Arc::new(RwLock::new(None)); let mut _now_playing: Arc<RwLock<Option<Song>>> = Arc::new(RwLock::new(None));
scope(|s| { scope(|s| {
let info = _info.clone(); let info = _info.clone();
@ -148,13 +150,9 @@ pub fn run() {
app.emit("now_playing_change", _Song::from(&song)).unwrap(); app.emit("now_playing_change", _Song::from(&song)).unwrap();
app.emit("queue_updated", ()).unwrap(); app.emit("queue_updated", ()).unwrap();
app.emit("playing", ()).unwrap(); app.emit("playing", ()).unwrap();
now_playing.write().insert(song); _ = now_playing.write().insert(song);
} }
}); });
let info = _info.clone();
let now_playing = _now_playing.clone();
}); });
}).unwrap(); }).unwrap();

View file

@ -299,12 +299,12 @@ function PlayBar({ playing, setPlaying }: PlayBarProps) {
useEffect(() => { useEffect(() => {
const unlisten = appWindow.listen<any>("playback_info", ({ payload, }) => { const unlisten = appWindow.listen<any>("playback_info", ({ payload, }) => {
const info = payload as playbackInfo; const info = payload as playbackInfo;
const _pos = Array.isArray(info.position) ? info.position![0] : 0; const pos_ = Array.isArray(info.position) ? info.position![0] : 0;
const _dur = Array.isArray(info.duration) ? info.duration![0] : 0; const dur_ = Array.isArray(info.duration) ? info.duration![0] : 0;
setPosition(_pos); setPosition(pos_);
setDuration(_dur); setDuration(dur_);
let progress = ((_pos/_dur) * 100); let progress = ((dur_/pos_) * 100);
setSeekBarSize(progress) setSeekBarSize(progress)
}) })
return () => { unlisten.then((f) => f()) } return () => { unlisten.then((f) => f()) }
@ -313,7 +313,7 @@ function PlayBar({ playing, setPlaying }: PlayBarProps) {
const seek = (event: React.MouseEvent<HTMLDivElement>) => { const seek = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation(); event.stopPropagation();
let rect = seekBarRef.current!.getBoundingClientRect(); let rect = seekBarRef.current!.getBoundingClientRect();
let val = ((event.clientX-rect.left) / (rect.width))*duration; let val = ((event.clientX-rect.left) / (rect.width))*position;
invoke('seek', { time: Math.round(val * 1000) }).then() invoke('seek', { time: Math.round(val * 1000) }).then()
}; };
@ -340,9 +340,9 @@ function PlayBar({ playing, setPlaying }: PlayBarProps) {
invoke('set_volume', { volume: volume.target.value }).then(() => {}) invoke('set_volume', { volume: volume.target.value }).then(() => {})
}} /> }} />
<p id="timeDisplay"> <p id="timeDisplay">
{ Math.round(+position / 60).toString().padStart(2, "0") }:
{ (+position % 60).toString().padStart(2, "0") }/
{ Math.round(+duration / 60).toString().padStart(2, "0") }: { Math.round(+duration / 60).toString().padStart(2, "0") }:
{ (+position % 60).toString().padStart(2, "0") }/
{ Math.round(+position / 60).toString().padStart(2, "0") }:
{ (+duration % 60).toString().padStart(2, "0") } { (+duration % 60).toString().padStart(2, "0") }
</p> </p>
</div> </div>

View file

@ -63,7 +63,6 @@ export enum BannedType {
} }
export interface playbackInfo { export interface playbackInfo {
duration?: number[],
position?: [number, number], position?: [number, number],
metadata: Map<string, string> duration?: [number, number],
} }