2023-03-10 14:35:18 +00:00
|
|
|
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};
|
2023-02-11 17:59:15 +00:00
|
|
|
use std::{
|
2023-03-11 18:02:52 +00:00
|
|
|
sync::{Arc, Mutex, RwLock},
|
2023-02-11 17:59:15 +00:00
|
|
|
thread,
|
|
|
|
time::{Duration, Instant},
|
|
|
|
};
|
2023-03-11 18:02:52 +00:00
|
|
|
use tokio::{
|
|
|
|
sync::mpsc::{channel, Receiver, Sender},
|
|
|
|
task::AbortHandle,
|
|
|
|
};
|
2023-02-11 17:59:15 +00:00
|
|
|
|
2023-02-26 03:17:00 +00:00
|
|
|
fn scan_frequency() -> Duration {
|
|
|
|
Duration::from_secs(60)
|
2023-02-11 17:59:15 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2023-02-11 17:59:15 +00:00
|
|
|
pub enum ControlMsg {
|
2023-03-10 14:35:18 +00:00
|
|
|
PlayTrack(TrackId),
|
2023-02-11 17:59:15 +00:00
|
|
|
Exit,
|
|
|
|
}
|
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2023-02-11 17:59:15 +00:00
|
|
|
pub enum TrackMsg {
|
2023-02-26 03:17:00 +00:00
|
|
|
UpdateInProgress,
|
|
|
|
UpdateComplete,
|
2023-02-11 17:59:15 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2023-02-11 17:59:15 +00:00
|
|
|
pub enum PlaybackMsg {
|
|
|
|
PositionUpdate,
|
|
|
|
Playing,
|
|
|
|
Pausing,
|
|
|
|
Stopping,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Core {
|
|
|
|
db: Arc<dyn MusicIndex>,
|
2023-03-10 14:35:18 +00:00
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
playback_controller: Option<Playback>,
|
|
|
|
|
|
|
|
next_scan: Arc<RwLock<Instant>>,
|
|
|
|
|
|
|
|
scanner_handle: thread::JoinHandle<()>,
|
2023-02-11 17:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Core {
|
2023-02-26 03:17:00 +00:00
|
|
|
pub fn new(
|
|
|
|
db: Arc<dyn MusicIndex>,
|
2023-03-10 14:35:18 +00:00
|
|
|
scanner: Arc<dyn MusicScanner>,
|
2023-03-11 18:02:52 +00:00
|
|
|
) -> Flow<Core, FatalError, Error> {
|
2023-02-26 03:17:00 +00:00
|
|
|
let db = db;
|
2023-02-11 17:59:15 +00:00
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
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,
|
|
|
|
})
|
2023-03-10 14:35:18 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
/*
|
|
|
|
pub fn main_loop(&self) -> Flow<(), FatalError, Error> {
|
|
|
|
// let (scanner_tx, _scanner_rx) = channel();
|
2023-03-10 14:35:18 +00:00
|
|
|
loop {
|
2023-03-11 18:02:52 +00:00
|
|
|
println!("loop");
|
|
|
|
/*
|
2023-03-10 14:35:18 +00:00
|
|
|
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(()),
|
|
|
|
}
|
2023-03-11 18:02:52 +00:00
|
|
|
*/
|
|
|
|
thread::sleep(Duration::from_secs(1));
|
2023-03-10 14:35:18 +00:00
|
|
|
}
|
2023-02-11 17:59:15 +00:00
|
|
|
}
|
2023-03-11 18:02:52 +00:00
|
|
|
*/
|
2023-02-11 17:59:15 +00:00
|
|
|
|
2023-02-26 03:17:00 +00:00
|
|
|
pub fn list_tracks<'a>(&'a self) -> Flow<Vec<TrackInfo>, FatalError, Error> {
|
|
|
|
self.db.list_tracks().map_err(Error::DatabaseError)
|
|
|
|
}
|
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
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)));
|
2023-03-10 14:35:18 +00:00
|
|
|
ok(())
|
|
|
|
}
|
2023-03-11 18:02:52 +00:00
|
|
|
|
|
|
|
pub fn stop_playback<'a>(&'a mut self) {
|
|
|
|
match self.playback_controller {
|
|
|
|
Some(ref controller) => controller.stop(),
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
self.playback_controller = None;
|
|
|
|
}
|
2023-03-10 14:35:18 +00:00
|
|
|
}
|
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
/*
|
2023-03-10 14:35:18 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct CoreAPI {
|
|
|
|
control_tx: Arc<Mutex<Sender<ControlMsg>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CoreAPI {
|
2023-03-11 18:02:52 +00:00
|
|
|
pub async fn play_track(&self, id: TrackId) -> () {
|
2023-03-10 14:35:18 +00:00
|
|
|
self.control_tx
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.send(ControlMsg::PlayTrack(id))
|
2023-03-11 18:02:52 +00:00
|
|
|
.await
|
2023-03-10 14:35:18 +00:00
|
|
|
.unwrap()
|
2023-02-11 17:59:15 +00:00
|
|
|
}
|
|
|
|
}
|
2023-03-11 18:02:52 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
pub fn scan_loop(
|
|
|
|
scanner: Arc<dyn MusicScanner>,
|
|
|
|
db: Arc<dyn MusicIndex>,
|
|
|
|
next_scan: Arc<RwLock<Instant>>,
|
|
|
|
) {
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2023-02-11 17:59:15 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2023-02-26 03:17:00 +00:00
|
|
|
mod test {
|
|
|
|
use super::*;
|
2023-03-02 04:54:23 +00:00
|
|
|
use crate::{database::MemoryIndex, media::TrackId, scanner::factories::MockScanner};
|
2023-02-26 03:17:00 +00:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
2023-03-11 18:02:52 +00:00
|
|
|
/*
|
2023-02-26 03:17:00 +00:00
|
|
|
fn with_example_index<F>(f: F)
|
|
|
|
where
|
|
|
|
F: Fn(Core),
|
|
|
|
{
|
|
|
|
let index = MemoryIndex::new();
|
|
|
|
let scanner = MockScanner::new();
|
2023-03-10 14:35:18 +00:00
|
|
|
match Core::new(Arc::new(index), Arc::new(scanner)) {
|
|
|
|
Flow::Ok((core, api)) => {
|
2023-02-26 03:17:00 +00:00
|
|
|
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::<HashSet<TrackId>>();
|
|
|
|
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),
|
|
|
|
})
|
|
|
|
}
|
2023-03-11 18:02:52 +00:00
|
|
|
*/
|
2023-02-26 03:17:00 +00:00
|
|
|
}
|