Compare commits

..

No commits in common. "19528beb22eace24d2b904ce087a0072015a642d" and "940254b4fd6e3e41f50872c1bcdd8604836bf511" have entirely different histories.

7 changed files with 52 additions and 214 deletions

View file

@ -25,6 +25,7 @@ mod imp {
window.present(); window.present();
glib::spawn_future_local(async move { glib::spawn_future_local(async move {
// run this in glib's main loop
let conn = zbus::connection::Builder::session() let conn = zbus::connection::Builder::session()
.expect("could not connect to the session bus") .expect("could not connect to the session bus")
.internal_executor(false) .internal_executor(false)
@ -32,17 +33,6 @@ mod imp {
.await .await
.expect("could not build connection to the session bus"); .expect("could not build connection to the session bus");
// run this in glib's main loop
glib::spawn_future_local(glib::clone!(
#[strong]
conn,
async move {
loop {
conn.executor().tick().await;
}
}
));
crate::Mpris::setup(conn.object_server(), &window) crate::Mpris::setup(conn.object_server(), &window)
.await .await
.expect("could not serve mpris"); .expect("could not serve mpris");
@ -59,6 +49,10 @@ mod imp {
conn.request_name("org.mpris.MediaPlayer2.audrey") conn.request_name("org.mpris.MediaPlayer2.audrey")
.await .await
.expect("could not register name in session bus"); .expect("could not register name in session bus");
loop {
conn.executor().tick().await;
}
}); });
} }
Some(win) => win.present(), Some(win) => win.present(),

View file

@ -1,4 +1,3 @@
/*
[DBus (name = "org.mpris.MediaPlayer2.Player")] [DBus (name = "org.mpris.MediaPlayer2.Player")]
class Audrey.MprisPlayer : Object { class Audrey.MprisPlayer : Object {
internal signal void on_next (); internal signal void on_next ();
@ -23,9 +22,9 @@ class Audrey.MprisPlayer : Object {
public signal void seeked (int64 position); public signal void seeked (int64 position);
public string playback_status { owned get; internal set; default = "Stopped"; } public string playback_status { owned get; internal set; default = "Stopped"; }
public string loop_status { owned get; set; default = "None"; } public string loop_status { owned get; /*set;*/ default = "None"; }
public double rate { get; set default = 1.0; } public double rate { get; /*set*/ default = 1.0; }
public bool shuffle { get; set default = false; } public bool shuffle { get; /*set*/ default = false; }
public HashTable<string, Variant> metadata { owned get; private set; default = new HashTable<string, Variant> (null, null); } public HashTable<string, Variant> metadata { owned get; private set; default = new HashTable<string, Variant> (null, null); }
public double volume { get; set; default = 1.0; } public double volume { get; set; default = 1.0; }
[CCode (notify = false)] [CCode (notify = false)]
@ -220,4 +219,3 @@ class Audrey.MprisPlayer : Object {
debug ("destroying mpris player"); debug ("destroying mpris player");
} }
} }
*/

View file

