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