| 
									
										
										
										
											2021-02-03 23:03:47 +01:00
										 |  |  | use crate::backend::TrackSet;
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  | use anyhow::anyhow;
 | 
					
						
							|  |  |  | use anyhow::Result;
 | 
					
						
							|  |  |  | use gstreamer_player::prelude::*;
 | 
					
						
							|  |  |  | use std::cell::{Cell, RefCell};
 | 
					
						
							|  |  |  | use std::path::PathBuf;
 | 
					
						
							|  |  |  | use std::rc::Rc;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #[derive(Clone)]
 | 
					
						
							|  |  |  | pub struct PlaylistItem {
 | 
					
						
							| 
									
										
										
										
											2021-01-16 15:08:12 +01:00
										 |  |  |     pub track_set: TrackSet,
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |     pub indices: Vec<usize>,
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | pub struct Player {
 | 
					
						
							|  |  |  |     music_library_path: PathBuf,
 | 
					
						
							|  |  |  |     player: gstreamer_player::Player,
 | 
					
						
							|  |  |  |     playlist: RefCell<Vec<PlaylistItem>>,
 | 
					
						
							|  |  |  |     current_item: Cell<Option<usize>>,
 | 
					
						
							|  |  |  |     current_track: Cell<Option<usize>>,
 | 
					
						
							|  |  |  |     playing: Cell<bool>,
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |     playlist_cbs: RefCell<Vec<Box<dyn Fn(Vec<PlaylistItem>)>>>,
 | 
					
						
							|  |  |  |     track_cbs: RefCell<Vec<Box<dyn Fn(usize, usize)>>>,
 | 
					
						
							|  |  |  |     duration_cbs: RefCell<Vec<Box<dyn Fn(u64)>>>,
 | 
					
						
							|  |  |  |     playing_cbs: RefCell<Vec<Box<dyn Fn(bool)>>>,
 | 
					
						
							|  |  |  |     position_cbs: RefCell<Vec<Box<dyn Fn(u64)>>>,
 | 
					
						
							| 
									
										
										
										
											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);
 | 
					
						
							|  |  |  |         let player = gstreamer_player::Player::new(None, Some(&dispatcher.upcast()));
 | 
					
						
							|  |  |  |         let mut config = player.get_config();
 | 
					
						
							|  |  |  |         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_item: Cell::new(None),
 | 
					
						
							|  |  |  |             current_track: Cell::new(None),
 | 
					
						
							|  |  |  |             playing: Cell::new(false),
 | 
					
						
							| 
									
										
										
										
											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()),
 | 
					
						
							| 
									
										
										
										
											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);
 | 
					
						
							|  |  |  |                 for cb in &*clone.playing_cbs.borrow() {
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |                     cb(false);
 | 
					
						
							|  |  |  |                 }
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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() {
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |                 cb(position.mseconds().unwrap());
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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() {
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |                 cb(duration.mseconds().unwrap());
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |     pub fn add_playlist_cb<F: Fn(Vec<PlaylistItem>) + '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
										 |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |     pub fn add_track_cb<F: Fn(usize, 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
										 |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn get_playlist(&self) -> Vec<PlaylistItem> {
 | 
					
						
							|  |  |  |         self.playlist.borrow().clone()
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn get_current_item(&self) -> Option<usize> {
 | 
					
						
							|  |  |  |         self.current_item.get()
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn get_current_track(&self) -> Option<usize> {
 | 
					
						
							|  |  |  |         self.current_track.get()
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn get_duration(&self) -> gstreamer::ClockTime {
 | 
					
						
							|  |  |  |         self.player.get_duration()
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn is_playing(&self) -> bool {
 | 
					
						
							|  |  |  |         self.playing.get()
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn add_item(&self, item: PlaylistItem) -> Result<()> {
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |         if item.indices.is_empty() {
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |             Err(anyhow!(
 | 
					
						
							|  |  |  |                 "Tried to add playlist item without tracks to playlist!"
 | 
					
						
							|  |  |  |             ))
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             let was_empty = {
 | 
					
						
							|  |  |  |                 let mut playlist = self.playlist.borrow_mut();
 | 
					
						
							|  |  |  |                 let was_empty = playlist.is_empty();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 playlist.push(item);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 was_empty
 | 
					
						
							|  |  |  |             };
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 20:07:26 +01:00
										 |  |  |             for cb in &*self.playlist_cbs.borrow() {
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |                 cb(self.playlist.borrow().clone());
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if was_empty {
 | 
					
						
							|  |  |  |                 self.set_track(0, 0)?;
 | 
					
						
							|  |  |  |                 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);
 | 
					
						
							|  |  |  |                 }
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Ok(())
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn play_pause(&self) {
 | 
					
						
							|  |  |  |         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);
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             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);
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 {
 | 
					
						
							|  |  |  |         if let Some(current_item) = self.current_item.get() {
 | 
					
						
							|  |  |  |             if let Some(current_track) = self.current_track.get() {
 | 
					
						
							|  |  |  |                 current_track > 0 || current_item > 0
 | 
					
						
							|  |  |  |             } else {
 | 
					
						
							|  |  |  |                 false
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             false
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn previous(&self) -> Result<()> {
 | 
					
						
							|  |  |  |         let mut current_item = self.current_item.get().ok_or(anyhow!("No current item!"))?;
 | 
					
						
							|  |  |  |         let mut current_track = self
 | 
					
						
							|  |  |  |             .current_track
 | 
					
						
							|  |  |  |             .get()
 | 
					
						
							|  |  |  |             .ok_or(anyhow!("No current track!"))?;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let playlist = self.playlist.borrow();
 | 
					
						
							|  |  |  |         if current_track > 0 {
 | 
					
						
							|  |  |  |             current_track -= 1;
 | 
					
						
							|  |  |  |         } else if current_item > 0 {
 | 
					
						
							|  |  |  |             current_item -= 1;
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |             current_track = playlist[current_item].indices.len() - 1;
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |         } else {
 | 
					
						
							|  |  |  |             return Err(anyhow!("No previous track!"));
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.set_track(current_item, current_track)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn has_next(&self) -> bool {
 | 
					
						
							|  |  |  |         if let Some(current_item) = self.current_item.get() {
 | 
					
						
							|  |  |  |             if let Some(current_track) = self.current_track.get() {
 | 
					
						
							|  |  |  |                 let playlist = self.playlist.borrow();
 | 
					
						
							|  |  |  |                 let item = &playlist[current_item];
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |                 current_track + 1 < item.indices.len() || current_item + 1 < playlist.len()
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |             } else {
 | 
					
						
							|  |  |  |                 false
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             false
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn next(&self) -> Result<()> {
 | 
					
						
							|  |  |  |         let mut current_item = self.current_item.get().ok_or(anyhow!("No current item!"))?;
 | 
					
						
							|  |  |  |         let mut current_track = self
 | 
					
						
							|  |  |  |             .current_track
 | 
					
						
							|  |  |  |             .get()
 | 
					
						
							|  |  |  |             .ok_or(anyhow!("No current track!"))?;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let playlist = self.playlist.borrow();
 | 
					
						
							|  |  |  |         let item = &playlist[current_item];
 | 
					
						
							| 
									
										
										
										
											2020-12-20 11:47:27 +01:00
										 |  |  |         if current_track + 1 < item.indices.len() {
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |             current_track += 1;
 | 
					
						
							|  |  |  |         } else if current_item + 1 < playlist.len() {
 | 
					
						
							|  |  |  |             current_item += 1;
 | 
					
						
							|  |  |  |             current_track = 0;
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             return Err(anyhow!("No next track!"));
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.set_track(current_item, current_track)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn set_track(&self, current_item: usize, current_track: usize) -> Result<()> {
 | 
					
						
							|  |  |  |         let uri = format!(
 | 
					
						
							|  |  |  |             "file://{}",
 | 
					
						
							|  |  |  |             self.music_library_path
 | 
					
						
							|  |  |  |                 .join(
 | 
					
						
							| 
									
										
										
										
											2021-01-16 15:08:12 +01:00
										 |  |  |                     self.playlist.borrow()[current_item].track_set.tracks[current_track].path.clone(),
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |                 )
 | 
					
						
							|  |  |  |                 .to_str()
 | 
					
						
							|  |  |  |                 .unwrap(),
 | 
					
						
							|  |  |  |         );
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.player.set_uri(&uri);
 | 
					
						
							|  |  |  |         if self.is_playing() {
 | 
					
						
							|  |  |  |             self.player.play();
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.current_item.set(Some(current_item));
 | 
					
						
							|  |  |  |         self.current_track.set(Some(current_track));
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 20:07:26 +01:00
										 |  |  |         for cb in &*self.track_cbs.borrow() {
 | 
					
						
							| 
									
										
										
										
											2020-11-07 16:11:08 +01:00
										 |  |  |             cb(current_item, current_track);
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Ok(())
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pub fn clear(&self) {
 | 
					
						
							|  |  |  |         self.player.stop();
 | 
					
						
							|  |  |  |         self.playing.set(false);
 | 
					
						
							|  |  |  |         self.current_item.set(None);
 | 
					
						
							|  |  |  |         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());
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 |