2024-11-02 11:24:25 +00:00
|
|
|
mod imp {
|
2024-11-03 18:36:52 +00:00
|
|
|
use crate::{Playbin, PlaybinSong};
|
2024-11-02 11:24:25 +00:00
|
|
|
use adw::prelude::*;
|
|
|
|
use adw::subclass::prelude::*;
|
|
|
|
use glib::subclass::InitializingObject;
|
|
|
|
use gtk::{gdk, glib};
|
|
|
|
use std::cell::{Cell, RefCell};
|
2024-11-03 09:37:37 +00:00
|
|
|
use std::rc::Rc;
|
2024-11-04 09:12:13 +00:00
|
|
|
use tracing::{event, Level};
|
2024-10-29 13:02:29 +00:00
|
|
|
|
2024-11-04 09:40:51 +00:00
|
|
|
#[derive(gtk::CompositeTemplate, glib::Properties)]
|
2024-11-02 11:24:25 +00:00
|
|
|
#[template(resource = "/eu/callcc/audrey/window.ui")]
|
|
|
|
#[properties(wrapper_type = super::Window)]
|
|
|
|
pub struct Window {
|
|
|
|
#[template_child]
|
|
|
|
pub(super) playbar: TemplateChild<crate::ui::Playbar>,
|
|
|
|
|
2024-11-03 17:45:52 +00:00
|
|
|
#[template_child]
|
|
|
|
pub(super) play_queue: TemplateChild<crate::ui::PlayQueue>,
|
|
|
|
|
2024-11-02 11:24:25 +00:00
|
|
|
#[property(get, set, default = false)]
|
|
|
|
can_click_shuffle_all: Cell<bool>,
|
|
|
|
|
|
|
|
#[property(get, set, nullable)]
|
|
|
|
playing_cover_art: RefCell<Option<gdk::Paintable>>,
|
|
|
|
|
|
|
|
#[property(get, set, nullable)]
|
2024-11-03 18:36:52 +00:00
|
|
|
song: RefCell<Option<PlaybinSong>>,
|
2024-11-02 11:24:25 +00:00
|
|
|
|
|
|
|
pub(super) setup: crate::ui::Setup,
|
2024-11-03 12:41:02 +00:00
|
|
|
|
2024-11-04 07:03:59 +00:00
|
|
|
pub(super) playbin: Rc<Playbin>,
|
|
|
|
pub(super) api: RefCell<Option<Rc<crate::subsonic::Client>>>,
|
2024-11-04 09:40:51 +00:00
|
|
|
|
2024-11-04 09:49:42 +00:00
|
|
|
pub(super) _sender: async_broadcast::Sender<crate::Event>,
|
2024-11-04 09:40:51 +00:00
|
|
|
pub(super) inactive_receiver: async_broadcast::InactiveReceiver<crate::Event>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Window {
|
|
|
|
fn default() -> Self {
|
|
|
|
let (sender, receiver) = async_broadcast::broadcast(100); // TODO: constantize
|
|
|
|
|
|
|
|
Self {
|
|
|
|
playbar: Default::default(),
|
|
|
|
play_queue: Default::default(),
|
|
|
|
can_click_shuffle_all: Cell::new(false),
|
|
|
|
playing_cover_art: Default::default(),
|
|
|
|
song: Default::default(),
|
|
|
|
setup: Default::default(),
|
|
|
|
playbin: Rc::new(Playbin::new(sender.clone())),
|
|
|
|
api: Default::default(),
|
2024-11-04 09:49:42 +00:00
|
|
|
_sender: sender,
|
2024-11-04 09:40:51 +00:00
|
|
|
inactive_receiver: receiver.deactivate(),
|
|
|
|
}
|
|
|
|
}
|
2024-10-29 13:02:29 +00:00
|
|
|
}
|
|
|
|
|
2024-11-02 11:24:25 +00:00
|
|
|
#[glib::object_subclass]
|
|
|
|
impl ObjectSubclass for Window {
|
|
|
|
const NAME: &'static str = "AudreyUiWindow";
|
|
|
|
type Type = super::Window;
|
|
|
|
type ParentType = adw::ApplicationWindow;
|
|
|
|
|
|
|
|
fn class_init(klass: &mut Self::Class) {
|
|
|
|
klass.bind_template();
|
|
|
|
klass.bind_template_callbacks();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn instance_init(obj: &InitializingObject<Self>) {
|
|
|
|
obj.init_template();
|
|
|
|
}
|
2024-10-29 13:02:29 +00:00
|
|
|
}
|
|
|
|
|
2024-11-02 11:24:25 +00:00
|
|
|
#[glib::derived_properties]
|
2024-11-03 09:37:37 +00:00
|
|
|
impl ObjectImpl for Window {
|
|
|
|
fn constructed(&self) {
|
|
|
|
self.parent_constructed();
|
|
|
|
|
2024-11-04 09:40:51 +00:00
|
|
|
crate::event::spawn_object_listener(
|
|
|
|
self.inactive_receiver.activate_cloned(),
|
|
|
|
self.obj().as_ref(),
|
|
|
|
|window, event| {
|
|
|
|
use crate::Event;
|
|
|
|
match dbg!(event) {
|
|
|
|
Event::PlaybinVolumeChanged => {
|
|
|
|
window
|
|
|
|
.imp()
|
|
|
|
.playbar
|
|
|
|
.set_volume(window.playbin().volume() as i32);
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::PlaybinMutedChanged => {
|
|
|
|
window.imp().playbar.set_mute(window.playbin().muted());
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::PlaybinEntryInserted(index) => {
|
|
|
|
window
|
|
|
|
.imp()
|
|
|
|
.play_queue
|
|
|
|
.model()
|
|
|
|
.unwrap()
|
|
|
|
.insert(index as u32, &window.playbin().entries()[index]);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2024-11-04 07:03:59 +00:00
|
|
|
let playbin = Rc::downgrade(&self.playbin);
|
2024-11-03 12:41:02 +00:00
|
|
|
glib::spawn_future_local(glib::clone!(async move {
|
2024-11-03 11:51:04 +00:00
|
|
|
loop {
|
2024-11-03 12:41:02 +00:00
|
|
|
match playbin.upgrade() {
|
2024-11-03 09:37:37 +00:00
|
|
|
None => break,
|
2024-11-03 12:41:02 +00:00
|
|
|
Some(playbin) => {
|
|
|
|
let listener = playbin.tick();
|
|
|
|
drop(playbin);
|
|
|
|
listener.await;
|
2024-11-03 09:37:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-03 12:41:02 +00:00
|
|
|
}));
|
2024-11-03 09:37:37 +00:00
|
|
|
|
2024-11-04 07:03:59 +00:00
|
|
|
self.play_queue.set_playbin(&self.playbin);
|
2024-11-03 17:45:52 +00:00
|
|
|
|
2024-11-03 09:37:37 +00:00
|
|
|
// set up mpris
|
|
|
|
let window = self.obj().clone();
|
|
|
|
glib::spawn_future_local(async move {
|
|
|
|
let conn = zbus::connection::Builder::session()
|
|
|
|
.expect("could not connect to the session bus")
|
|
|
|
.internal_executor(false)
|
|
|
|
.build()
|
|
|
|
.await
|
|
|
|
.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)
|
|
|
|
.await
|
|
|
|
.expect("could not serve mpris");
|
2024-11-04 07:03:59 +00:00
|
|
|
crate::mpris::Player::setup(conn.object_server(), &window.imp().playbin)
|
2024-11-03 09:37:37 +00:00
|
|
|
.await
|
|
|
|
.expect("could not serve mpris player");
|
|
|
|
|
|
|
|
drop(window); // don't keep this alive
|
|
|
|
|
|
|
|
// always set up handlers before requesting service name
|
|
|
|
conn.request_name("org.mpris.MediaPlayer2.audrey")
|
|
|
|
.await
|
|
|
|
.expect("could not register name in session bus");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2024-11-02 11:24:25 +00:00
|
|
|
|
|
|
|
impl WidgetImpl for Window {}
|
|
|
|
|
|
|
|
impl WindowImpl for Window {}
|
|
|
|
|
|
|
|
impl ApplicationWindowImpl for Window {}
|
|
|
|
|
|
|
|
impl AdwApplicationWindowImpl for Window {}
|
|
|
|
|
|
|
|
#[gtk::template_callbacks]
|
|
|
|
impl Window {
|
|
|
|
#[template_callback]
|
|
|
|
fn show_playbar_cover_art(&self, stack_child: Option<&str>) -> bool {
|
|
|
|
stack_child != Some("play-queue")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
async fn shuffle_all(&self) {
|
2024-11-03 15:11:25 +00:00
|
|
|
self.obj().set_can_click_shuffle_all(false);
|
2024-11-04 07:03:59 +00:00
|
|
|
self.playbin.stop();
|
|
|
|
let api = self.api.borrow();
|
2024-11-03 15:11:25 +00:00
|
|
|
let api = api.as_ref().unwrap();
|
|
|
|
for song in api.get_random_songs(10).await.unwrap().into_iter() {
|
2024-11-04 07:03:59 +00:00
|
|
|
self.playbin.push_entry(PlaybinSong::from_child(api, &song));
|
2024-11-03 15:11:25 +00:00
|
|
|
}
|
|
|
|
self.obj().set_can_click_shuffle_all(true);
|
2024-11-02 11:24:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn show_setup_dialog(&self) {
|
|
|
|
self.setup.present(Some(self.obj().as_ref()));
|
|
|
|
}
|
|
|
|
|
2024-11-03 19:18:12 +00:00
|
|
|
pub(super) fn now_playing(&self, _song: &PlaybinSong) {
|
2024-11-02 11:24:25 +00:00
|
|
|
/*
|
|
|
|
this.song = song;
|
|
|
|
// api.scrobble.begin (this.song.id); TODO
|
|
|
|
|
|
|
|
if (this.cancel_loading_art != null) {
|
|
|
|
this.cancel_loading_art.cancel ();
|
|
|
|
}
|
|
|
|
this.cancel_loading_art = new GLib.Cancellable ();
|
|
|
|
|
|
|
|
this.playing_cover_art = null; // TODO: preload next art somehow
|
|
|
|
this.cover_art_loading = true;
|
|
|
|
|
|
|
|
string song_id = this.song.id;
|
|
|
|
this.api.cover_art.begin (song_id, -1, Priority.DEFAULT, this.cancel_loading_art, (obj, res) => {
|
|
|
|
try {
|
|
|
|
this.playing_cover_art = Gdk.Texture.for_pixbuf (this.api.cover_art.end (res));
|
|
|
|
this.cover_art_loading = false;
|
|
|
|
} catch (Error e) {
|
|
|
|
if (!(e is IOError.CANCELLED)) {
|
|
|
|
warning ("could not load cover for %s: %s", song_id, e.message);
|
|
|
|
this.cover_art_loading = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
*/
|
|
|
|
todo!()
|
|
|
|
}
|
2024-11-03 09:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Window {
|
|
|
|
fn drop(&mut self) {
|
2024-11-04 09:12:13 +00:00
|
|
|
event!(Level::DEBUG, "dropping AudreyUiWindow");
|
2024-11-03 09:37:37 +00:00
|
|
|
}
|
2024-10-29 13:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
use adw::prelude::*;
|
2024-11-02 11:24:25 +00:00
|
|
|
use adw::subclass::prelude::*;
|
2024-11-03 19:18:12 +00:00
|
|
|
use gtk::{gio, glib};
|
2024-11-03 18:25:32 +00:00
|
|
|
use std::rc::Rc;
|
2024-10-29 13:02:29 +00:00
|
|
|
|
|
|
|
glib::wrapper! {
|
2024-11-02 11:24:25 +00:00
|
|
|
pub struct Window(ObjectSubclass<imp::Window>)
|
2024-10-29 13:02:29 +00:00
|
|
|
@extends adw::ApplicationWindow, gtk::ApplicationWindow, gtk::Window, gtk::Widget,
|
|
|
|
@implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Window {
|
|
|
|
pub fn new(app: &impl IsA<gtk::Application>) -> Self {
|
2024-11-02 11:24:25 +00:00
|
|
|
let window: Self = glib::Object::builder().property("application", app).build();
|
|
|
|
|
2024-11-03 15:11:25 +00:00
|
|
|
// manual bidirectional sync
|
2024-11-03 15:27:47 +00:00
|
|
|
window
|
|
|
|
.imp()
|
|
|
|
.playbar
|
2024-11-04 07:03:59 +00:00
|
|
|
.set_volume(window.imp().playbin.volume() as i32);
|
2024-11-03 15:11:25 +00:00
|
|
|
window.imp().playbar.connect_notify_local(
|
|
|
|
Some("volume"),
|
|
|
|
glib::clone!(
|
|
|
|
#[weak(rename_to = playbin)]
|
2024-11-04 07:03:59 +00:00
|
|
|
window.imp().playbin,
|
2024-11-03 15:11:25 +00:00
|
|
|
move |playbar, _| playbin.set_volume(playbar.volume() as i64)
|
2024-11-02 11:24:25 +00:00
|
|
|
),
|
|
|
|
);
|
2024-11-03 15:11:25 +00:00
|
|
|
|
2024-11-04 07:03:59 +00:00
|
|
|
window.imp().playbar.set_mute(window.imp().playbin.muted());
|
2024-11-03 15:17:54 +00:00
|
|
|
window.imp().playbar.connect_notify_local(
|
|
|
|
Some("mute"),
|
|
|
|
glib::clone!(
|
|
|
|
#[weak(rename_to = playbin)]
|
2024-11-04 07:03:59 +00:00
|
|
|
window.imp().playbin,
|
2024-11-03 15:17:54 +00:00
|
|
|
move |playbar, _| playbin.set_muted(playbar.mute())
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2024-11-04 07:03:59 +00:00
|
|
|
window.imp().playbin.file_started().connect_object(
|
2024-11-03 18:25:32 +00:00
|
|
|
&*window.imp().playbar,
|
|
|
|
|playbin, playbar, ()| {
|
|
|
|
let entry = &playbin.entries()[playbin.current_entry().unwrap()];
|
|
|
|
playbar.set_duration(entry.duration() as f64);
|
|
|
|
true
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
// update position every 100 ms
|
|
|
|
glib::source::timeout_add_local(std::time::Duration::from_millis(100), {
|
|
|
|
let playbar = window.imp().playbar.downgrade();
|
2024-11-04 07:03:59 +00:00
|
|
|
let playbin = Rc::downgrade(&window.imp().playbin);
|
2024-11-03 18:25:32 +00:00
|
|
|
move || {
|
|
|
|
let playbar = match playbar.upgrade() {
|
|
|
|
None => return glib::ControlFlow::Break,
|
|
|
|
Some(playbar) => playbar,
|
|
|
|
};
|
|
|
|
let playbin = match playbin.upgrade() {
|
|
|
|
None => return glib::ControlFlow::Break,
|
|
|
|
Some(playbin) => playbin,
|
|
|
|
};
|
|
|
|
playbar.set_position(playbin.position().unwrap_or(0.0));
|
|
|
|
glib::ControlFlow::Continue
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-11-03 15:11:25 +00:00
|
|
|
window
|
|
|
|
.imp()
|
|
|
|
.setup
|
|
|
|
.connected()
|
|
|
|
.connect_object(&window, |_setup, window, api| {
|
2024-11-04 07:03:59 +00:00
|
|
|
window.imp().api.replace(Some(api));
|
|
|
|
window.imp().playbin.stop();
|
2024-11-03 15:11:25 +00:00
|
|
|
window.set_can_click_shuffle_all(true);
|
|
|
|
true
|
|
|
|
});
|
2024-11-02 11:24:25 +00:00
|
|
|
window.imp().setup.load();
|
|
|
|
|
2024-11-03 15:11:25 +00:00
|
|
|
window
|
|
|
|
.imp()
|
2024-11-04 07:03:59 +00:00
|
|
|
.playbin
|
2024-11-03 15:11:25 +00:00
|
|
|
.file_started()
|
2024-11-03 19:18:12 +00:00
|
|
|
.connect_object(&window, |playbin, window, ()| {
|
|
|
|
window
|
|
|
|
.imp()
|
|
|
|
.now_playing(&playbin.entries()[playbin.current_entry().unwrap()]);
|
2024-11-03 15:11:25 +00:00
|
|
|
true
|
|
|
|
});
|
2024-11-02 11:24:25 +00:00
|
|
|
|
|
|
|
window
|
2024-11-01 12:50:29 +00:00
|
|
|
}
|
2024-11-04 09:40:51 +00:00
|
|
|
|
|
|
|
pub fn playbin(&self) -> &crate::Playbin {
|
|
|
|
&self.imp().playbin
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn receiver(&self) -> async_broadcast::Receiver<crate::Event> {
|
|
|
|
self.imp().inactive_receiver.activate_cloned()
|
|
|
|
}
|
2024-10-29 13:02:29 +00:00
|
|
|
}
|