diff --git a/src/mpv/event.rs b/src/mpv/event.rs index 2586997..2617ce4 100644 --- a/src/mpv/event.rs +++ b/src/mpv/event.rs @@ -61,6 +61,7 @@ pub struct PropertyEvent { #[derive(Clone, Debug)] pub enum PropertyEventValue { Int64(i64), + Double(f64), } #[derive(Clone, Debug)] diff --git a/src/mpv/handle.rs b/src/mpv/handle.rs index d8935b0..e55e6b6 100644 --- a/src/mpv/handle.rs +++ b/src/mpv/handle.rs @@ -141,7 +141,7 @@ impl Handle { }) } - pub fn observe_property_i64(&self, reply_userdata: u64, name: &str) -> Result<(), Error> { + pub fn observe_property_int64(&self, reply_userdata: u64, name: &str) -> Result<(), Error> { let name = CString::new(name).expect("null bytes in property name"); Error::from_return_code(unsafe { ffi::mpv_observe_property( @@ -153,6 +153,18 @@ impl Handle { }) } + pub fn observe_property_double(&self, reply_userdata: u64, name: &str) -> Result<(), Error> { + let name = CString::new(name).expect("null bytes in property name"); + Error::from_return_code(unsafe { + ffi::mpv_observe_property( + self.inner.as_ptr(), + reply_userdata, + name.as_ptr(), + ffi::mpv_format_MPV_FORMAT_DOUBLE, + ) + }) + } + pub fn unobserve_property(&self, registered_reply_userdata: u64) -> Result { let rc = unsafe { ffi::mpv_unobserve_property(self.inner.as_ptr(), registered_reply_userdata) }; @@ -264,6 +276,11 @@ impl Handle { *(data.data as *mut i64) })) } + ffi::mpv_format_MPV_FORMAT_DOUBLE => { + Some(PropertyEventValue::Double(unsafe { + *(data.data as *mut f64) + })) + } _ => todo!(), }, })) diff --git a/src/ui/window.rs b/src/ui/window.rs index 3491621..aaadc01 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -65,8 +65,8 @@ mod imp { playlist_pos: Cell, #[property(type = f64, get = Self::time_pos)] _time_pos: (), - #[property(type = f64, get = Self::duration)] - _duration: (), // as reported by mpv, compare with song.duration + #[property(get)] + duration: Cell, // as reported by mpv, compare with song.duration #[property(type = bool, get = Self::idle_active)] _idle_active: (), #[property(type = i64, get = Self::playlist_count)] @@ -97,10 +97,10 @@ mod imp { mpv.set_property("vid", false).unwrap(); mpv.set_property("prefetch-playlist", true).unwrap(); - mpv.observe_property_i64(3, "playlist-pos").unwrap(); + mpv.observe_property_int64(3, "playlist-pos").unwrap(); mpv.observe_property(4, "idle-active").unwrap(); mpv.observe_property(6, "playlist-count").unwrap(); - mpv.observe_property(7, "duration").unwrap(); + mpv.observe_property_double(7, "duration").unwrap(); // "Useful to drain property changes before a new file is loaded." mpv.add_hook(0, "on_before_start_file", 0).unwrap(); @@ -128,7 +128,7 @@ mod imp { _pause: (), playlist_pos: Cell::new(-1), _time_pos: (), - _duration: (), + duration: Cell::new(0.0), _idle_active: (), _playlist_count: (), @@ -268,11 +268,6 @@ mod imp { // only send property change notifications after the event queue is drained let freeze_notify = window.freeze_notify(); - event!( - Level::TRACE, - "before event loop, state is {:?}", - window.imp().state.get() - ); let listener = loop { let listener = window.imp().mpv.wakeup_listener(); @@ -322,11 +317,6 @@ mod imp { // send property change notifications now drop(freeze_notify); - event!( - Level::TRACE, - "after event loop, state is {:?}", - window.imp().state.get() - ); drop(window); listener.await; @@ -482,21 +472,6 @@ mod imp { } } - fn duration(&self) -> f64 { - match self.state.get() { - State::Idle => 0.0, - - State::FileLoading | State::FileEnded => self - .song() - .map(|song| song.duration() as f64) - .unwrap_or(0.0), - - State::FileLoaded | State::Active | State::Seeking => { - self.mpv.get_property::("duration").unwrap() - } - } - } - fn idle_active(&self) -> bool { self.mpv.get_property("idle-active").unwrap() } @@ -581,6 +556,16 @@ mod imp { 7 => { assert_eq!(event.name, "duration"); + let value = match event.value { + None => return, // don't change, could be one of two things + // 1. we are switching tracks (duration is set in + // on_start_file) + // 2. we are done (duration is set in on_idle_active) + Some(PropertyEventValue::Double(f)) => f, + _ => unreachable!(), + }; + self.duration.set(value); + event!(Level::TRACE, "duration is now {value} (from mpv)"); self.obj().notify("duration"); } @@ -633,6 +618,10 @@ mod imp { other => panic!("invalid state transition: IdleActive from {other:?}"), } self.state.set(State::Idle); + + self.duration.set(0.0); + event!(Level::DEBUG, "duration is now 0 (idle active)"); + self.obj().notify("duration"); } fn on_start_file(&self) { @@ -651,6 +640,11 @@ mod imp { self.obj().notify("song"); self.buffering_start(); + let duration = self.obj().song().unwrap().duration() as f64; + self.duration.set(duration); + event!(Level::TRACE, "duration is now {duration} (from subsonic)"); + self.obj().notify("duration"); + let window = self.obj().clone(); let song_id = window.song().unwrap().id(); if let Some(handle) = self