Compare commits
3 commits
4c90d0f10e
...
20aaacd40e
Author | SHA1 | Date | |
---|---|---|---|
20aaacd40e | |||
378eb0761d | |||
eab11095c8 |
4 changed files with 97 additions and 25 deletions
|
@ -9,7 +9,7 @@ use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Value};
|
||||||
const MICROSECONDS: f64 = 1e6; // in a second
|
const MICROSECONDS: f64 = 1e6; // in a second
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct MetadataMap {
|
pub struct MetadataMap {
|
||||||
// mpris
|
// mpris
|
||||||
track_id: Option<OwnedObjectPath>,
|
track_id: Option<OwnedObjectPath>,
|
||||||
length: Option<i64>,
|
length: Option<i64>,
|
||||||
|
@ -37,7 +37,7 @@ struct MetadataMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetadataMap {
|
impl MetadataMap {
|
||||||
fn from_playbin_song(song: Option<&Song>) -> Self {
|
pub fn from_playbin_song(song: Option<&Song>) -> Self {
|
||||||
song.map(|song| MetadataMap {
|
song.map(|song| MetadataMap {
|
||||||
// use a unique growing counter to identify tracks
|
// use a unique growing counter to identify tracks
|
||||||
track_id: Some({
|
track_id: Some({
|
||||||
|
@ -115,7 +115,6 @@ impl MetadataMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
metadata: MetadataMap,
|
|
||||||
window: SendWeakRef<Window>,
|
window: SendWeakRef<Window>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +124,6 @@ impl Player {
|
||||||
playbin: &Window,
|
playbin: &Window,
|
||||||
) -> Result<InterfaceRef<Player>, zbus::Error> {
|
) -> Result<InterfaceRef<Player>, zbus::Error> {
|
||||||
let player = Self {
|
let player = Self {
|
||||||
metadata: MetadataMap::from_playbin_song(None),
|
|
||||||
window: playbin.downgrade().into(),
|
window: playbin.downgrade().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -268,7 +266,8 @@ impl Player {
|
||||||
|
|
||||||
#[tracing::instrument(skip(self), parent = None, target = "audrey::mpris", level = Level::DEBUG, ret)]
|
#[tracing::instrument(skip(self), parent = None, target = "audrey::mpris", level = Level::DEBUG, ret)]
|
||||||
fn set_position(&self, track_id: ObjectPath<'_>, position: i64) -> zbus::fdo::Result<()> {
|
fn set_position(&self, track_id: ObjectPath<'_>, position: i64) -> zbus::fdo::Result<()> {
|
||||||
if Some(track_id) == self.metadata.track_id.as_ref().map(|x| x.as_ref()) {
|
let metadata = self.metadata();
|
||||||
|
if Some(&track_id.into()) == metadata.get("track_id") {
|
||||||
self.window().set_time_pos(position as f64 / MICROSECONDS);
|
self.window().set_time_pos(position as f64 / MICROSECONDS);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -329,7 +328,8 @@ impl Player {
|
||||||
|
|
||||||
#[zbus(property)]
|
#[zbus(property)]
|
||||||
fn metadata(&self) -> HashMap<&'static str, OwnedValue> {
|
fn metadata(&self) -> HashMap<&'static str, OwnedValue> {
|
||||||
self.metadata.as_hash_map()
|
// TODO: no need to MetadataMap wrapper anymore
|
||||||
|
MetadataMap::from_playbin_song(self.window().song().as_ref()).as_hash_map()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[zbus(property)]
|
#[zbus(property)]
|
||||||
|
|
|
@ -63,6 +63,7 @@ pub enum PropertyEventValue {
|
||||||
Int64(i64),
|
Int64(i64),
|
||||||
Double(f64),
|
Double(f64),
|
||||||
String(String),
|
String(String),
|
||||||
|
Flag(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use super::event::{
|
||||||
use super::{ffi, Error, Event as MpvEvent, GetProperty, SetProperty};
|
use super::{ffi, Error, Event as MpvEvent, GetProperty, SetProperty};
|
||||||
use event_listener::{Event, EventListener, IntoNotification};
|
use event_listener::{Event, EventListener, IntoNotification};
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::ffi::{c_char, c_void, CStr, CString};
|
use std::ffi::{c_char, c_int, c_void, CStr, CString};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
@ -177,6 +177,18 @@ impl Handle {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn observe_property_flag(&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_FLAG,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unobserve_property(&self, registered_reply_userdata: u64) -> Result<u32, Error> {
|
pub fn unobserve_property(&self, registered_reply_userdata: u64) -> Result<u32, Error> {
|
||||||
let rc =
|
let rc =
|
||||||
unsafe { ffi::mpv_unobserve_property(self.inner.as_ptr(), registered_reply_userdata) };
|
unsafe { ffi::mpv_unobserve_property(self.inner.as_ptr(), registered_reply_userdata) };
|
||||||
|
@ -299,6 +311,13 @@ impl Handle {
|
||||||
String::from_utf8_lossy(value.to_bytes()).into_owned(),
|
String::from_utf8_lossy(value.to_bytes()).into_owned(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
ffi::mpv_format_MPV_FORMAT_FLAG => Some(PropertyEventValue::Flag(unsafe {
|
||||||
|
match *(data.data as *mut c_int) {
|
||||||
|
0 => false,
|
||||||
|
1 => true,
|
||||||
|
other => unreachable!("bad mpv flag value {other}"),
|
||||||
|
}
|
||||||
|
})),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -99,7 +99,7 @@ mod imp {
|
||||||
|
|
||||||
mpv.observe_property_string(0, "path").unwrap();
|
mpv.observe_property_string(0, "path").unwrap();
|
||||||
mpv.observe_property_int64(3, "playlist-pos").unwrap();
|
mpv.observe_property_int64(3, "playlist-pos").unwrap();
|
||||||
mpv.observe_property(4, "idle-active").unwrap();
|
mpv.observe_property_flag(4, "idle-active").unwrap();
|
||||||
mpv.observe_property(6, "playlist-count").unwrap();
|
mpv.observe_property(6, "playlist-count").unwrap();
|
||||||
mpv.observe_property_double(7, "duration").unwrap();
|
mpv.observe_property_double(7, "duration").unwrap();
|
||||||
|
|
||||||
|
@ -407,6 +407,44 @@ mod imp {
|
||||||
self.setup.present(Some(self.obj().as_ref()));
|
self.setup.present(Some(self.obj().as_ref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mpris_player(&self) -> Option<InterfaceRef<mpris::Player>> {
|
||||||
|
let borrowed = self.mpris_player.borrow();
|
||||||
|
borrowed.as_ref().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mpris_player_playback_status_changed(&self) {
|
||||||
|
if let Some(iface_ref) = self.mpris_player() {
|
||||||
|
glib::spawn_future_local(async move {
|
||||||
|
let iface = iface_ref.get().await;
|
||||||
|
match iface
|
||||||
|
.playback_status_changed(iface_ref.signal_emitter())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(err) => event!(
|
||||||
|
Level::ERROR,
|
||||||
|
"could not notify new playback status through mpris interface: {err}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mpris_player_metadata_changed(&self) {
|
||||||
|
if let Some(iface_ref) = self.mpris_player() {
|
||||||
|
glib::spawn_future_local(async move {
|
||||||
|
let iface = iface_ref.get().await;
|
||||||
|
match iface.metadata_changed(iface_ref.signal_emitter()).await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(err) => event!(
|
||||||
|
Level::ERROR,
|
||||||
|
"could not notify new metadata through mpris interface: {err}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn volume(&self) -> i64 {
|
fn volume(&self) -> i64 {
|
||||||
self.mpv.get_property("volume").unwrap()
|
self.mpv.get_property("volume").unwrap()
|
||||||
}
|
}
|
||||||
|
@ -414,21 +452,18 @@ mod imp {
|
||||||
fn set_volume(&self, volume: i64) {
|
fn set_volume(&self, volume: i64) {
|
||||||
self.mpv.set_property("volume", volume).unwrap();
|
self.mpv.set_property("volume", volume).unwrap();
|
||||||
|
|
||||||
let iface_borrowed = self.mpris_player.borrow();
|
if let Some(iface_ref) = self.mpris_player() {
|
||||||
let iface_ref = match iface_borrowed.as_ref() {
|
glib::spawn_future_local(async move {
|
||||||
None => return, // zbus not set up yet
|
let iface = iface_ref.get().await;
|
||||||
Some(iface_ref) => iface_ref.clone(),
|
match iface.volume_changed(iface_ref.signal_emitter()).await {
|
||||||
};
|
Ok(()) => {}
|
||||||
glib::spawn_future_local(async move {
|
Err(err) => event!(
|
||||||
let iface = iface_ref.get().await;
|
Level::ERROR,
|
||||||
match iface.volume_changed(iface_ref.signal_emitter()).await {
|
"could not notify new volume through mpris interface: {err}"
|
||||||
Ok(()) => {}
|
),
|
||||||
Err(err) => event!(
|
}
|
||||||
Level::ERROR,
|
});
|
||||||
"could not notify new volume through mpris interface: {err}"
|
}
|
||||||
),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mute(&self) -> bool {
|
fn mute(&self) -> bool {
|
||||||
|
@ -445,6 +480,10 @@ mod imp {
|
||||||
|
|
||||||
fn set_pause(&self, pause: bool) {
|
fn set_pause(&self, pause: bool) {
|
||||||
self.mpv.set_property("pause", pause).unwrap();
|
self.mpv.set_property("pause", pause).unwrap();
|
||||||
|
|
||||||
|
if !self.idle_active() {
|
||||||
|
self.mpris_player_playback_status_changed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn time_pos(&self) -> f64 {
|
fn time_pos(&self) -> f64 {
|
||||||
|
@ -544,8 +583,13 @@ mod imp {
|
||||||
|
|
||||||
4 => {
|
4 => {
|
||||||
assert_eq!(event.name, "idle-active");
|
assert_eq!(event.name, "idle-active");
|
||||||
|
let value = match event.value {
|
||||||
|
Some(PropertyEventValue::Flag(b)) => b,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
event!(Level::TRACE, "idle-active is now {value}");
|
||||||
self.obj().notify("idle-active");
|
self.obj().notify("idle-active");
|
||||||
if self.obj().idle_active() {
|
if value {
|
||||||
self.on_idle_active();
|
self.on_idle_active();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,6 +685,9 @@ mod imp {
|
||||||
|
|
||||||
self.obj().set_playing_cover_art(None::<gdk::Texture>);
|
self.obj().set_playing_cover_art(None::<gdk::Texture>);
|
||||||
self.obj().set_background(None::<gdk::Texture>);
|
self.obj().set_background(None::<gdk::Texture>);
|
||||||
|
|
||||||
|
self.mpris_player_metadata_changed();
|
||||||
|
self.mpris_player_playback_status_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_start_file(&self) {
|
fn on_start_file(&self) {
|
||||||
|
@ -663,11 +710,16 @@ mod imp {
|
||||||
self.obj().notify("song");
|
self.obj().notify("song");
|
||||||
self.buffering_start();
|
self.buffering_start();
|
||||||
|
|
||||||
let duration = self.obj().song().unwrap().duration() as f64;
|
let song = self.obj().song().unwrap();
|
||||||
|
|
||||||
|
let duration = song.duration() as f64;
|
||||||
self.duration.set(duration);
|
self.duration.set(duration);
|
||||||
event!(target: "audrey::playback", Level::DEBUG, "duration is now {duration} (from subsonic)");
|
event!(target: "audrey::playback", Level::DEBUG, "duration is now {duration} (from subsonic)");
|
||||||
self.obj().notify("duration");
|
self.obj().notify("duration");
|
||||||
|
|
||||||
|
self.mpris_player_metadata_changed();
|
||||||
|
self.mpris_player_playback_status_changed();
|
||||||
|
|
||||||
let window = self.obj().clone();
|
let window = self.obj().clone();
|
||||||
let song_id = window.song().unwrap().id();
|
let song_id = window.song().unwrap().id();
|
||||||
if let Some(handle) = self
|
if let Some(handle) = self
|
||||||
|
|
Loading…
Reference in a new issue