@ -1,45 +1,11 @@
use glib::SendWeakRef; use glib::SendWeakRef;
use gtk::glib; use gtk::glib;
use std::collections::HashMap;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::zvariant::{ObjectPath, OwnedObjectPath, Value}; use zbus::zvariant::{ObjectPath, Value};
const MICROSECONDS: f64 = 1e6; // in a second
#[derive(Default)]
struct MetadataMap {
pub track_id: Option<OwnedObjectPath>,
// rest: TODO
}
impl MetadataMap {
fn from_playbin_song(song: Option<&crate::playbin::Song>) -> Self {
song.map(|song| MetadataMap {
// FIXME: see if there's a better way to do this lol
track_id: Some({
let song_object_intptr =
glib::translate::ToGlibPtr::<*const _>::to_glib_none(song).0 as usize;
format!("/eu/callcc/audrey/TrackId/{song_object_intptr:x}")
.try_into()
.unwrap()
}),
})
.unwrap_or_else(Default::default)
}
fn as_hash_map(&self) -> HashMap<&'static str, Value> {
let mut map = HashMap::new();
if let Some(track_id) = &self.track_id {
map.insert("mpris:trackid", Value::new(track_id.as_ref()));
}
map
}
}
pub struct Player { pub struct Player {
playbin: SendWeakRef<crate::Playbin>, playbin: SendWeakRef<crate::Playbin>,
metadata: MetadataMap, metadata: Vec<(String, String)>,
volume: f64,
} }
impl Player { impl Player {
@ -51,13 +17,12 @@ impl Player {
let player = Self { let player = Self {
playbin: playbin.downgrade().into(), playbin: playbin.downgrade().into(),
metadata: MetadataMap::from_playbin_song(None), metadata: Default::default(),
volume: (playbin.volume() as f64) / 100.0,
}; };
object_server.at("/org/mpris/MediaPlayer2", player).await?; object_server.at("/org/mpris/MediaPlayer2", player).await?;
let player_ref = object_server let player = object_server
.interface::<_, Self>("/org/mpris/MediaPlayer2") .interface::<_, Self>("/org/mpris/MediaPlayer2")
.await?; .await?;
@ -66,40 +31,10 @@ impl Player {
false, false,
glib::closure_local!( glib::closure_local!(
#[strong] #[strong]
player_ref, player,
move |_playbin: &crate::Playbin, song: &crate::playbin::Song| { move |_playbin: &crate::Playbin| {
let metadata = MetadataMap::from_playbin_song(Some(song)); let _player = player.get_mut();
// TODO
let player_ref = player_ref.clone();
glib::spawn_future_local(async move {
let mut player = player_ref.get_mut().await;
player.metadata = metadata;
player
.metadata_changed(player_ref.signal_emitter())
.await
.unwrap();
});
}
),
);
playbin.connect_notify_local(
Some("volume"),
glib::clone!(
#[strong]
player_ref,
move |playbin: &crate::Playbin, _| {
let playbin_volume = playbin.volume();
let player_ref = player_ref.clone();
glib::spawn_future_local(async move {
let mut player = player_ref.get_mut().await;
player.volume = (playbin_volume as f64) / 100.0;
player
.volume_changed(player_ref.signal_emitter())
.await
.unwrap();
});
} }
), ),
); );
@ -111,122 +46,39 @@ impl Player {
#[zbus::interface(name = "org.mpris.MediaPlayer2.Player")] #[zbus::interface(name = "org.mpris.MediaPlayer2.Player")]
impl Player { impl Player {
fn next(&self) { fn next(&self) {
// If CanGoNext is false, attempting to call this method should have no effect. todo!()
if !self.can_go_next() {
return;
}
let playbin = self.playbin.upgrade().unwrap();
if playbin.play_queue_position() + 1 > playbin.play_queue_length() {
// If there is no next track (and endless playback and track repeat are both off), stop playback.
self.stop();
} else {
playbin.go_to_next_track();
}
} }
fn previous(&self) { fn previous(&self) {
// If CanGoPrevious is false, attempting to call this method should have no effect. todo!()
if !self.can_go_previous() {
return;
}
let playbin = self.playbin.upgrade().unwrap();
if playbin.play_queue_position() == 0 {
// If there is no previous track (and endless playback and track repeat are both off), stop playback.
self.stop();
} else {
playbin.go_to_prev_track();
}
} }
fn pause(&self) { fn pause(&self) {
// If CanPause is false, attempting to call this method should have no effect. todo!()
if !self.can_pause() {
return;
}
self.playbin.upgrade().unwrap().pause();
} }
fn play_pause(&self) { fn play_pause(&self) {
// don't think this is exactly according to spec but it looks more reasonable to me todo!()
if self.playbin.upgrade().unwrap().state() == crate::playbin::State::Paused {
self.play();
} else {
self.pause();
}
} }
fn stop(&self) { fn stop(&self) {
// TODO: Calling Play after this should cause playback to start again from the beginning of the track. todo!()
// (not the play queue!!)
self.playbin.upgrade().unwrap().stop();
} }
fn play(&self) { fn play(&self) {
// If CanPlay is false, attempting to call this method should have no effect. todo!()
if !self.can_play() {
return;
}
let playbin = self.playbin.upgrade().unwrap();
// If there is no track to play, this has no effect.
if playbin.play_queue_length() == 0 {
return;
}
playbin.play();
} }
fn seek(&self, offset: i64) { fn seek(&self, _offset: i64) {
// If the CanSeek property is false, this has no effect. todo!()
if !self.can_seek() {
return;
}
let playbin = self.playbin.upgrade().unwrap();
// Seeks forward in the current track by the specified number of microseconds.
let mut new_position = (playbin.position() * MICROSECONDS) as i64 + offset;
// A negative value seeks back. If this would mean seeking back further than the start of the track, the position is set to 0.
if new_position < 0 {
new_position = 0;
}
// If the value passed in would mean seeking beyond the end of the track, acts like a call to Next.
if new_position >= (playbin.duration() * MICROSECONDS) as i64 {
return self.next();
}
playbin.seek(new_position as f64 / MICROSECONDS);
} }
fn set_position(&self, track_id: ObjectPath<'_>, position: i64) { fn set_position(&self, _track_id: ObjectPath<'_>, _position: i64) {
let playbin = self.playbin.upgrade().unwrap(); todo!()
// If the Position argument is less than 0, do nothing.
if position < 0 {
return;
}
// If the Position argument is greater than the track length, do nothing.
if position > (playbin.duration() * MICROSECONDS) as i64 {
return;
}
// If the CanSeek property is false, this has no effect.
if !self.can_seek() {
return;
}
// check if it's stale
if self.metadata.track_id.as_deref() != Some(&track_id) {
// TODO: warn of stale seek
return;
}
playbin.seek(position as f64 / MICROSECONDS);
} }
fn open_uri(&self, _s: &str) -> zbus::fdo::Result<()> { fn open_uri(&self, _s: &str) {
Err(zbus::fdo::Error::NotSupported("OpenUri".into())) todo!()
} }
#[zbus(signal)] #[zbus(signal)]
@ -257,8 +109,8 @@ impl Player {
} }
#[zbus(property)] #[zbus(property)]
fn set_playback_rate(&self, _playback_rate: f64) -> zbus::Result<()> { fn set_playback_rate(&self, _playback_rate: f64) {
Err(zbus::Error::Unsupported) todo!()
} }
#[zbus(property)] #[zbus(property)]
@ -272,8 +124,11 @@ impl Player {
} }
#[zbus(property)] #[zbus(property)]
fn metadata(&self) -> HashMap<&'static str, Value> { fn metadata(&self) -> Vec<(String, Value)> {
self.metadata.as_hash_map() self.metadata
.iter()
.map(|(k, v)| (k.clone(), v.into()))
.collect()
} }
#[zbus(property)] #[zbus(property)]
@ -282,15 +137,13 @@ impl Player {
} }
#[zbus(property)] #[zbus(property)]
fn set_volume(&mut self, volume: f64) { fn set_volume(&self, _volume: f64) {
let playbin = self.playbin.upgrade().unwrap(); todo!()
// FIXME: check if this is set by the notify callback: self.volume = volume;
playbin.set_volume((volume * 100.0) as i32);
} }
#[zbus(property(emits_changed_signal = "false"))] #[zbus(property(emits_changed_signal = "false"))]
fn position(&self) -> i64 { fn position(&self) -> i64 {
(self.playbin.upgrade().unwrap().position() * MICROSECONDS) as i64 (self.playbin.upgrade().unwrap().position() * 1e9) as i64
} }
#[zbus(property)] #[zbus(property)]
@ -305,31 +158,31 @@ impl Player {
#[zbus(property)] #[zbus(property)]
fn can_go_next(&self) -> bool { fn can_go_next(&self) -> bool {
self.playbin.upgrade().unwrap().state() != crate::playbin::State::Stopped false // TODO
} }
#[zbus(property)] #[zbus(property)]
fn can_go_previous(&self) -> bool { fn can_go_previous(&self) -> bool {
self.playbin.upgrade().unwrap().state() != crate::playbin::State::Stopped false // TODO
} }
#[zbus(property)] #[zbus(property)]
fn can_play(&self) -> bool { fn can_play(&self) -> bool {
self.playbin.upgrade().unwrap().state() != crate::playbin::State::Stopped false // TODO
} }
#[zbus(property)] #[zbus(property)]
fn can_pause(&self) -> bool { fn can_pause(&self) -> bool {
self.playbin.upgrade().unwrap().state() != crate::playbin::State::Stopped false // TODO
} }
#[zbus(property)] #[zbus(property)]
fn can_seek(&self) -> bool { fn can_seek(&self) -> bool {
self.playbin.upgrade().unwrap().state() != crate::playbin::State::Stopped false // TODO
} }
#[zbus(property(emits_changed_signal = "const"))] #[zbus(property(emits_changed_signal = "const"))]
fn can_control(&self) -> bool { fn can_control(&self) -> bool {
true false // TODO
} }
} }

