Start setting up an abstract music scanner interface
This commit is contained in:
parent
5c61a48006
commit
86708ebdb2
|
@ -15,20 +15,6 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum ScannerError {
|
|
||||||
#[error("Cannot scan {0}")]
|
|
||||||
CannotScan(PathBuf),
|
|
||||||
#[error("IO error {0}")]
|
|
||||||
IO(std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for ScannerError {
|
|
||||||
fn from(err: std::io::Error) -> Self {
|
|
||||||
Self::IO(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ControlMsg {
|
pub enum ControlMsg {
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
@ -53,87 +39,6 @@ pub struct Core {
|
||||||
control_tx: Sender<ControlMsg>,
|
control_tx: Sender<ControlMsg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_frequency() -> Duration {
|
|
||||||
Duration::from_secs(60)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FileScanner {
|
|
||||||
db: Arc<dyn MusicIndex>,
|
|
||||||
control_rx: Receiver<ControlMsg>,
|
|
||||||
tracker_tx: Sender<TrackMsg>,
|
|
||||||
next_scan: Instant,
|
|
||||||
music_directories: Vec<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileScanner {
|
|
||||||
fn new(
|
|
||||||
db: Arc<dyn MusicIndex>,
|
|
||||||
roots: Vec<PathBuf>,
|
|
||||||
control_rx: Receiver<ControlMsg>,
|
|
||||||
tracker_tx: Sender<TrackMsg>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
db,
|
|
||||||
control_rx,
|
|
||||||
tracker_tx,
|
|
||||||
next_scan: Instant::now(),
|
|
||||||
music_directories: roots,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scan(&mut self) {
|
|
||||||
loop {
|
|
||||||
match self.control_rx.recv_timeout(Duration::from_millis(100)) {
|
|
||||||
Ok(ControlMsg::Exit) => return,
|
|
||||||
Err(RecvTimeoutError::Timeout) => (),
|
|
||||||
Err(RecvTimeoutError::Disconnected) => return,
|
|
||||||
}
|
|
||||||
if Instant::now() >= self.next_scan {
|
|
||||||
for root in self.music_directories.iter() {
|
|
||||||
self.scan_dir(vec![root.clone()]);
|
|
||||||
}
|
|
||||||
self.next_scan = Instant::now() + scan_frequency();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scan_dir(&self, mut paths: Vec<PathBuf>) -> Flow<(), FatalError, ScannerError> {
|
|
||||||
while let Some(dir) = paths.pop() {
|
|
||||||
println!("scanning {:?}", dir);
|
|
||||||
return_error!(self.scan_dir_(&mut paths, dir));
|
|
||||||
}
|
|
||||||
ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scan_dir_(
|
|
||||||
&self,
|
|
||||||
paths: &mut Vec<PathBuf>,
|
|
||||||
dir: PathBuf,
|
|
||||||
) -> Flow<(), FatalError, ScannerError> {
|
|
||||||
let dir_iter = return_error!(Flow::from(dir.read_dir().map_err(ScannerError::from)));
|
|
||||||
for entry in dir_iter {
|
|
||||||
match entry {
|
|
||||||
Ok(entry) if entry.path().is_dir() => paths.push(entry.path()),
|
|
||||||
Ok(entry) => {
|
|
||||||
let _ = return_fatal!(self.scan_file(entry.path()).or_else(|err| {
|
|
||||||
println!("scan_file failed: {:?}", err);
|
|
||||||
ok::<(), FatalError, ScannerError>(())
|
|
||||||
}));
|
|
||||||
()
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
println!("scan_dir could not read path: ({:?})", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scan_file(&self, path: PathBuf) -> Flow<(), FatalError, ScannerError> {
|
|
||||||
ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn new(db: Arc<dyn MusicIndex>) -> Flow<Core, FatalError, Error> {
|
pub fn new(db: Arc<dyn MusicIndex>) -> Flow<Core, FatalError, Error> {
|
||||||
let (control_tx, control_rx) = channel::<ControlMsg>();
|
let (control_tx, control_rx) = channel::<ControlMsg>();
|
||||||
|
@ -141,15 +46,7 @@ impl Core {
|
||||||
let (track_handle, track_rx) = {
|
let (track_handle, track_rx) = {
|
||||||
let (track_tx, track_rx) = channel();
|
let (track_tx, track_rx) = channel();
|
||||||
let db = db.clone();
|
let db = db.clone();
|
||||||
let track_handle = thread::spawn(move || {
|
let track_handle = thread::spawn(move || {});
|
||||||
FileScanner::new(
|
|
||||||
db,
|
|
||||||
vec![PathBuf::from("/home/savanni/Music/")],
|
|
||||||
control_rx,
|
|
||||||
track_tx,
|
|
||||||
)
|
|
||||||
.scan();
|
|
||||||
});
|
|
||||||
(track_handle, track_rx)
|
(track_handle, track_rx)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
|
pub mod music_scanner;
|
||||||
use database::DatabaseError;
|
use database::DatabaseError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
use crate::{
|
||||||
|
core::{ControlMsg, TrackMsg},
|
||||||
|
database::MusicIndex,
|
||||||
|
FatalError,
|
||||||
|
};
|
||||||
|
use flow::{ok, return_error, return_fatal, Flow};
|
||||||
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{
|
||||||
|
mpsc::{Receiver, RecvTimeoutError, Sender},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
fn scan_frequency() -> Duration {
|
||||||
|
Duration::from_secs(60)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ScannerError {
|
||||||
|
#[error("Cannot scan {0}")]
|
||||||
|
CannotScan(PathBuf),
|
||||||
|
#[error("IO error {0}")]
|
||||||
|
IO(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for ScannerError {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
Self::IO(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MusicScanner {
|
||||||
|
fn scan(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FileScanner {
|
||||||
|
db: Arc<dyn MusicIndex>,
|
||||||
|
control_rx: Receiver<ControlMsg>,
|
||||||
|
tracker_tx: Sender<TrackMsg>,
|
||||||
|
next_scan: Instant,
|
||||||
|
music_directories: Vec<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileScanner {
|
||||||
|
fn new(
|
||||||
|
db: Arc<dyn MusicIndex>,
|
||||||
|
roots: Vec<PathBuf>,
|
||||||
|
control_rx: Receiver<ControlMsg>,
|
||||||
|
tracker_tx: Sender<TrackMsg>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
db,
|
||||||
|
control_rx,
|
||||||
|
tracker_tx,
|
||||||
|
next_scan: Instant::now(),
|
||||||
|
music_directories: roots,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan(&mut self) {
|
||||||
|
loop {
|
||||||
|
match self.control_rx.recv_timeout(Duration::from_millis(100)) {
|
||||||
|
Ok(ControlMsg::Exit) => return,
|
||||||
|
Err(RecvTimeoutError::Timeout) => (),
|
||||||
|
Err(RecvTimeoutError::Disconnected) => return,
|
||||||
|
}
|
||||||
|
if Instant::now() >= self.next_scan {
|
||||||
|
for root in self.music_directories.iter() {
|
||||||
|
self.scan_dir(vec![root.clone()]);
|
||||||
|
}
|
||||||
|
self.next_scan = Instant::now() + scan_frequency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_dir(&self, mut paths: Vec<PathBuf>) -> Flow<(), FatalError, ScannerError> {
|
||||||
|
while let Some(dir) = paths.pop() {
|
||||||
|
println!("scanning {:?}", dir);
|
||||||
|
return_error!(self.scan_dir_(&mut paths, dir));
|
||||||
|
}
|
||||||
|
ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_dir_(
|
||||||
|
&self,
|
||||||
|
paths: &mut Vec<PathBuf>,
|
||||||
|
dir: PathBuf,
|
||||||
|
) -> Flow<(), FatalError, ScannerError> {
|
||||||
|
let dir_iter = return_error!(Flow::from(dir.read_dir().map_err(ScannerError::from)));
|
||||||
|
for entry in dir_iter {
|
||||||
|
match entry {
|
||||||
|
Ok(entry) if entry.path().is_dir() => paths.push(entry.path()),
|
||||||
|
Ok(entry) => {
|
||||||
|
let _ = return_fatal!(self.scan_file(entry.path()).or_else(|err| {
|
||||||
|
println!("scan_file failed: {:?}", err);
|
||||||
|
ok::<(), FatalError, ScannerError>(())
|
||||||
|
}));
|
||||||
|
()
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("scan_dir could not read path: ({:?})", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_file(&self, path: PathBuf) -> Flow<(), FatalError, ScannerError> {
|
||||||
|
ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue