Compare commits

..

No commits in common. "371c966be86baa79747ed122de5a2d34edb906dd" and "1b8a59bef59f1283964ba97139dbf30de41b3087" have entirely different histories.

14 changed files with 441 additions and 36 deletions

View file

@ -128,6 +128,7 @@ template $AudreyUiWindow: Adw.ApplicationWindow {
[bottom] [bottom]
$AudreyUiPlaybar playbar { $AudreyUiPlaybar playbar {
song: bind template.song; song: bind template.song;
playbin: bind template.playbin;
playing_cover_art: bind template.playing_cover_art; playing_cover_art: bind template.playing_cover_art;
show_cover_art: bind $show_playbar_cover_art (stack.visible-child-name) as <bool>; show_cover_art: bind $show_playbar_cover_art (stack.visible-child-name) as <bool>;
} }

View file

@ -11,13 +11,13 @@ pub mod ui;
pub mod mpris; pub mod mpris;
pub use mpris::Mpris; pub use mpris::Mpris;
pub mod playbin_song; pub mod playbin;
pub use playbin_song::Song as PlaybinSong; pub use playbin::Playbin;
pub mod subsonic; pub mod subsonic;
pub mod subsonic_vala;
pub mod playbin2; pub mod playbin2;
pub type Playbin = playbin2::Playbin<PlaybinSong>;
mod signal; mod signal;
pub use signal::{Signal, SignalEmitter, SignalHandler}; pub use signal::{Signal, SignalEmitter, SignalHandler};

View file

@ -1,9 +1,11 @@
use crate::{Playbin, PlaybinSong};
use gtk::glib::spawn_future_local; use gtk::glib::spawn_future_local;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Value}; use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Value};
use crate::playbin::Song as PlaybinSong;
type Playbin = crate::playbin2::Playbin<PlaybinSong>;
const MICROSECONDS: f64 = 1e6; // in a second const MICROSECONDS: f64 = 1e6; // in a second
#[derive(Default)] #[derive(Default)]
@ -35,7 +37,7 @@ struct MetadataMap {
} }
impl MetadataMap { impl MetadataMap {
fn from_playbin_song(song: Option<&PlaybinSong>) -> Self { fn from_playbin_song(song: Option<&crate::playbin::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({

190
src/playbin.rs Normal file
View file

@ -0,0 +1,190 @@
pub mod song;
pub use song::Song;
mod state;
pub use state::State;
pub mod ffi {
use gtk::{gio, glib};
#[repr(C)]
pub struct AudreyPlaybin {
parent_instance: glib::gobject_ffi::GObject,
}
#[repr(C)]
pub struct AudreyPlaybinClass {
parent_class: glib::gobject_ffi::GObjectClass,
}
extern "C" {
pub fn audrey_playbin_get_type() -> glib::ffi::GType;
pub fn audrey_playbin_new() -> *mut AudreyPlaybin;
pub fn audrey_playbin_get_state(self_: *mut AudreyPlaybin) -> super::state::ffi::State;
pub fn audrey_playbin_pause(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_play(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_stop(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_clear(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_get_volume(self_: *mut AudreyPlaybin) -> std::ffi::c_int;
pub fn audrey_playbin_set_volume(self_: *mut AudreyPlaybin, volume: std::ffi::c_int);
pub fn audrey_playbin_set_api(
self_: *mut AudreyPlaybin,
api: *mut crate::subsonic_vala::client::ffi::AudreySubsonicClient,
);
pub fn audrey_playbin_seek(self_: *mut AudreyPlaybin, position: f64);
pub fn audrey_playbin_go_to_next_track(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_go_to_prev_track(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_get_position(self_: *mut AudreyPlaybin) -> f64;
pub fn audrey_playbin_get_duration(self_: *mut AudreyPlaybin) -> f64;
pub fn audrey_playbin_get_mute(self_: *mut AudreyPlaybin) -> glib::ffi::gboolean;
pub fn audrey_playbin_set_mute(self_: *mut AudreyPlaybin, mute: glib::ffi::gboolean);
pub fn audrey_playbin_get_play_queue_position(
self_: *mut AudreyPlaybin,
) -> 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(
self_: *mut AudreyPlaybin,
from: std::ffi::c_uint,
to: std::ffi::c_uint,
);
pub fn audrey_playbin_remove_track(self_: *mut AudreyPlaybin, position: std::ffi::c_uint);
pub fn audrey_playbin_select_track(self_: *mut AudreyPlaybin, position: std::ffi::c_uint);
pub fn audrey_playbin_get_play_queue(
self_: *mut AudreyPlaybin,
) -> *mut gio::ffi::GListModel;
}
}
use adw::prelude::*;
use glib::translate::{from_glib, from_glib_none, IntoGlib, ToGlibPtr};
use gtk::{gio, glib};
glib::wrapper! {
pub struct Playbin(Object<ffi::AudreyPlaybin, ffi::AudreyPlaybinClass>);
match fn {
type_ => || ffi::audrey_playbin_get_type(),
}
}
impl Default for Playbin {
fn default() -> Self {
unsafe { from_glib_none(ffi::audrey_playbin_new()) }
}
}
impl Playbin {
pub fn state(&self) -> State {
unsafe { glib::translate::from_glib(ffi::audrey_playbin_get_state(self.to_glib_none().0)) }
}
pub fn pause(&self) {
unsafe { ffi::audrey_playbin_pause(self.to_glib_none().0) }
}
pub fn play(&self) {
unsafe { ffi::audrey_playbin_play(self.to_glib_none().0) }
}
pub fn volume(&self) -> i32 {
unsafe { ffi::audrey_playbin_get_volume(self.to_glib_none().0) }
}
pub fn set_volume(&self, value: i32) {
unsafe { ffi::audrey_playbin_set_volume(self.to_glib_none().0, value) }
}
pub fn seek(&self, position: f64) {
unsafe { ffi::audrey_playbin_seek(self.to_glib_none().0, position) }
}
pub fn go_to_next_track(&self) {
unsafe { ffi::audrey_playbin_go_to_next_track(self.to_glib_none().0) }
}
pub fn go_to_prev_track(&self) {
unsafe { ffi::audrey_playbin_go_to_prev_track(self.to_glib_none().0) }
}
pub fn position(&self) -> f64 {
unsafe { ffi::audrey_playbin_get_position(self.to_glib_none().0) }
}
pub fn duration(&self) -> f64 {
unsafe { ffi::audrey_playbin_get_duration(self.to_glib_none().0) }
}
pub fn mute(&self) -> bool {
unsafe { from_glib(ffi::audrey_playbin_get_mute(self.to_glib_none().0)) }
}
pub fn set_mute(&self, mute: bool) {
unsafe { ffi::audrey_playbin_set_mute(self.to_glib_none().0, mute.into_glib()) }
}
pub fn play_queue_position(&self) -> u32 {
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) {
unsafe { ffi::audrey_playbin_move_track(self.to_glib_none().0, from, to) }
}
pub fn remove_track(&self, position: u32) {
unsafe { ffi::audrey_playbin_remove_track(self.to_glib_none().0, position) }
}
pub fn select_track(&self, position: u32) {
unsafe { ffi::audrey_playbin_select_track(self.to_glib_none().0, position) }
}
pub fn stop(&self) {
unsafe { ffi::audrey_playbin_stop(self.to_glib_none().0) }
}
pub fn clear(&self) {
unsafe { ffi::audrey_playbin_clear(self.to_glib_none().0) }
}
pub fn play_queue(&self) -> gio::ListModel {
unsafe { from_glib_none(ffi::audrey_playbin_get_play_queue(self.to_glib_none().0)) }
}
pub fn connect_new_track<F: Fn(&Self, &Song) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_closure(
"new-track",
false,
glib::closure_local!(|playbin, song| f(playbin, song)),
)
}
pub fn connect_seeked<F: Fn(&Self, f64) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_closure(
"seeked",
false,
glib::closure_local!(|playbin, position| f(playbin, position)),
)
}
// FIXME: this would be useful in other places, probably
pub fn connect_notify_future_local<
F: Fn(&Self, &glib::ParamSpec) -> U + 'static,
U: std::future::Future<Output = ()> + 'static,
>(
&self,
name: &str,
f: F,
) {
self.connect_notify_local(Some(name), move |self_, param_spec| {
glib::spawn_future_local(f(self_, param_spec));
});
}
pub fn set_api(&self, api: &crate::subsonic_vala::Client) {
unsafe { ffi::audrey_playbin_set_api(self.to_glib_none().0, api.to_glib_none().0) }
}
}

46
src/playbin/state.rs Normal file
View file

@ -0,0 +1,46 @@
pub mod ffi {
use gtk::glib;
pub type State = std::ffi::c_int;
extern "C" {
pub fn audrey_playbin_state_get_type() -> glib::ffi::GType;
}
}
use glib::prelude::*;
use gtk::glib;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum State {
Stopped,
Paused,
Playing,
}
impl glib::translate::FromGlib<ffi::State> for State {
unsafe fn from_glib(value: ffi::State) -> Self {
match value {
0 => Self::Stopped,
1 => Self::Paused,
2 => Self::Playing,
_ => unreachable!(),
}
}
}
unsafe impl<'a> glib::value::FromValue<'a> for State {
type Checker = glib::value::GenericValueTypeChecker<Self>;
unsafe fn from_value(value: &'a glib::Value) -> Self {
use glib::translate::ToGlibPtr;
glib::translate::from_glib(glib::gobject_ffi::g_value_get_enum(value.to_glib_none().0))
}
}
impl StaticType for State {
fn static_type() -> glib::Type {
unsafe { glib::translate::from_glib(ffi::audrey_playbin_state_get_type()) }
}
}

5
src/subsonic_vala.rs Normal file
View file

@ -0,0 +1,5 @@
pub mod client;
pub use client::Client;
mod song;
pub use song::Song;

View file

@ -0,0 +1,48 @@
pub mod ffi {
use gtk::glib;
use std::ffi::c_char;
#[repr(C)]
pub struct AudreySubsonicClient {
parent_instance: glib::gobject_ffi::GObject,
}
#[repr(C)]
pub struct AudreySubsonicClientClass {
parent_class: glib::gobject_ffi::GObjectClass,
}
extern "C" {
pub fn audrey_subsonic_client_get_type() -> glib::ffi::GType;
pub fn audrey_subsonic_client_new_with_token(
server_url: *mut c_char,
username: *mut c_char,
token: *mut c_char,
salt: *mut c_char,
) -> *mut AudreySubsonicClient;
}
}
use glib::translate::{from_glib_none, ToGlibPtr};
use gtk::glib;
glib::wrapper! {
pub struct Client(Object<ffi::AudreySubsonicClient, ffi::AudreySubsonicClientClass>);
match fn {
type_ => || ffi::audrey_subsonic_client_get_type(),
}
}
impl Client {
pub fn with_token(server_url: &str, username: &str, token: &str, salt: &str) -> Self {
unsafe {
from_glib_none(ffi::audrey_subsonic_client_new_with_token(
server_url.to_glib_none().0,
username.to_glib_none().0,
token.to_glib_none().0,
salt.to_glib_none().0,
))
}
}
}

39
src/subsonic_vala/song.rs Normal file
View file

@ -0,0 +1,39 @@
mod ffi {
use gtk::glib;
use std::ffi::*;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct AudreySubsonicSong {
pub id: *mut c_char,
pub title: *mut c_char,
pub album: *mut c_char,
pub artist: *mut c_char,
pub track: i64,
pub year: i64,
pub starred: *mut glib::ffi::GDateTime,
pub duration: i64,
pub play_count: i64,
pub genre: *mut c_char,
pub cover_art: *mut c_char,
}
extern "C" {
pub fn audrey_subsonic_song_copy(ptr: *const AudreySubsonicSong)
-> *mut AudreySubsonicSong;
pub fn audrey_subsonic_song_free(ptr: *mut AudreySubsonicSong);
pub fn audrey_subsonic_song_get_type() -> glib::ffi::GType;
}
}
use gtk::glib;
glib::wrapper! {
pub struct Song(BoxedInline<ffi::AudreySubsonicSong>);
match fn {
copy => |ptr| ffi::audrey_subsonic_song_copy(ptr),
free => |ptr| ffi::audrey_subsonic_song_free(ptr),
type_ => || ffi::audrey_subsonic_song_get_type(),
}
}

View file

@ -2,12 +2,14 @@ pub mod song;
pub use song::Song; pub use song::Song;
mod imp { mod imp {
use crate::{Playbin, PlaybinSong}; use crate::playbin::Song as PlaybinSong;
use adw::{gio, glib, prelude::*, subclass::prelude::*}; use adw::{gio, glib, prelude::*, subclass::prelude::*};
use glib::{subclass::InitializingObject, WeakRef}; use glib::{subclass::InitializingObject, WeakRef};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::Rc; use std::rc::Rc;
type Playbin = crate::playbin2::Playbin<PlaybinSong>;
#[derive(gtk::CompositeTemplate, glib::Properties, Default)] #[derive(gtk::CompositeTemplate, glib::Properties, Default)]
#[template(resource = "/eu/callcc/audrey/play_queue.ui")] #[template(resource = "/eu/callcc/audrey/play_queue.ui")]
#[properties(wrapper_type = super::PlayQueue)] #[properties(wrapper_type = super::PlayQueue)]
@ -76,7 +78,10 @@ mod imp {
child.bind( child.bind(
item.position(), item.position(),
item.item().unwrap().downcast_ref::<PlaybinSong>().unwrap(), item.item()
.unwrap()
.downcast_ref::<crate::playbin::Song>()
.unwrap(),
); );
} }
@ -104,11 +109,13 @@ mod imp {
} }
} }
use crate::Playbin; use crate::playbin::Song as PlaybinSong;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gtk::glib; use gtk::glib;
use std::rc::Rc; use std::rc::Rc;
type Playbin = crate::playbin2::Playbin<PlaybinSong>;
glib::wrapper! { glib::wrapper! {
pub struct PlayQueue(ObjectSubclass<imp::PlayQueue>) pub struct PlayQueue(ObjectSubclass<imp::PlayQueue>)
@extends adw::Bin, gtk::Widget, @extends adw::Bin, gtk::Widget,

View file

@ -1,11 +1,13 @@
mod imp { mod imp {
use crate::playbin::Song as PlaybinSong;
use crate::signal::SignalHandler; use crate::signal::SignalHandler;
use crate::{Playbin, PlaybinSong};
use glib::{subclass::InitializingObject, WeakRef}; use glib::{subclass::InitializingObject, WeakRef};
use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*}; use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::Rc; use std::rc::Rc;
type Playbin = crate::playbin2::Playbin<PlaybinSong>;
#[derive(gtk::CompositeTemplate, glib::Properties, Default)] #[derive(gtk::CompositeTemplate, glib::Properties, Default)]
#[template(resource = "/eu/callcc/audrey/play_queue_song.ui")] #[template(resource = "/eu/callcc/audrey/play_queue_song.ui")]
#[properties(wrapper_type = super::Song)] #[properties(wrapper_type = super::Song)]
@ -165,13 +167,15 @@ mod imp {
} }
} }
use crate::{Playbin, PlaybinSong}; use crate::playbin::Song as PlaybinSong;
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use glib::Object; use glib::Object;
use gtk::glib; use gtk::glib;
use std::rc::Rc; use std::rc::Rc;
type Playbin = crate::playbin2::Playbin<PlaybinSong>;
glib::wrapper! { glib::wrapper! {
pub struct Song(ObjectSubclass<imp::Song>) pub struct Song(ObjectSubclass<imp::Song>)
@extends gtk::Box, gtk::Widget, @extends gtk::Box, gtk::Widget,
@ -195,7 +199,7 @@ impl Song {
Rc::clone(self.imp().playbin.borrow().as_ref().unwrap()) Rc::clone(self.imp().playbin.borrow().as_ref().unwrap())
} }
pub fn bind(&self, position: u32, song: &PlaybinSong) { pub fn bind(&self, position: u32, song: &crate::playbin::Song) {
self.set_displayed_position(position + 1); self.set_displayed_position(position + 1);
self.set_song(song); self.set_song(song);
self.set_current(self.playbin().current_entry() == Some(position as usize)); self.set_current(self.playbin().current_entry() == Some(position as usize));

View file

@ -1,5 +1,4 @@
mod imp { mod imp {
use crate::PlaybinSong;
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use glib::subclass::InitializingObject; use glib::subclass::InitializingObject;
@ -12,7 +11,7 @@ mod imp {
#[template(resource = "/eu/callcc/audrey/playbar.ui")] #[template(resource = "/eu/callcc/audrey/playbar.ui")]
pub struct Playbar { pub struct Playbar {
#[property(get, set)] #[property(get, set)]
song: RefCell<Option<PlaybinSong>>, song: RefCell<Option<crate::playbin::Song>>,
#[property(get, set)] #[property(get, set)]
playing_cover_art: RefCell<Option<gdk::Paintable>>, playing_cover_art: RefCell<Option<gdk::Paintable>>,
#[property(get, set, default = true)] #[property(get, set, default = true)]
@ -54,17 +53,17 @@ mod imp {
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl Playbar { impl Playbar {
#[template_callback] #[template_callback]
fn song_title(&self, song: Option<&PlaybinSong>) -> Option<GString> { fn song_title(&self, song: Option<&crate::playbin::Song>) -> Option<GString> {
song.map(|song| song.title()) song.map(|song| song.title())
} }
#[template_callback] #[template_callback]
fn song_artist(&self, song: Option<&PlaybinSong>) -> Option<GString> { fn song_artist(&self, song: Option<&crate::playbin::Song>) -> Option<GString> {
song.map(|song| song.artist()) song.map(|song| song.artist())
} }
#[template_callback] #[template_callback]
fn song_album(&self, song: Option<&PlaybinSong>) -> Option<GString> { fn song_album(&self, song: Option<&crate::playbin::Song>) -> Option<GString> {
song.map(|song| song.album()) song.map(|song| song.album())
} }
@ -73,6 +72,25 @@ mod imp {
gformat!("{:02}:{:02}", (s as i64) / 64, (s as i64) % 60) gformat!("{:02}:{:02}", (s as i64) / 64, (s as i64) % 60)
} }
#[template_callback]
fn playbin_active(&self, state: crate::playbin::State) -> bool {
true // TODO
}
#[template_callback]
fn can_press_play(&self, state: crate::playbin::State, n_items: u32) -> bool {
true // TODO
}
#[template_callback]
fn play_pause_icon_name(&self, state: crate::playbin::State) -> &'static str {
/*
match state {
crate::playbin::State::Playing => "media-playback-pause",
_ => "media-playback-start",
}*/todo!()
}
#[template_callback] #[template_callback]
fn mute_button_icon_name(&self, mute: bool) -> &'static str { fn mute_button_icon_name(&self, mute: bool) -> &'static str {
if mute { if mute {
@ -94,24 +112,21 @@ mod imp {
if range.adjustment().lower() < range.adjustment().upper() { if range.adjustment().lower() < range.adjustment().upper() {
playbin.seek(value); playbin.seek(value);
} }
false*/ false*/todo!()
todo!()
} }
#[template_callback] #[template_callback]
fn on_skip_forward_clicked(&self) { fn on_skip_forward_clicked(&self) {
/* /*
let playbin = self.playbin.upgrade().unwrap(); let playbin = self.playbin.upgrade().unwrap();
playbin.go_to_next_track();*/ playbin.go_to_next_track();*/todo!()
todo!()
} }
#[template_callback] #[template_callback]
fn on_skip_backward_clicked(&self) { fn on_skip_backward_clicked(&self) {
/* /*
let playbin = self.playbin.upgrade().unwrap(); let playbin = self.playbin.upgrade().unwrap();
playbin.go_to_prev_track();*/ playbin.go_to_prev_track();*/todo!()
todo!()
} }
#[template_callback] #[template_callback]
@ -123,8 +138,7 @@ mod imp {
if new_position < 0.0 { if new_position < 0.0 {
new_position = 0.0; new_position = 0.0;
} }
playbin.seek(new_position);*/ playbin.seek(new_position);*/todo!()
todo!()
} }
#[template_callback] #[template_callback]
@ -136,8 +150,7 @@ mod imp {
if new_position > playbin.duration() { if new_position > playbin.duration() {
new_position = playbin.duration(); new_position = playbin.duration();
} }
playbin.seek(new_position);*/ playbin.seek(new_position);*/todo!()
todo!()
} }
#[template_callback] #[template_callback]
@ -149,8 +162,7 @@ mod imp {
playbin.pause(); playbin.pause();
} else { } else {
playbin.play(); playbin.play();
}*/ }*/todo!()
todo!()
} }
#[template_callback] #[template_callback]

View file

@ -45,7 +45,16 @@ mod imp {
} }
#[glib::derived_properties] #[glib::derived_properties]
impl ObjectImpl for Setup {} impl ObjectImpl for Setup {
fn signals() -> &'static [Signal] {
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
SIGNALS.get_or_init(|| {
vec![Signal::builder("connected")
.param_types([crate::subsonic_vala::Client::static_type()])
.build()]
})
}
}
impl WidgetImpl for Setup {} impl WidgetImpl for Setup {}
@ -112,6 +121,34 @@ mod imp {
.await .await
.unwrap(); .unwrap();
// please REMOVEME once we've killed vala
fn get_random_salt(length: usize) -> String {
use rand::Rng;
let mut rng = rand::thread_rng();
std::iter::repeat(())
// 0.9: s/distributions/distr
.map(|()| rng.sample(rand::distributions::Alphanumeric))
.map(char::from)
.take(length)
.collect::<String>()
}
let new_salt = get_random_salt(8);
use md5::Digest;
let mut hasher = md5::Md5::new();
hasher.update(self.obj().password().as_bytes());
hasher.update(new_salt.as_bytes());
let new_token_bytes = hasher.finalize();
let new_token = base16ct::lower::encode_string(&new_token_bytes);
let vala_api = crate::subsonic_vala::Client::with_token(
&self.obj().server_url(),
&self.obj().username(),
&new_token,
&new_salt,
);
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)); self.connected.emit(self.obj().as_ref(), Rc::new(api));
} }
} }

View file

@ -1,5 +1,4 @@
mod imp { mod imp {
use crate::{Playbin, PlaybinSong};
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use glib::subclass::InitializingObject; use glib::subclass::InitializingObject;
@ -7,7 +6,7 @@ mod imp {
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::Rc; use std::rc::Rc;
impl crate::playbin2::PlaybinEntry for PlaybinSong { impl crate::playbin2::PlaybinEntry for crate::playbin::Song {
fn url(&self) -> url::Url { fn url(&self) -> url::Url {
self.stream_url() self.stream_url()
} }
@ -23,6 +22,9 @@ mod imp {
#[template_child] #[template_child]
pub(super) play_queue: TemplateChild<crate::ui::PlayQueue>, pub(super) play_queue: TemplateChild<crate::ui::PlayQueue>,
#[property(get, set)]
playbin: RefCell<crate::Playbin>,
#[property(get, set, default = false)] #[property(get, set, default = false)]
can_click_shuffle_all: Cell<bool>, can_click_shuffle_all: Cell<bool>,
@ -30,11 +32,11 @@ mod imp {
playing_cover_art: RefCell<Option<gdk::Paintable>>, playing_cover_art: RefCell<Option<gdk::Paintable>>,
#[property(get, set, nullable)] #[property(get, set, nullable)]
song: RefCell<Option<PlaybinSong>>, song: RefCell<Option<crate::playbin::Song>>,
pub(super) setup: crate::ui::Setup, pub(super) setup: crate::ui::Setup,
pub(super) playbin2: Rc<Playbin>, pub(super) playbin2: Rc<crate::playbin2::Playbin<crate::playbin::Song>>,
pub(super) api2: RefCell<Option<Rc<crate::subsonic::Client>>>, pub(super) api2: RefCell<Option<Rc<crate::subsonic::Client>>>,
} }
@ -136,7 +138,7 @@ mod imp {
let api = api.as_ref().unwrap(); let api = api.as_ref().unwrap();
for song in api.get_random_songs(10).await.unwrap().into_iter() { for song in api.get_random_songs(10).await.unwrap().into_iter() {
self.playbin2 self.playbin2
.push_entry(PlaybinSong::from_child(api, &song)); .push_entry(crate::playbin::Song::from_child(api, &song));
} }
self.obj().set_can_click_shuffle_all(true); self.obj().set_can_click_shuffle_all(true);
} }
@ -146,7 +148,7 @@ mod imp {
self.setup.present(Some(self.obj().as_ref())); self.setup.present(Some(self.obj().as_ref()));
} }
pub(super) fn now_playing(&self, _song: PlaybinSong) { pub(super) fn now_playing(&self, _song: &crate::playbin::Song) {
/* /*
this.song = song; this.song = song;
// api.scrobble.begin (this.song.id); TODO // api.scrobble.begin (this.song.id); TODO
@ -183,7 +185,6 @@ mod imp {
} }
} }
use crate::PlaybinSong;
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gtk::{gdk, gio, glib}; use gtk::{gdk, gio, glib};
@ -285,6 +286,19 @@ impl Window {
true true
}); });
window.playbin().connect_closure(
"stopped",
false,
glib::closure_local!(
#[weak]
window,
move |_playbin: crate::Playbin| {
window.set_playing_cover_art(None::<gdk::Paintable>);
window.set_song(None::<crate::playbin::Song>);
}
),
);
window window
} }
} }