View file

@ -35,7 +35,6 @@ pub mod ffi {
pub fn audrey_playbin_get_play_queue_position( pub fn audrey_playbin_get_play_queue_position(
self_: *mut AudreyPlaybin, self_: *mut AudreyPlaybin,
) -> std::ffi::c_uint; ) -> std::ffi::c_uint;
pub fn audrey_playbin_get_play_queue_length(self_: *mut AudreyPlaybin) -> std::ffi::c_uint;
pub fn audrey_playbin_move_track( pub fn audrey_playbin_move_track(
self_: *mut AudreyPlaybin, self_: *mut AudreyPlaybin,
from: std::ffi::c_uint, from: std::ffi::c_uint,
@ -112,10 +111,6 @@ impl Playbin {
unsafe { ffi::audrey_playbin_get_play_queue_position(self.to_glib_none().0) } unsafe { ffi::audrey_playbin_get_play_queue_position(self.to_glib_none().0) }
} }
pub fn play_queue_length(&self) -> u32 {
unsafe { ffi::audrey_playbin_get_play_queue_length(self.to_glib_none().0) }
}
pub fn move_track(&self, from: u32, to: u32) { pub fn move_track(&self, from: u32, to: u32) {
unsafe { ffi::audrey_playbin_move_track(self.to_glib_none().0, from, to) } unsafe { ffi::audrey_playbin_move_track(self.to_glib_none().0, from, to) }
} }

View file

@ -77,7 +77,7 @@ public class Audrey.Playbin : GLib.Object {
if (ret >= 0) { if (ret >= 0) {
_volume = value; _volume = value;
} else { } else {
warning ("failed to set volume to %d: %s", value, ret.to_string ()); warning ("failed to set volume: %s", ret.to_string ());
} }
} }
} }
@ -98,11 +98,9 @@ public class Audrey.Playbin : GLib.Object {
public int play_queue_position { get; private set; default = -1; } public int play_queue_position { get; private set; default = -1; }
// signalled when a new track is current // signalled when a new track is current
public signal void new_track (PlaybinSong song); public signal void new_track ();
// signalled when the last track is over // signalled when the last track is over
public signal void stopped (); public signal void stopped ();
// signalled when the position changes discontinuously
public signal void seeked (double position);
// these are mostly synced with mpv // these are mostly synced with mpv
public double position { get; private set; default = 0.0; } public double position { get; private set; default = 0.0; }
@ -213,10 +211,9 @@ public class Audrey.Playbin : GLib.Object {
// estimate duration from api data // estimate duration from api data
// while mpv doesn't know it // while mpv doesn't know it
var song = (PlaybinSong) this._play_queue.get_item (this.play_queue_position); this.duration = ((PlaybinSong) this._play_queue.get_item (this.play_queue_position)).duration;
this.duration = song.duration;
this.new_track (song); this.new_track ();
break; break;
case Mpv.EventId.END_FILE: case Mpv.EventId.END_FILE:
@ -265,7 +262,6 @@ public class Audrey.Playbin : GLib.Object {
warning (@"could not seek to $position: $rc"); warning (@"could not seek to $position: $rc");
} else { } else {
this.position = position; this.position = position;
this.seeked (position);
} }
} }

View file

@ -42,8 +42,8 @@ pub struct Child {
pub title: String, pub title: String,
pub album: String, pub album: String,
pub artist: String, pub artist: String,
pub track: Option<u32>, pub track: u32,
pub year: Option<u32>, pub year: u32,
pub starred: Option<()>, pub starred: Option<()>,
pub duration: u64, pub duration: u64,
pub play_count: Option<u32>, pub play_count: Option<u32>,

View file

@ -29,6 +29,8 @@ public class Audrey.Ui.Window : Adw.ApplicationWindow {
Object (application: app); Object (application: app);
} }
private MprisPlayer mpris_player;
private void now_playing (PlaybinSong song) { private void now_playing (PlaybinSong song) {
this.song = song; this.song = song;
// api.scrobble.begin (this.song.id); TODO // api.scrobble.begin (this.song.id); TODO