musicus/src/player.rs

262 lines
7.8 KiB
Rust
Raw Normal View History

2023-10-25 17:45:32 +02:00
use crate::playlist_item::PlaylistItem;
2023-11-03 17:48:27 +01:00
use fragile::Fragile;
use gstreamer_player::gst;
use gtk::{
gio,
2023-11-03 18:59:47 +01:00
glib::{self, clone, subclass::Signal, Properties},
2023-11-03 17:48:27 +01:00
prelude::*,
subclass::prelude::*,
};
2023-11-03 18:20:41 +01:00
use mpris_player::{MprisPlayer, PlaybackStatus};
2023-11-03 18:59:47 +01:00
use once_cell::sync::Lazy;
2023-11-03 18:20:41 +01:00
use std::{
cell::{Cell, OnceCell},
sync::Arc,
};
2023-09-29 21:18:28 +02:00
mod imp {
2023-11-03 18:20:41 +01:00
use mpris_player::Metadata;
2023-09-29 21:18:28 +02:00
use super::*;
#[derive(Properties, Debug, Default)]
#[properties(wrapper_type = super::MusicusPlayer)]
pub struct MusicusPlayer {
#[property(get, set)]
pub active: Cell<bool>,
#[property(get, set)]
pub playing: Cell<bool>,
2023-10-25 17:45:32 +02:00
#[property(get, construct_only)]
pub playlist: OnceCell<gio::ListStore>,
2023-10-26 11:48:42 +02:00
#[property(get, set = Self::set_current_index)]
2023-10-25 17:45:32 +02:00
pub current_index: Cell<u32>,
2023-11-03 16:22:58 +01:00
#[property(get, set)]
2023-11-03 17:48:27 +01:00
pub duration_ms: Cell<u64>,
2023-11-03 16:22:58 +01:00
#[property(get, set)]
2023-11-07 15:57:56 +01:00
pub position_ms: Cell<u64>,
2023-11-03 17:48:27 +01:00
#[property(get, construct_only)]
pub player: OnceCell<gstreamer_player::Player>,
2023-11-03 18:20:41 +01:00
pub mpris: OnceCell<Arc<MprisPlayer>>,
2023-09-29 21:18:28 +02:00
}
2023-10-26 11:48:42 +02:00
impl MusicusPlayer {
pub fn set_current_index(&self, index: u32) {
let playlist = self.playlist.get().unwrap();
2023-10-27 12:32:40 +02:00
2023-10-26 11:48:42 +02:00
if let Some(item) = playlist.item(index) {
2023-11-03 17:48:27 +01:00
if let Some(old_item) = playlist.item(self.current_index.get()) {
2023-11-03 18:20:41 +01:00
old_item
.downcast::<PlaylistItem>()
2023-11-03 17:48:27 +01:00
.unwrap()
.set_is_playing(false);
}
2023-11-03 18:20:41 +01:00
2023-11-03 17:48:27 +01:00
let item = item.downcast::<PlaylistItem>().unwrap();
2023-11-03 18:20:41 +01:00
self.mpris.get().unwrap().set_metadata(Metadata {
artist: Some(vec![item.make_title()]),
title: item.make_subtitle(),
..Default::default()
});
2023-11-03 17:48:27 +01:00
let uri = glib::filename_to_uri(&item.path(), None)
2023-11-03 18:20:41 +01:00
.expect("track path should be parsable as an URI");
2023-11-03 17:48:27 +01:00
let player = self.player.get().unwrap();
player.set_uri(Some(&uri));
if self.playing.get() {
player.play();
}
2023-11-03 18:20:41 +01:00
2023-11-03 17:48:27 +01:00
self.current_index.set(index);
item.set_is_playing(true);
2023-10-26 11:48:42 +02:00
}
}
}
2023-09-29 21:18:28 +02:00
#[glib::object_subclass]
impl ObjectSubclass for MusicusPlayer {
const NAME: &'static str = "MusicusPlayer";
type Type = super::MusicusPlayer;
}
#[glib::derived_properties]
2023-11-03 17:48:27 +01:00
impl ObjectImpl for MusicusPlayer {
2023-11-03 18:59:47 +01:00
fn signals() -> &'static [Signal] {
static SIGNALS: Lazy<Vec<Signal>> =
Lazy::new(|| vec![Signal::builder("raise").build()]);
SIGNALS.as_ref()
}
2023-11-03 17:48:27 +01:00
fn constructed(&self) {
self.parent_constructed();
2023-11-03 18:20:41 +01:00
let mpris = MprisPlayer::new(
"de.johrpan.musicus".to_string(),
"Musicus".to_string(),
"de.johrpan.musicus.desktop".to_string(),
);
2023-11-03 18:59:47 +01:00
mpris.set_can_raise(true);
2023-11-03 18:20:41 +01:00
mpris.set_can_play(true);
mpris.set_can_pause(true);
mpris.set_can_go_previous(true);
mpris.set_can_go_next(true);
mpris.set_can_seek(false);
mpris.set_can_set_fullscreen(false);
let obj = self.obj();
2023-11-03 18:59:47 +01:00
mpris.connect_raise(clone!(@weak obj => move || obj.emit_by_name::<()>("raise", &[])));
2023-11-03 18:20:41 +01:00
mpris.connect_play(clone!(@weak obj => move || obj.play()));
mpris.connect_pause(clone!(@weak obj => move || obj.pause()));
mpris.connect_play_pause(clone!(@weak obj => move || obj.play_pause()));
mpris.connect_previous(clone!(@weak obj => move || obj.previous()));
mpris.connect_next(clone!(@weak obj => move || obj.next()));
self.mpris.set(mpris).expect("mpris should not be set");
2023-11-03 17:48:27 +01:00
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());
2023-11-07 15:57:56 +01:00
player.connect_position_updated(move |_, position| {
if let Some(position) = position {
2023-11-03 17:48:27 +01:00
let obj = obj.get();
2023-11-07 15:57:56 +01:00
obj.imp().position_ms.set(position.mseconds());
obj.notify_position_ms();
2023-11-03 17:48:27 +01:00
}
});
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();
2023-11-07 15:57:56 +01:00
imp.position_ms.set(0);
obj.notify_position_ms();
imp.duration_ms.set(duration.mseconds());
2023-11-03 17:48:27 +01:00
obj.notify_duration_ms();
}
});
}
}
2023-09-29 21:18:28 +02:00
}
glib::wrapper! {
pub struct MusicusPlayer(ObjectSubclass<imp::MusicusPlayer>);
}
impl MusicusPlayer {
pub fn new() -> Self {
2023-11-03 17:48:27 +01:00
let player = gstreamer_player::Player::new(
None::<gstreamer_player::PlayerVideoRenderer>,
Some(gstreamer_player::PlayerGMainContextSignalDispatcher::new(
None,
)),
);
2023-10-25 17:45:32 +02:00
glib::Object::builder()
.property("active", false)
.property("playing", false)
.property("playlist", gio::ListStore::new::<PlaylistItem>())
.property("current-index", 0u32)
2023-11-07 15:57:56 +01:00
.property("position-ms", 0u64)
2023-11-03 17:48:27 +01:00
.property("duration-ms", 60_000u64)
.property("player", player)
2023-10-25 17:45:32 +02:00
.build()
2023-09-29 21:18:28 +02:00
}
2023-11-03 18:59:47 +01:00
pub fn connect_raise<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_local("raise", true, move |values| {
let obj = values[0].get::<Self>().unwrap();
f(&obj);
None
})
}
2023-10-25 17:45:32 +02:00
pub fn append(&self, tracks: Vec<PlaylistItem>) {
let playlist = self.playlist();
2023-10-27 12:32:40 +02:00
2023-10-25 17:45:32 +02:00
for track in tracks {
playlist.append(&track);
2023-09-29 21:18:28 +02:00
}
2023-11-03 17:48:27 +01:00
if !self.active() && playlist.n_items() > 0 {
self.set_active(true);
self.set_current_index(0);
self.play();
}
2023-10-25 17:45:32 +02:00
}
2023-11-03 18:20:41 +01:00
pub fn play_pause(&self) {
if self.playing() {
self.pause();
} else {
self.play();
}
}
2023-10-25 17:45:32 +02:00
pub fn play(&self) {
2023-11-03 17:48:27 +01:00
self.player().play();
self.set_playing(true);
2023-11-03 18:59:47 +01:00
self.imp()
.mpris
.get()
.unwrap()
.set_playback_status(PlaybackStatus::Playing);
2023-09-29 21:18:28 +02:00
}
pub fn pause(&self) {
2023-11-03 17:48:27 +01:00
self.player().pause();
self.set_playing(false);
2023-11-03 18:59:47 +01:00
self.imp()
.mpris
.get()
.unwrap()
.set_playback_status(PlaybackStatus::Paused);
2023-09-29 21:18:28 +02:00
}
2023-10-27 12:32:40 +02:00
2023-11-07 15:57:56 +01:00
pub fn seek_to(&self, time_ms: u64) {
self.player().seek(gst::ClockTime::from_mseconds(time_ms));
}
2023-10-27 12:32:40 +02:00
pub fn current_item(&self) -> Option<PlaylistItem> {
let imp = self.imp();
imp.playlist
.get()
.unwrap()
.item(imp.current_index.get())
.and_downcast::<PlaylistItem>()
}
2023-11-03 16:22:58 +01:00
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);
}
}
2023-09-29 21:18:28 +02:00
}
impl Default for MusicusPlayer {
fn default() -> Self {
Self::new()
}
}