Render a file list from the filesystem #24

Merged
savanni merged 9 commits from track-list into main 2023-02-26 03:17:01 +00:00
3 changed files with 116 additions and 104 deletions
Showing only changes of commit 86708ebdb2 - Show all commits

View File

@ -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)
}; };

View File

@ -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;

View File

@ -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(())
}
}