2022-04-08 19:59:49 +02:00
|
|
|
use crate::{Backend, Error, Result};
|
2023-02-11 13:35:26 +01:00
|
|
|
use db::Track;
|
2021-02-05 00:50:27 +01:00
|
|
|
use glib::clone;
|
2023-03-28 14:39:54 +02:00
|
|
|
use gstreamer_player::PlayerVideoRenderer;
|
2023-02-11 13:35:26 +01:00
|
|
|
use musicus_database as db;
|
2020-11-07 16:11:08 +01:00
|
|
|
use std::cell::{Cell, RefCell};
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::rc::Rc;
|
2021-02-05 00:50:27 +01:00
|
|
|
use std::sync::Arc;
|
2020-11-07 16:11:08 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
use mpris_player::{Metadata, MprisPlayer, PlaybackStatus};
|
|
|
|
|
|
2020-11-07 16:11:08 +01:00
|
|
|
pub struct Player {
|
|
|
|
|
music_library_path: PathBuf,
|
|
|
|
|
player: gstreamer_player::Player,
|
2021-04-08 00:08:31 +02:00
|
|
|
playlist: RefCell<Vec<Track>>,
|
2020-11-07 16:11:08 +01:00
|
|
|
current_track: Cell<Option<usize>>,
|
|
|
|
|
playing: Cell<bool>,
|
2021-10-10 20:35:54 +02:00
|
|
|
duration: Cell<u64>,
|
2022-04-08 19:59:49 +02:00
|
|
|
track_generator: RefCell<Option<Box<dyn TrackGenerator>>>,
|
2021-04-08 00:08:31 +02:00
|
|
|
playlist_cbs: RefCell<Vec<Box<dyn Fn(Vec<Track>)>>>,
|
|
|
|
|
track_cbs: RefCell<Vec<Box<dyn Fn(usize)>>>,
|
2020-12-20 11:47:27 +01:00
|
|
|
duration_cbs: RefCell<Vec<Box<dyn Fn(u64)>>>,
|
|
|
|
|
playing_cbs: RefCell<Vec<Box<dyn Fn(bool)>>>,
|
|
|
|
|
position_cbs: RefCell<Vec<Box<dyn Fn(u64)>>>,
|
2021-02-05 00:50:27 +01:00
|
|
|
raise_cb: RefCell<Option<Box<dyn Fn()>>>,
|
2021-02-15 12:38:17 +01:00
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
mpris: Arc<MprisPlayer>,
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Player {
|
|
|
|
|
pub fn new(music_library_path: PathBuf) -> Rc<Self> {
|
|
|
|
|
let dispatcher = gstreamer_player::PlayerGMainContextSignalDispatcher::new(None);
|
2023-03-28 14:39:54 +02:00
|
|
|
let player = gstreamer_player::Player::new(None::<PlayerVideoRenderer>, Some(dispatcher));
|
2021-06-30 20:28:29 +02:00
|
|
|
let mut config = player.config();
|
2020-11-07 16:11:08 +01:00
|
|
|
config.set_position_update_interval(250);
|
|
|
|
|
player.set_config(config).unwrap();
|
|
|
|
|
player.set_video_track_enabled(false);
|
|
|
|
|
|
|
|
|
|
let result = Rc::new(Self {
|
|
|
|
|
music_library_path,
|
|
|
|
|
player: player.clone(),
|
|
|
|
|
playlist: RefCell::new(Vec::new()),
|
|
|
|
|
current_track: Cell::new(None),
|
|
|
|
|
playing: Cell::new(false),
|
2021-10-10 20:35:54 +02:00
|
|
|
duration: Cell::new(0),
|
2022-04-08 19:59:49 +02:00
|
|
|
track_generator: RefCell::new(None),
|
2020-11-07 20:07:26 +01:00
|
|
|
playlist_cbs: RefCell::new(Vec::new()),
|
|
|
|
|
track_cbs: RefCell::new(Vec::new()),
|
|
|
|
|
duration_cbs: RefCell::new(Vec::new()),
|
|
|
|
|
playing_cbs: RefCell::new(Vec::new()),
|
|
|
|
|
position_cbs: RefCell::new(Vec::new()),
|
2021-02-05 00:50:27 +01:00
|
|
|
raise_cb: RefCell::new(None),
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
mpris: {
|
|
|
|
|
let mpris = MprisPlayer::new(
|
|
|
|
|
"de.johrpan.musicus".to_string(),
|
|
|
|
|
"Musicus".to_string(),
|
|
|
|
|
"de.johrpan.musicus.desktop".to_string(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
mpris.set_can_raise(true);
|
|
|
|
|
mpris.set_can_play(false);
|
|
|
|
|
mpris.set_can_go_previous(false);
|
|
|
|
|
mpris.set_can_go_next(false);
|
|
|
|
|
mpris.set_can_seek(false);
|
|
|
|
|
mpris.set_can_set_fullscreen(false);
|
|
|
|
|
|
|
|
|
|
mpris
|
|
|
|
|
},
|
2020-11-07 16:11:08 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let clone = fragile::Fragile::new(result.clone());
|
|
|
|
|
player.connect_end_of_stream(move |_| {
|
|
|
|
|
let clone = clone.get();
|
|
|
|
|
if clone.has_next() {
|
|
|
|
|
clone.next().unwrap();
|
|
|
|
|
} else {
|
|
|
|
|
clone.player.stop();
|
2020-11-07 20:07:26 +01:00
|
|
|
clone.playing.replace(false);
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*clone.playing_cbs.borrow() {
|
2020-11-07 16:11:08 +01:00
|
|
|
cb(false);
|
|
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
2021-02-05 00:50:27 +01:00
|
|
|
clone.mpris.set_playback_status(PlaybackStatus::Paused);
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let clone = fragile::Fragile::new(result.clone());
|
|
|
|
|
player.connect_position_updated(move |_, position| {
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*clone.get().position_cbs.borrow() {
|
2021-06-30 20:28:29 +02:00
|
|
|
cb(position.unwrap().mseconds());
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let clone = fragile::Fragile::new(result.clone());
|
|
|
|
|
player.connect_duration_changed(move |_, duration| {
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*clone.get().duration_cbs.borrow() {
|
2021-10-10 20:35:54 +02:00
|
|
|
let duration = duration.unwrap().mseconds();
|
|
|
|
|
clone.get().duration.set(duration);
|
|
|
|
|
cb(duration);
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
2021-05-07 23:49:05 +02:00
|
|
|
result
|
|
|
|
|
.mpris
|
|
|
|
|
.connect_play_pause(clone!(@weak result => move || {
|
2022-02-08 18:18:29 +01:00
|
|
|
result.play_pause().unwrap();
|
2021-05-07 23:49:05 +02:00
|
|
|
}));
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
result.mpris.connect_play(clone!(@weak result => move || {
|
|
|
|
|
if !result.is_playing() {
|
2022-02-08 18:18:29 +01:00
|
|
|
result.play_pause().unwrap();
|
2021-02-15 12:38:17 +01:00
|
|
|
}
|
|
|
|
|
}));
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
result.mpris.connect_pause(clone!(@weak result => move || {
|
|
|
|
|
if result.is_playing() {
|
2022-02-08 18:18:29 +01:00
|
|
|
result.play_pause().unwrap();
|
2021-02-15 12:38:17 +01:00
|
|
|
}
|
|
|
|
|
}));
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-05-07 23:49:05 +02:00
|
|
|
result
|
|
|
|
|
.mpris
|
|
|
|
|
.connect_previous(clone!(@weak result => move || {
|
|
|
|
|
let _ = result.previous();
|
|
|
|
|
}));
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
result.mpris.connect_next(clone!(@weak result => move || {
|
|
|
|
|
let _ = result.next();
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
result.mpris.connect_raise(clone!(@weak result => move || {
|
|
|
|
|
let cb = result.raise_cb.borrow();
|
|
|
|
|
if let Some(cb) = &*cb {
|
|
|
|
|
cb()
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2020-11-07 16:11:08 +01:00
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-08 19:59:49 +02:00
|
|
|
pub fn set_track_generator<G: TrackGenerator + 'static>(&self, generator: Option<G>) {
|
|
|
|
|
self.track_generator.replace(match generator {
|
|
|
|
|
Some(generator) => Some(Box::new(generator)),
|
|
|
|
|
None => None,
|
|
|
|
|
});
|
2022-02-08 17:44:21 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
pub fn add_playlist_cb<F: Fn(Vec<Track>) + 'static>(&self, cb: F) {
|
2020-11-07 20:07:26 +01:00
|
|
|
self.playlist_cbs.borrow_mut().push(Box::new(cb));
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
pub fn add_track_cb<F: Fn(usize) + 'static>(&self, cb: F) {
|
2020-11-07 20:07:26 +01:00
|
|
|
self.track_cbs.borrow_mut().push(Box::new(cb));
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-20 11:47:27 +01:00
|
|
|
pub fn add_duration_cb<F: Fn(u64) + 'static>(&self, cb: F) {
|
2020-11-07 20:07:26 +01:00
|
|
|
self.duration_cbs.borrow_mut().push(Box::new(cb));
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-20 11:47:27 +01:00
|
|
|
pub fn add_playing_cb<F: Fn(bool) + 'static>(&self, cb: F) {
|
2020-11-07 20:07:26 +01:00
|
|
|
self.playing_cbs.borrow_mut().push(Box::new(cb));
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-20 11:47:27 +01:00
|
|
|
pub fn add_position_cb<F: Fn(u64) + 'static>(&self, cb: F) {
|
2020-11-07 20:07:26 +01:00
|
|
|
self.position_cbs.borrow_mut().push(Box::new(cb));
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-05 00:50:27 +01:00
|
|
|
pub fn set_raise_cb<F: Fn() + 'static>(&self, cb: F) {
|
|
|
|
|
self.raise_cb.replace(Some(Box::new(cb)));
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
pub fn get_playlist(&self) -> Vec<Track> {
|
2020-11-07 16:11:08 +01:00
|
|
|
self.playlist.borrow().clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_current_track(&self) -> Option<usize> {
|
|
|
|
|
self.current_track.get()
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-30 20:28:29 +02:00
|
|
|
pub fn get_duration(&self) -> Option<gstreamer::ClockTime> {
|
|
|
|
|
self.player.duration()
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_playing(&self) -> bool {
|
|
|
|
|
self.playing.get()
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-08 19:59:49 +02:00
|
|
|
/// Add some items to the playlist.
|
|
|
|
|
pub fn add_items(&self, mut items: Vec<Track>) -> Result<()> {
|
|
|
|
|
if items.is_empty() {
|
2022-12-19 17:00:45 +01:00
|
|
|
return Ok(());
|
2022-04-08 19:59:49 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
let was_empty = {
|
|
|
|
|
let mut playlist = self.playlist.borrow_mut();
|
|
|
|
|
let was_empty = playlist.is_empty();
|
2020-11-07 16:11:08 +01:00
|
|
|
|
2022-04-08 19:59:49 +02:00
|
|
|
playlist.append(&mut items);
|
2020-11-07 16:11:08 +01:00
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
was_empty
|
|
|
|
|
};
|
2020-11-07 16:11:08 +01:00
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
for cb in &*self.playlist_cbs.borrow() {
|
|
|
|
|
cb(self.playlist.borrow().clone());
|
|
|
|
|
}
|
2020-11-07 16:11:08 +01:00
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
if was_empty {
|
|
|
|
|
self.set_track(0)?;
|
|
|
|
|
self.player.play();
|
|
|
|
|
self.playing.set(true);
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
for cb in &*self.playing_cbs.borrow() {
|
|
|
|
|
cb(true);
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
|
|
|
|
self.mpris.set_can_play(true);
|
|
|
|
|
self.mpris.set_playback_status(PlaybackStatus::Playing);
|
|
|
|
|
}
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
2021-04-08 00:08:31 +02:00
|
|
|
|
|
|
|
|
Ok(())
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-08 18:18:29 +01:00
|
|
|
pub fn play_pause(&self) -> Result<()> {
|
2020-11-07 16:11:08 +01:00
|
|
|
if self.is_playing() {
|
|
|
|
|
self.player.pause();
|
|
|
|
|
self.playing.set(false);
|
|
|
|
|
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*self.playing_cbs.borrow() {
|
2020-11-07 16:11:08 +01:00
|
|
|
cb(false);
|
|
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
2021-02-05 00:50:27 +01:00
|
|
|
self.mpris.set_playback_status(PlaybackStatus::Paused);
|
2020-11-07 16:11:08 +01:00
|
|
|
} else {
|
2022-02-08 18:18:29 +01:00
|
|
|
if self.current_track.get().is_none() {
|
|
|
|
|
self.next()?;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-07 16:11:08 +01:00
|
|
|
self.player.play();
|
|
|
|
|
self.playing.set(true);
|
|
|
|
|
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*self.playing_cbs.borrow() {
|
2020-11-07 16:11:08 +01:00
|
|
|
cb(true);
|
|
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
2021-02-05 00:50:27 +01:00
|
|
|
self.mpris.set_playback_status(PlaybackStatus::Playing);
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
2022-02-08 18:18:29 +01:00
|
|
|
|
|
|
|
|
Ok(())
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-07 20:07:26 +01:00
|
|
|
pub fn seek(&self, ms: u64) {
|
|
|
|
|
self.player.seek(gstreamer::ClockTime::from_mseconds(ms));
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-07 16:11:08 +01:00
|
|
|
pub fn has_previous(&self) -> bool {
|
2021-04-08 00:08:31 +02:00
|
|
|
if let Some(current_track) = self.current_track.get() {
|
|
|
|
|
current_track > 0
|
2020-11-07 16:11:08 +01:00
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn previous(&self) -> Result<()> {
|
2021-06-30 20:28:29 +02:00
|
|
|
let mut current_track = self.current_track.get().ok_or_else(|| {
|
|
|
|
|
Error::Other(String::from(
|
|
|
|
|
"Player tried to access non existant current track.",
|
|
|
|
|
))
|
|
|
|
|
})?;
|
2020-11-07 16:11:08 +01:00
|
|
|
|
|
|
|
|
if current_track > 0 {
|
|
|
|
|
current_track -= 1;
|
|
|
|
|
} else {
|
2021-02-18 18:20:31 +01:00
|
|
|
return Err(Error::Other(String::from("No existing previous track.")));
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
self.set_track(current_track)
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn has_next(&self) -> bool {
|
2022-04-08 19:59:49 +02:00
|
|
|
if let Some(generator) = &*self.track_generator.borrow() {
|
|
|
|
|
generator.has_next()
|
2022-02-08 17:44:21 +01:00
|
|
|
} else if let Some(current_track) = self.current_track.get() {
|
2021-04-08 00:08:31 +02:00
|
|
|
let playlist = self.playlist.borrow();
|
|
|
|
|
current_track + 1 < playlist.len()
|
2020-11-07 16:11:08 +01:00
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn next(&self) -> Result<()> {
|
2022-02-08 18:18:29 +01:00
|
|
|
let current_track = self.current_track.get();
|
2022-04-08 19:59:49 +02:00
|
|
|
let generator = self.track_generator.borrow();
|
2020-11-07 16:11:08 +01:00
|
|
|
|
2022-02-08 18:18:29 +01:00
|
|
|
if let Some(current_track) = current_track {
|
|
|
|
|
if current_track + 1 >= self.playlist.borrow().len() {
|
2022-04-08 19:59:49 +02:00
|
|
|
if let Some(generator) = &*generator {
|
|
|
|
|
let items = generator.next();
|
|
|
|
|
if !items.is_empty() {
|
|
|
|
|
self.add_items(items)?;
|
|
|
|
|
} else {
|
|
|
|
|
return Err(Error::Other(String::from(
|
|
|
|
|
"Track generator failed to generate next track.",
|
|
|
|
|
)));
|
|
|
|
|
}
|
2022-02-08 18:18:29 +01:00
|
|
|
} else {
|
|
|
|
|
return Err(Error::Other(String::from("No existing next track.")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.set_track(current_track + 1)?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
2022-04-08 19:59:49 +02:00
|
|
|
} else if let Some(generator) = &*generator {
|
|
|
|
|
let items = generator.next();
|
|
|
|
|
if !items.is_empty() {
|
|
|
|
|
self.add_items(items)?;
|
|
|
|
|
} else {
|
|
|
|
|
return Err(Error::Other(String::from(
|
|
|
|
|
"Track generator failed to generate next track.",
|
|
|
|
|
)));
|
|
|
|
|
}
|
2022-02-08 18:18:29 +01:00
|
|
|
|
|
|
|
|
Ok(())
|
2020-11-07 16:11:08 +01:00
|
|
|
} else {
|
2022-02-08 18:18:29 +01:00
|
|
|
Err(Error::Other(String::from("No existing next track.")))
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
pub fn set_track(&self, current_track: usize) -> Result<()> {
|
|
|
|
|
let track = &self.playlist.borrow()[current_track];
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-05-07 23:49:05 +02:00
|
|
|
let path = self
|
|
|
|
|
.music_library_path
|
|
|
|
|
.join(track.path.clone())
|
|
|
|
|
.into_os_string()
|
|
|
|
|
.into_string()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2021-06-30 20:28:29 +02:00
|
|
|
let uri = glib::filename_to_uri(&path, None)
|
|
|
|
|
.map_err(|_| Error::Other(format!("Failed to create URI from path: {}", path)))?;
|
2020-11-07 16:11:08 +01:00
|
|
|
|
2022-04-16 12:24:30 +02:00
|
|
|
self.player.set_uri(Some(&uri));
|
2021-02-18 18:20:31 +01:00
|
|
|
|
2020-11-07 16:11:08 +01:00
|
|
|
if self.is_playing() {
|
|
|
|
|
self.player.play();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.current_track.set(Some(current_track));
|
|
|
|
|
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*self.track_cbs.borrow() {
|
2021-04-08 00:08:31 +02:00
|
|
|
cb(current_track);
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
|
|
|
|
let mut parts = Vec::<String>::new();
|
|
|
|
|
for part in &track.work_parts {
|
2021-04-08 00:08:31 +02:00
|
|
|
parts.push(track.recording.work.parts[*part].title.clone());
|
2021-02-15 12:38:17 +01:00
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
let mut title = track.recording.work.get_title();
|
2021-02-15 12:38:17 +01:00
|
|
|
if !parts.is_empty() {
|
|
|
|
|
title = format!("{}: {}", title, parts.join(", "));
|
|
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-04-08 00:08:31 +02:00
|
|
|
let subtitle = track.recording.get_performers();
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
let mut metadata = Metadata::new();
|
|
|
|
|
metadata.artist = Some(vec![title]);
|
|
|
|
|
metadata.title = Some(subtitle);
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
self.mpris.set_metadata(metadata);
|
|
|
|
|
self.mpris.set_can_go_previous(self.has_previous());
|
|
|
|
|
self.mpris.set_can_go_next(self.has_next());
|
|
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2020-11-07 16:11:08 +01:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-05 15:50:31 +01:00
|
|
|
pub fn send_data(&self) {
|
|
|
|
|
for cb in &*self.playlist_cbs.borrow() {
|
|
|
|
|
cb(self.playlist.borrow().clone());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for cb in &*self.track_cbs.borrow() {
|
2021-04-08 00:08:31 +02:00
|
|
|
cb(self.current_track.get().unwrap());
|
2021-02-05 15:50:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for cb in &*self.duration_cbs.borrow() {
|
2021-10-10 20:35:54 +02:00
|
|
|
cb(self.duration.get());
|
2021-02-05 15:50:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for cb in &*self.playing_cbs.borrow() {
|
|
|
|
|
cb(self.is_playing());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-07 16:11:08 +01:00
|
|
|
pub fn clear(&self) {
|
|
|
|
|
self.player.stop();
|
|
|
|
|
self.playing.set(false);
|
|
|
|
|
self.current_track.set(None);
|
|
|
|
|
self.playlist.replace(Vec::new());
|
|
|
|
|
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*self.playing_cbs.borrow() {
|
2020-11-07 16:11:08 +01:00
|
|
|
cb(false);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-07 20:07:26 +01:00
|
|
|
for cb in &*self.playlist_cbs.borrow() {
|
2020-11-07 16:11:08 +01:00
|
|
|
cb(Vec::new());
|
|
|
|
|
}
|
2021-02-05 00:50:27 +01:00
|
|
|
|
2021-02-15 12:38:17 +01:00
|
|
|
#[cfg(target_os = "linux")]
|
2021-02-05 00:50:27 +01:00
|
|
|
self.mpris.set_can_play(false);
|
2020-11-07 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-04-08 19:59:49 +02:00
|
|
|
|
|
|
|
|
/// Generator for new tracks to be appended to the playlist.
|
|
|
|
|
pub trait TrackGenerator {
|
|
|
|
|
/// Whether the generator will provide a next track if asked.
|
|
|
|
|
fn has_next(&self) -> bool;
|
|
|
|
|
|
|
|
|
|
/// Provide the next track.
|
|
|
|
|
///
|
|
|
|
|
/// This function should always return at least one track in a state where
|
|
|
|
|
/// `has_next()` returns `true`.
|
|
|
|
|
fn next(&self) -> Vec<Track>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A track generator that generates one random track per call.
|
|
|
|
|
pub struct RandomTrackGenerator {
|
|
|
|
|
backend: Rc<Backend>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RandomTrackGenerator {
|
|
|
|
|
pub fn new(backend: Rc<Backend>) -> Self {
|
|
|
|
|
Self { backend }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TrackGenerator for RandomTrackGenerator {
|
|
|
|
|
fn has_next(&self) -> bool {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn next(&self) -> Vec<Track> {
|
2023-02-11 13:35:26 +01:00
|
|
|
vec![db::random_track(&mut self.backend.db().lock().unwrap()).unwrap()]
|
2022-04-08 19:59:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A track generator that returns the tracks of one random recording per call.
|
|
|
|
|
pub struct RandomRecordingGenerator {
|
|
|
|
|
backend: Rc<Backend>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RandomRecordingGenerator {
|
|
|
|
|
pub fn new(backend: Rc<Backend>) -> Self {
|
|
|
|
|
Self { backend }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TrackGenerator for RandomRecordingGenerator {
|
|
|
|
|
fn has_next(&self) -> bool {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn next(&self) -> Vec<Track> {
|
2023-02-11 13:35:26 +01:00
|
|
|
let recording = db::random_recording(&mut self.backend.db().lock().unwrap()).unwrap();
|
|
|
|
|
db::get_tracks(&mut self.backend.db().lock().unwrap(), &recording.id).unwrap()
|
2022-04-08 19:59:49 +02:00
|
|
|
}
|
|
|
|
|
}
|