use crate::{ database::MusicIndex, media::{TrackId, TrackInfo}, playback::{Playback, PlaybackControl, PlaybackStatus}, scanner::MusicScanner, Error, FatalError, }; use flow::{ok, return_error, Flow}; use gstreamer::{format::ClockTime, prelude::*, MessageView}; use std::{ sync::{Arc, Mutex, RwLock}, thread, time::{Duration, Instant}, }; use tokio::{ sync::mpsc::{channel, Receiver, Sender}, task::AbortHandle, }; fn scan_frequency() -> Duration { Duration::from_secs(60) } #[derive(Clone, Debug)] pub enum ControlMsg { PlayTrack(TrackId), Exit, } #[derive(Clone, Debug)] pub enum TrackMsg { UpdateInProgress, UpdateComplete, } #[derive(Clone, Debug)] pub enum PlaybackMsg { PositionUpdate, Playing, Pausing, Stopping, } pub struct Core { db: Arc, playback_controller: Option, next_scan: Arc>, scanner_handle: thread::JoinHandle<()>, } impl Core { pub fn new( db: Arc, scanner: Arc, ) -> Flow { let db = db; let next_scan = Arc::new(RwLock::new(Instant::now())); let scanner_handle = { let db = db.clone(); let next_scan = next_scan.clone(); thread::spawn(move || scan_loop(scanner, db, next_scan)) }; ok(Core { db, playback_controller: None, next_scan, scanner_handle, }) } /* pub fn main_loop(&self) -> Flow<(), FatalError, Error> { // let (scanner_tx, _scanner_rx) = channel(); loop { println!("loop"); /* match self.control_rx.recv_timeout(Duration::from_millis(1000)) { Ok(ControlMsg::PlayTrack(id)) => { let _ = self.play_track(id); } Ok(ControlMsg::Exit) => return ok(()), Err(RecvTimeoutError::Timeout) => (), Err(RecvTimeoutError::Disconnected) => return ok(()), } */ thread::sleep(Duration::from_secs(1)); } } */ pub fn list_tracks<'a>(&'a self) -> Flow, FatalError, Error> { self.db.list_tracks().map_err(Error::DatabaseError) } pub fn play_track<'a>(&'a mut self, id: TrackId) -> Flow<(), FatalError, Error> { self.stop_playback(); self.playback_controller = Some(return_error!(Playback::new(id))); ok(()) } pub fn stop_playback<'a>(&'a mut self) { match self.playback_controller { Some(ref controller) => controller.stop(), None => (), } self.playback_controller = None; } } /* #[derive(Clone)] pub struct CoreAPI { control_tx: Arc>>, } impl CoreAPI { pub async fn play_track(&self, id: TrackId) -> () { self.control_tx .lock() .unwrap() .send(ControlMsg::PlayTrack(id)) .await .unwrap() } } */ pub fn scan_loop( scanner: Arc, db: Arc, next_scan: Arc>, ) { loop { if Instant::now() >= *next_scan.read().unwrap() { let scan_start = Instant::now(); let mut counter = 0; for track in scanner.scan() { counter += 1; match track { Ok(track) => db.add_track(track), Err(_) => ok(()), }; } *next_scan.write().unwrap() = Instant::now() + scan_frequency(); println!( "scanned {} files in {:?}", counter, Instant::now() - scan_start ); } thread::sleep(Duration::from_secs(1)); } } #[cfg(test)] mod test { use super::*; use crate::{database::MemoryIndex, media::TrackId, scanner::factories::MockScanner}; use std::collections::HashSet; /* fn with_example_index(f: F) where F: Fn(Core), { let index = MemoryIndex::new(); let scanner = MockScanner::new(); match Core::new(Arc::new(index), Arc::new(scanner)) { Flow::Ok((core, api)) => { thread::sleep(Duration::from_millis(10)); f(core) } Flow::Err(error) => panic!("{:?}", error), Flow::Fatal(error) => panic!("{:?}", error), } } #[test] fn it_lists_tracks() { with_example_index(|core| match core.list_tracks() { Flow::Ok(tracks) => { let track_ids = tracks .iter() .map(|t| t.id.clone()) .collect::>(); assert_eq!(track_ids.len(), 5); assert_eq!( track_ids, HashSet::from([ TrackId::from("/home/savanni/Track 1.mp3".to_owned()), TrackId::from("/home/savanni/Track 2.mp3".to_owned()), TrackId::from("/home/savanni/Track 3.mp3".to_owned()), TrackId::from("/home/savanni/Track 4.mp3".to_owned()), TrackId::from("/home/savanni/Track 5.mp3".to_owned()), ]) ); } Flow::Fatal(err) => panic!("fatal error: {:?}", err), Flow::Err(err) => panic!("error: {:?}", err), }) } */ }