use crate::playlist_item::PlaylistItem; use fragile::Fragile; use gstreamer_player::gst; use gtk::{ gio, glib::{self, Properties}, prelude::*, subclass::prelude::*, }; use std::cell::{Cell, OnceCell}; mod imp { use super::*; #[derive(Properties, Debug, Default)] #[properties(wrapper_type = super::MusicusPlayer)] pub struct MusicusPlayer { #[property(get, set)] pub active: Cell, #[property(get, set)] pub playing: Cell, #[property(get, construct_only)] pub playlist: OnceCell, #[property(get, set = Self::set_current_index)] pub current_index: Cell, #[property(get, set)] pub duration_ms: Cell, #[property(get, set)] pub current_time_ms: Cell, #[property(get, set = Self::set_position)] pub position: Cell, #[property(get, construct_only)] pub player: OnceCell, } impl MusicusPlayer { pub fn set_current_index(&self, index: u32) { let playlist = self.playlist.get().unwrap(); if let Some(item) = playlist.item(index) { if let Some(old_item) = playlist.item(self.current_index.get()) { old_item.downcast::() .unwrap() .set_is_playing(false); } let item = item.downcast::().unwrap(); let uri = glib::filename_to_uri(&item.path(), None) .expect("track path should be parsable as an URI"); let player = self.player.get().unwrap(); player.set_uri(Some(&uri)); if self.playing.get() { player.play(); } self.current_index.set(index); item.set_is_playing(true); } } pub fn set_position(&self, position: f64) { self.player .get() .unwrap() .seek(gst::ClockTime::from_mseconds( (position * self.duration_ms.get() as f64) as u64, )); } } #[glib::object_subclass] impl ObjectSubclass for MusicusPlayer { const NAME: &'static str = "MusicusPlayer"; type Type = super::MusicusPlayer; } #[glib::derived_properties] impl ObjectImpl for MusicusPlayer { fn constructed(&self) { self.parent_constructed(); let player = self.player.get().unwrap(); let mut config = player.config(); config.set_position_update_interval(250); player.set_config(config).unwrap(); player.set_video_track_enabled(false); let obj = Fragile::new(self.obj().to_owned()); player.connect_end_of_stream(move |_| { obj.get().next(); }); let obj = Fragile::new(self.obj().to_owned()); player.connect_position_updated(move |_, current_time| { if let Some(current_time) = current_time { let obj = obj.get(); let imp = obj.imp(); let current_time_ms = current_time.mseconds(); let duration_ms = imp.duration_ms.get(); let mut position = current_time_ms as f64 / duration_ms as f64; if position > 1.0 { position = 1.0 } imp.current_time_ms.set(current_time_ms); obj.notify_current_time_ms(); imp.position.set(position); obj.notify_position(); } }); let obj = Fragile::new(self.obj().to_owned()); player.connect_duration_changed(move |_, duration| { if let Some(duration) = duration { let obj = obj.get(); let imp = obj.imp(); let duration_ms = duration.mseconds(); imp.duration_ms.set(duration_ms); obj.notify_duration_ms(); imp.current_time_ms.set(0); obj.notify_current_time_ms(); imp.position.set(0.0); obj.notify_position(); } }); } } } glib::wrapper! { pub struct MusicusPlayer(ObjectSubclass); } impl MusicusPlayer { pub fn new() -> Self { let player = gstreamer_player::Player::new( None::, Some(gstreamer_player::PlayerGMainContextSignalDispatcher::new( None, )), ); glib::Object::builder() .property("active", false) .property("playing", false) .property("playlist", gio::ListStore::new::()) .property("current-index", 0u32) .property("current-time-ms", 0u64) .property("duration-ms", 60_000u64) .property("position", 0.0) .property("player", player) .build() } pub fn append(&self, tracks: Vec) { let playlist = self.playlist(); for track in tracks { playlist.append(&track); } if !self.active() && playlist.n_items() > 0 { self.set_active(true); self.set_current_index(0); self.play(); } } pub fn play(&self) { self.player().play(); self.set_playing(true); } pub fn pause(&self) { self.player().pause(); self.set_playing(false); } pub fn current_item(&self) -> Option { let imp = self.imp(); imp.playlist .get() .unwrap() .item(imp.current_index.get()) .and_downcast::() } pub fn next(&self) { if self.current_index() < self.playlist().n_items() - 1 { self.set_current_index(self.current_index() + 1); } } pub fn previous(&self) { if self.current_index() > 0 { self.set_current_index(self.current_index() - 1); } } } impl Default for MusicusPlayer { fn default() -> Self { Self::new() } }