Compare commits

...

2 commits

Author SHA1 Message Date
29774b136b hm yes 2024-11-13 20:33:25 +01:00
939b08887e also for duration 2024-11-13 20:19:41 +01:00
3 changed files with 91 additions and 50 deletions

View file

@ -61,6 +61,8 @@ pub struct PropertyEvent {
#[derive(Clone, Debug)]
pub enum PropertyEventValue {
Int64(i64),
Double(f64),
String(String),
}
#[derive(Clone, Debug)]

View file

@ -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,30 @@ 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 observe_property_string(&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_STRING,
)
})
}
pub fn unobserve_property(&self, registered_reply_userdata: u64) -> Result<u32, Error> {
let rc =
unsafe { ffi::mpv_unobserve_property(self.inner.as_ptr(), registered_reply_userdata) };
@ -264,6 +288,17 @@ impl Handle {
*(data.data as *mut i64)
}))
}
ffi::mpv_format_MPV_FORMAT_DOUBLE => {
Some(PropertyEventValue::Double(unsafe {
*(data.data as *mut f64)
}))
}
ffi::mpv_format_MPV_FORMAT_STRING => {
let value = unsafe { CStr::from_ptr(*(data.data as *mut *mut c_char)) };
Some(PropertyEventValue::String(
String::from_utf8_lossy(value.to_bytes()).into_owned(),
))
}
_ => todo!(),
},
}))

View file

@ -65,8 +65,8 @@ mod imp {
playlist_pos: Cell<i64>,
#[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<f64>, // 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,11 @@ 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_string(0, "path").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 +129,7 @@ mod imp {
_pause: (),
playlist_pos: Cell::new(-1),
_time_pos: (),
_duration: (),
duration: Cell::new(0.0),
_idle_active: (),
_playlist_count: (),
@ -268,11 +269,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 +318,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,28 +473,16 @@ 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::<f64>("duration").unwrap()
}
}
}
fn idle_active(&self) -> bool {
self.mpv.get_property("idle-active").unwrap()
match self.state.get() {
State::Idle => true,
_ => false,
}
}
fn playlist_count(&self) -> i64 {
let count = self.mpv.get_property::<i64>("playlist-count").unwrap();
assert_eq!(count as u32, self.playlist_model.n_items());
assert_eq!(count as u32, self.playlist_model.n_items()); // sanity check
count
}
@ -555,6 +534,18 @@ mod imp {
use crate::mpv::event::PropertyEventValue;
match event.reply_userdata {
0 => {
/* FIXME: can happen before playlist-pos is updated
assert_eq!(event.name, "path");
match event.value {
None => {}
Some(PropertyEventValue::String(s)) => {
// sanity check
assert_eq!(self.obj().song().unwrap().stream_url(), s);
}
_ => unreachable!(),
}*/
}
3 => {
assert_eq!(event.name, "playlist-pos");
let value = match event.value {
@ -581,7 +572,28 @@ 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");
{
let left = value as i64;
let right = self.song().unwrap().duration();
if left != right {
event!(
Level::WARN,
"mpv duration {left:?} doesn not match subsonic duration {right:?}"
);
}
}
}
_ => unreachable!(),
@ -633,6 +645,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 +667,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
@ -713,23 +734,6 @@ mod imp {
self.state.set(State::FileLoaded);
event!(Level::INFO, "FileLoaded");
// sanity check
// i think "path" is only available after this event is dispatched
assert_eq!(
self.mpv.get_property::<String>("path").unwrap(),
self.obj().song().unwrap().stream_url()
);
// same with "duration"
{
let left = self.mpv.get_property::<f64>("duration").unwrap() as i64;
let right = self.song().unwrap().duration();
if left != right {
event!(
Level::WARN,
"mpv duration {left:?} doesn not match subsonic duration {right:?}"
);
}
}
}
fn on_seek(&self) {