Compare commits
No commits in common. "acd1d7d80363e79f87afb977d5657b59b1eab511" and "e4083288e674fb0fc4b6bb9ca3bfd3cc1c167d96" have entirely different histories.
acd1d7d803
...
e4083288e6
8 changed files with 111 additions and 199 deletions
|
@ -150,7 +150,7 @@ template $AudreyUiPlaybar: Adw.Bin {
|
|||
}
|
||||
|
||||
Button {
|
||||
icon-name: bind $mute_button_icon_name (template.mute) as <string>;
|
||||
icon-name: bind $mute_button_icon_name (template.playbin as <$AudreyPlaybin>.mute) as <string>;
|
||||
valign: center;
|
||||
|
||||
clicked => $on_mute_toggle () swapped;
|
||||
|
|
|
@ -17,7 +17,7 @@ pub use playbin::Playbin;
|
|||
pub mod subsonic;
|
||||
pub mod subsonic_vala;
|
||||
|
||||
pub mod playbin2;
|
||||
mod playbin2;
|
||||
|
||||
mod signal;
|
||||
pub use signal::{Signal, SignalEmitter, SignalHandler};
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use crate::mpv;
|
||||
use crate::signal::{Signal, SignalEmitter};
|
||||
use crate::signal::SignalEmitter;
|
||||
use event_listener::EventListener;
|
||||
use std::cell::{Ref, RefCell};
|
||||
use url::Url;
|
||||
|
||||
pub trait PlaybinEntry {
|
||||
fn url(&self) -> Url;
|
||||
fn url(&self) -> &Url;
|
||||
}
|
||||
|
||||
impl PlaybinEntry for Url {
|
||||
fn url(&self) -> Url {
|
||||
self.clone()
|
||||
fn url(&self) -> &Url {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,16 +19,12 @@ pub struct Playbin<E> {
|
|||
mpv: mpv::Handle,
|
||||
entries: RefCell<Vec<E>>,
|
||||
|
||||
volume_changed: SignalEmitter<Self, ()>,
|
||||
muted_changed: SignalEmitter<Self, ()>,
|
||||
paused_changed: SignalEmitter<Self, ()>,
|
||||
current_entry_changed: SignalEmitter<Self, ()>,
|
||||
paused_changed: SignalEmitter<()>,
|
||||
current_entry_changed: SignalEmitter<()>,
|
||||
|
||||
entry_inserted: SignalEmitter<Self, u32>,
|
||||
stopped: SignalEmitter<Self, ()>,
|
||||
entry_removed: SignalEmitter<Self, u32>,
|
||||
|
||||
file_started: SignalEmitter<Self, ()>,
|
||||
entry_inserted: SignalEmitter<u32>,
|
||||
stopped: SignalEmitter<()>,
|
||||
entry_removed: SignalEmitter<u32>,
|
||||
}
|
||||
|
||||
impl<E> Default for Playbin<E> {
|
||||
|
@ -40,10 +36,8 @@ impl<E> Default for Playbin<E> {
|
|||
mpv.set_property("prefetch-playlist", true).unwrap();
|
||||
mpv.set_property("gapless-audio", true).unwrap();
|
||||
|
||||
mpv.observe_property(0, "volume").unwrap();
|
||||
mpv.observe_property(1, "mute").unwrap();
|
||||
mpv.observe_property(2, "pause").unwrap();
|
||||
mpv.observe_property(3, "playlist-pos").unwrap();
|
||||
mpv.observe_property(0, "pause").unwrap();
|
||||
mpv.observe_property(1, "playlist-pos").unwrap();
|
||||
|
||||
// "Useful to drain property changes before a new file is loaded."
|
||||
mpv.add_hook(0, "on_before_start_file", 0).unwrap();
|
||||
|
@ -52,16 +46,12 @@ impl<E> Default for Playbin<E> {
|
|||
mpv,
|
||||
entries: RefCell::new(vec![]),
|
||||
|
||||
volume_changed: Default::default(),
|
||||
muted_changed: Default::default(),
|
||||
paused_changed: Default::default(),
|
||||
current_entry_changed: Default::default(),
|
||||
|
||||
entry_inserted: Default::default(),
|
||||
stopped: Default::default(),
|
||||
entry_removed: Default::default(),
|
||||
|
||||
file_started: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,22 +60,6 @@ impl<E> Playbin<E>
|
|||
where
|
||||
E: PlaybinEntry,
|
||||
{
|
||||
pub fn volume(&self) -> i64 {
|
||||
self.mpv.get_property("volume").unwrap()
|
||||
}
|
||||
|
||||
pub fn set_volume(&self, volume: i64) {
|
||||
self.mpv.set_property("volume", volume).unwrap();
|
||||
}
|
||||
|
||||
pub fn muted(&self) -> bool {
|
||||
self.mpv.get_property("mute").unwrap()
|
||||
}
|
||||
|
||||
pub fn set_muted(&self, muted: bool) {
|
||||
self.mpv.set_property("mute", muted).unwrap();
|
||||
}
|
||||
|
||||
pub fn paused(&self) -> bool {
|
||||
self.mpv.get_property("pause").unwrap()
|
||||
}
|
||||
|
@ -137,7 +111,7 @@ where
|
|||
entries.push(entry);
|
||||
|
||||
drop(entries);
|
||||
self.entry_inserted.emit(self, index as u32);
|
||||
self.entry_inserted.emit(index as u32);
|
||||
}
|
||||
|
||||
pub fn insert_entry(&self, index: u32, entry: E) {
|
||||
|
@ -148,7 +122,7 @@ where
|
|||
entries.insert(index as usize, entry);
|
||||
|
||||
drop(entries);
|
||||
self.entry_inserted.emit(self, index);
|
||||
self.entry_inserted.emit(index);
|
||||
}
|
||||
|
||||
// stop playback and clear playlist
|
||||
|
@ -158,7 +132,7 @@ where
|
|||
entries.clear();
|
||||
|
||||
drop(entries);
|
||||
self.stopped.emit(self, ());
|
||||
self.stopped.emit(());
|
||||
}
|
||||
|
||||
pub fn remove_entry(&self, index: u32) {
|
||||
|
@ -167,7 +141,7 @@ where
|
|||
entries.remove(index as usize);
|
||||
|
||||
drop(entries);
|
||||
self.entry_removed.emit(self, index);
|
||||
self.entry_removed.emit(index);
|
||||
}
|
||||
|
||||
pub fn move_entry(&self, _from: u32, _to: u32) {
|
||||
|
@ -186,26 +160,14 @@ where
|
|||
match event {
|
||||
mpv::Event::PropertyChange(event) => match event.reply_userdata {
|
||||
0 => {
|
||||
assert_eq!(&event.name, "volume");
|
||||
self.volume_changed.emit(self, ());
|
||||
println!("new volume! {:?}", self.volume());
|
||||
}
|
||||
|
||||
1 => {
|
||||
assert_eq!(&event.name, "mute");
|
||||
self.muted_changed.emit(self, ());
|
||||
println!("new muted! {:?}", self.muted());
|
||||
}
|
||||
|
||||
2 => {
|
||||
assert_eq!(&event.name, "pause");
|
||||
self.paused_changed.emit(self, ());
|
||||
self.paused_changed.emit(());
|
||||
println!("new paused! {:?}", self.paused());
|
||||
}
|
||||
|
||||
3 => {
|
||||
1 => {
|
||||
assert_eq!(&event.name, "playlist-pos");
|
||||
self.current_entry_changed.emit(self, ());
|
||||
self.current_entry_changed.emit(());
|
||||
println!("new current_entry! {:?}", self.current_entry());
|
||||
}
|
||||
|
||||
|
@ -223,27 +185,9 @@ where
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|
||||
mpv::Event::StartFile(_) => {
|
||||
// since we set up the hook before, the current song is guaranteed not to change
|
||||
// under our feet
|
||||
self.file_started.emit(self, ());
|
||||
}
|
||||
|
||||
_ => println!("mpv event {:?}", event),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn volume_changed(&self) -> Signal<'_, Self, ()> {
|
||||
self.volume_changed.signal()
|
||||
}
|
||||
|
||||
pub fn muted_changed(&self) -> Signal<'_, Self, ()> {
|
||||
self.muted_changed.signal()
|
||||
}
|
||||
|
||||
pub fn file_started(&self) -> Signal<'_, Self, ()> {
|
||||
self.file_started.signal()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Drop for Playbin<E> {
|
||||
|
|
|
@ -5,14 +5,14 @@ use gtk::{
|
|||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
type SignalHandlerBox<E, T> = Box<dyn FnMut(&E, T) -> bool>;
|
||||
type SignalHandlerBox<T> = Box<dyn FnMut(T) -> bool>;
|
||||
|
||||
pub struct SignalEmitter<E, T> {
|
||||
handlers: RefCell<Vec<SignalHandlerBox<E, T>>>,
|
||||
just_connected: RefCell<Vec<SignalHandlerBox<E, T>>>,
|
||||
pub struct SignalEmitter<T> {
|
||||
handlers: RefCell<Vec<SignalHandlerBox<T>>>,
|
||||
just_connected: RefCell<Vec<SignalHandlerBox<T>>>,
|
||||
}
|
||||
|
||||
impl<E, T> Default for SignalEmitter<E, T> {
|
||||
impl<T> Default for SignalEmitter<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
handlers: RefCell::new(vec![]),
|
||||
|
@ -21,8 +21,8 @@ impl<E, T> Default for SignalEmitter<E, T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Signal<'a, E, T> {
|
||||
just_connected: &'a RefCell<Vec<SignalHandlerBox<E, T>>>,
|
||||
pub struct Signal<'a, T> {
|
||||
just_connected: &'a RefCell<Vec<SignalHandlerBox<T>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -40,19 +40,19 @@ impl SignalHandler {
|
|||
}
|
||||
}
|
||||
|
||||
impl<E, T> Signal<'_, E, T> {
|
||||
fn connect_impl(&self, f: impl FnMut(&E, T) -> bool + 'static) {
|
||||
impl<T> Signal<'_, T> {
|
||||
fn connect_impl(&self, f: impl FnMut(T) -> bool + 'static) {
|
||||
self.just_connected.borrow_mut().push(Box::new(f));
|
||||
}
|
||||
|
||||
pub fn connect(&self, mut f: impl FnMut(&E, T) -> bool + 'static) -> SignalHandler {
|
||||
pub fn connect(&self, mut f: impl FnMut(T) -> bool + 'static) -> SignalHandler {
|
||||
let disconnect = Rc::new(Cell::new(false));
|
||||
let disconnect_weak = Rc::downgrade(&disconnect);
|
||||
|
||||
self.connect_impl(move |e, t| match disconnect.get() {
|
||||
true => false,
|
||||
false => {
|
||||
f(e, t);
|
||||
self.connect_impl(move |t| match disconnect.get() {
|
||||
false => false,
|
||||
true => {
|
||||
f(t);
|
||||
true
|
||||
}
|
||||
});
|
||||
|
@ -63,38 +63,38 @@ impl<E, T> Signal<'_, E, T> {
|
|||
pub fn connect_rc<L: 'static>(
|
||||
&self,
|
||||
listener: &Rc<L>,
|
||||
mut f: impl FnMut(&E, Rc<L>, T) -> bool + 'static,
|
||||
mut f: impl FnMut(Rc<L>, T) -> bool + 'static,
|
||||
) -> SignalHandler {
|
||||
let listener = Rc::downgrade(listener);
|
||||
|
||||
self.connect(move |e, t| match listener.upgrade() {
|
||||
self.connect(move |t| match listener.upgrade() {
|
||||
None => false,
|
||||
Some(listener) => f(e, listener, t),
|
||||
Some(listener) => f(listener, t),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_object<L: IsA<Object>>(
|
||||
&self,
|
||||
listener: &L,
|
||||
mut f: impl FnMut(&E, L, T) -> bool + 'static,
|
||||
mut f: impl FnMut(L, T) -> bool + 'static,
|
||||
) -> SignalHandler {
|
||||
let listener = listener.downgrade();
|
||||
|
||||
self.connect(move |e, t| match listener.upgrade() {
|
||||
self.connect(move |t| match listener.upgrade() {
|
||||
None => false,
|
||||
Some(listener) => f(e, listener, t),
|
||||
Some(listener) => f(listener, t),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T> SignalEmitter<E, T> {
|
||||
pub fn signal(&self) -> Signal<'_, E, T> {
|
||||
impl<T> SignalEmitter<T> {
|
||||
pub fn signal(&self) -> Signal<'_, T> {
|
||||
Signal {
|
||||
just_connected: &self.just_connected,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit_with(&self, emitter: &E, mut f: impl FnMut() -> T) {
|
||||
pub fn emit_with(&self, mut f: impl FnMut() -> T) {
|
||||
let mut handlers = self
|
||||
.handlers
|
||||
.try_borrow_mut()
|
||||
|
@ -108,7 +108,7 @@ impl<E, T> SignalEmitter<E, T> {
|
|||
let mut i = 0;
|
||||
let mut skip = 0;
|
||||
loop {
|
||||
if handlers[i + skip](emitter, f()) {
|
||||
if handlers[i + skip](f()) {
|
||||
i += 1;
|
||||
} else {
|
||||
skip += 1;
|
||||
|
@ -121,16 +121,15 @@ impl<E, T> SignalEmitter<E, T> {
|
|||
handlers.swap(i, i + skip);
|
||||
}
|
||||
|
||||
println!("emitted to {i} listeners");
|
||||
handlers.truncate(i);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T> SignalEmitter<E, T>
|
||||
impl<T> SignalEmitter<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
pub fn emit(&self, emitter: &E, t: T) {
|
||||
self.emit_with(emitter, || t.clone());
|
||||
pub fn emit(&self, t: T) {
|
||||
self.emit_with(|| t.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub mod schema;
|
||||
mod schema;
|
||||
|
||||
use md5::Digest;
|
||||
use rand::Rng;
|
||||
|
@ -145,22 +145,17 @@ impl Client {
|
|||
}
|
||||
}
|
||||
|
||||
fn url(&self, path: &[&str], query: &[(&str, &str)]) -> url::Url {
|
||||
let mut url = self.base_url.clone();
|
||||
url.path_segments_mut()
|
||||
// literally can't fail
|
||||
.unwrap_or_else(|_| unsafe { std::hint::unreachable_unchecked() })
|
||||
.extend(path);
|
||||
url.query_pairs_mut().extend_pairs(query);
|
||||
url
|
||||
}
|
||||
|
||||
async fn get<T: serde::de::DeserializeOwned + Send + 'static>(
|
||||
&self,
|
||||
path: &[&str],
|
||||
query: &[(&str, &str)],
|
||||
) -> Result<T, Error> {
|
||||
self.send(self.client.get(self.url(path, query))).await
|
||||
let mut url = self.base_url.clone();
|
||||
url.path_segments_mut()
|
||||
// literally can't fail
|
||||
.unwrap_or_else(|_| unsafe { std::hint::unreachable_unchecked() })
|
||||
.extend(path);
|
||||
self.send(self.client.get(url).query(query)).await
|
||||
}
|
||||
|
||||
pub async fn ping(&self) -> Result<(), Error> {
|
||||
|
@ -175,10 +170,6 @@ impl Client {
|
|||
.await
|
||||
.map(|response| response.random_songs.song)
|
||||
}
|
||||
|
||||
pub fn stream_url(&self, id: &str) -> url::Url {
|
||||
self.url(&["rest", "stream"], &[("id", id)])
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Client {
|
||||
|
|
|
@ -21,8 +21,6 @@ mod imp {
|
|||
|
||||
#[property(get, set)]
|
||||
volume: Cell<i32>,
|
||||
#[property(get, set)]
|
||||
mute: Cell<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -158,7 +156,8 @@ mod imp {
|
|||
|
||||
#[template_callback]
|
||||
fn on_mute_toggle(&self) {
|
||||
self.obj().set_mute(!self.obj().mute());
|
||||
let playbin = self.playbin.upgrade().unwrap();
|
||||
playbin.set_mute(!playbin.mute());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
mod imp {
|
||||
use crate::signal::SignalEmitter;
|
||||
use adw::{glib, prelude::*, subclass::prelude::*};
|
||||
use glib::subclass::{InitializingObject, Signal};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::Rc;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
#[derive(gtk::CompositeTemplate, glib::Properties, Default)]
|
||||
|
@ -24,8 +22,6 @@ mod imp {
|
|||
username: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
password: RefCell<String>,
|
||||
|
||||
pub(super) connected: SignalEmitter<super::Setup, Rc<crate::subsonic::Client>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -149,7 +145,6 @@ mod imp {
|
|||
self.obj().set_authn_can_edit(true);
|
||||
|
||||
self.obj().emit_by_name::<()>("connected", &[&vala_api]);
|
||||
self.connected.emit(self.obj().as_ref(), Rc::new(api));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,10 +170,6 @@ impl Default for Setup {
|
|||
}
|
||||
}
|
||||
|
||||
use crate::signal::Signal;
|
||||
use crate::subsonic;
|
||||
use std::rc::Rc;
|
||||
|
||||
impl Setup {
|
||||
pub fn load(&self) {
|
||||
glib::spawn_future_local(glib::clone!(
|
||||
|
@ -218,10 +209,6 @@ impl Setup {
|
|||
}
|
||||
));
|
||||
}
|
||||
|
||||
pub fn connected(&self) -> Signal<'_, super::Setup, Rc<subsonic::Client>> {
|
||||
self.imp().connected.signal()
|
||||
}
|
||||
}
|
||||
|
||||
mod ffi {
|
||||
|
|
118
src/ui/window.rs
118
src/ui/window.rs
|
@ -26,9 +26,9 @@ mod imp {
|
|||
song: RefCell<Option<crate::playbin::Song>>,
|
||||
|
||||
pub(super) setup: crate::ui::Setup,
|
||||
pub(super) api: RefCell<Option<crate::subsonic_vala::Client>>,
|
||||
|
||||
pub(super) playbin2: Rc<crate::playbin2::Playbin<url::Url>>,
|
||||
pub(super) api2: RefCell<Option<Rc<crate::subsonic::Client>>>,
|
||||
playbin2: Rc<crate::playbin2::Playbin<url::Url>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -52,6 +52,13 @@ mod imp {
|
|||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
self.playbin2.tick();
|
||||
self.playbin2.push_entry(
|
||||
"https://www.youtube.com/watch?v=19y8YTbvri8"
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let playbin = Rc::downgrade(&self.playbin2);
|
||||
glib::spawn_future_local(glib::clone!(async move {
|
||||
loop {
|
||||
|
@ -121,15 +128,22 @@ mod imp {
|
|||
|
||||
#[template_callback]
|
||||
async fn shuffle_all(&self) {
|
||||
self.obj().set_can_click_shuffle_all(false);
|
||||
self.playbin2.stop();
|
||||
let api = self.api2.borrow();
|
||||
let api = api.as_ref().unwrap();
|
||||
for song in api.get_random_songs(10).await.unwrap().into_iter() {
|
||||
println!("{song:?}");
|
||||
self.playbin2.push_entry(api.stream_url(&song.id));
|
||||
}
|
||||
self.obj().set_can_click_shuffle_all(true);
|
||||
/*
|
||||
this.can_click_shuffle_all = false;
|
||||
this.playbin.clear ();
|
||||
api.get_random_songs.begin (null, (song) => {
|
||||
this.playbin.append_track (song);
|
||||
}, (obj, res) => {
|
||||
try {
|
||||
api.get_random_songs.end (res);
|
||||
} catch (Error e) {
|
||||
error ("could not get random songs: %s", e.message);
|
||||
}
|
||||
this.can_click_shuffle_all = true;
|
||||
|
||||
this.playbin.select_track (0);
|
||||
});*/
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
|
@ -188,61 +202,39 @@ impl Window {
|
|||
pub fn new(app: &impl IsA<gtk::Application>) -> Self {
|
||||
let window: Self = glib::Object::builder().property("application", app).build();
|
||||
|
||||
// manual bidirectional sync
|
||||
window.imp().playbar.set_volume(window.imp().playbin2.volume() as i32);
|
||||
window.imp().playbin2.volume_changed().connect_object(
|
||||
&*window.imp().playbar,
|
||||
|playbin, playbar, ()| {
|
||||
playbar.set_volume(playbin.volume() as i32);
|
||||
true
|
||||
},
|
||||
);
|
||||
window.imp().playbar.connect_notify_local(
|
||||
Some("volume"),
|
||||
glib::clone!(
|
||||
#[weak(rename_to = playbin)]
|
||||
window.imp().playbin2,
|
||||
move |playbar, _| playbin.set_volume(playbar.volume() as i64)
|
||||
),
|
||||
);
|
||||
|
||||
window.imp().playbar.set_mute(window.imp().playbin2.muted());
|
||||
window.imp().playbin2.muted_changed().connect_object(
|
||||
&*window.imp().playbar,
|
||||
|playbin, playbar, ()| {
|
||||
playbar.set_mute(playbin.muted());
|
||||
true
|
||||
},
|
||||
);
|
||||
window.imp().playbar.connect_notify_local(
|
||||
Some("mute"),
|
||||
glib::clone!(
|
||||
#[weak(rename_to = playbin)]
|
||||
window.imp().playbin2,
|
||||
move |playbar, _| playbin.set_muted(playbar.mute())
|
||||
),
|
||||
);
|
||||
|
||||
window
|
||||
.imp()
|
||||
.setup
|
||||
.connected()
|
||||
.connect_object(&window, |_setup, window, api| {
|
||||
window.imp().api2.replace(Some(api));
|
||||
window.imp().playbin2.stop();
|
||||
window.set_can_click_shuffle_all(true);
|
||||
true
|
||||
});
|
||||
.playbin()
|
||||
.bind_property("volume", &*window.imp().playbar, "volume")
|
||||
.bidirectional()
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
window.imp().setup.connect_closure(
|
||||
"connected",
|
||||
false,
|
||||
glib::closure_local!(
|
||||
#[weak]
|
||||
window,
|
||||
move |_setup: crate::ui::Setup, api: crate::subsonic_vala::Client| {
|
||||
window.imp().api.replace(Some(api.clone()));
|
||||
window.playbin().set_api(&api);
|
||||
window.set_can_click_shuffle_all(true);
|
||||
}
|
||||
),
|
||||
);
|
||||
window.imp().setup.load();
|
||||
|
||||
window
|
||||
.imp()
|
||||
.playbin2
|
||||
.file_started()
|
||||
.connect_object(&window, |_playbin, _window, ()| {
|
||||
// TODO window.imp().now_playing(song);
|
||||
true
|
||||
});
|
||||
window.playbin().connect_closure(
|
||||
"new-track",
|
||||
false,
|
||||
glib::closure_local!(
|
||||
#[weak]
|
||||
window,
|
||||
move |_playbin: crate::Playbin, song: crate::playbin::Song| {
|
||||
window.imp().now_playing(&song);
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
window.playbin().connect_closure(
|
||||
"stopped",
|
||||
|
|
Loading…
Reference in a new issue