Skip to content

Commit

Permalink
Refactor scanning code
Browse files Browse the repository at this point in the history
  • Loading branch information
nukeop committed Nov 12, 2023
1 parent ababcea commit 37d35f8
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 106 deletions.
4 changes: 3 additions & 1 deletion packages/scanner/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ use std::fmt;
#[derive(Debug)]
pub struct ScannerError {
pub message: String,
pub path: String,
}

impl Error for ScannerError {}

impl ScannerError {
pub fn new(message: &str) -> ScannerError {
pub fn new(message: &str, path: &str) -> ScannerError {
ScannerError {
message: message.to_string(),
path: path.to_string(),
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions packages/scanner/src/js.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use neon::prelude::*;

use crate::metadata::AudioMetadata;

pub fn set_optional_field_str(
cx: &mut FunctionContext,
obj: &mut Handle<JsObject>,
Expand Down Expand Up @@ -53,3 +55,19 @@ pub fn set_optional_field_buffer(
}
}
}

pub fn set_properties_from_metadata(
cx: &mut FunctionContext,
obj: &mut Handle<JsObject>,
metadata: &AudioMetadata,
) {
set_optional_field_str(cx, obj, "artist", metadata.artist.clone());
set_optional_field_str(cx, obj, "title", metadata.title.clone());
set_optional_field_str(cx, obj, "album", metadata.album.clone());

set_optional_field_u32(cx, obj, "duration", metadata.duration);
set_optional_field_str(cx, obj, "thumbnail", metadata.thumbnail.clone());
set_optional_field_u32(cx, obj, "position", metadata.position);
set_optional_field_u32(cx, obj, "disc", metadata.disc);
set_optional_field_u32(cx, obj, "year", metadata.year);
}
173 changes: 86 additions & 87 deletions packages/scanner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,44 @@ mod local_track;
mod metadata;
mod scanner;
mod thumbnails;
use error::ScannerError;
use id3::Tag;
use js::{set_optional_field_str, set_optional_field_u32};
use js::set_properties_from_metadata;
use local_track::LocalTrack;
use neon::prelude::*;
use scanner::{extractor_from_path, visit_directory, visit_file};
use std::collections::LinkedList;
use thumbnails::create_thumbnails_dir;

fn handle_progress<'a>(
cx: &mut FunctionContext<'a>,
total_files_to_scan_num: usize,
index: usize,
filename: String,
on_progress_callback: Handle<JsFunction>,
) -> JsResult<'a, JsValue> {
let js_this = cx.undefined();
let args = vec![
cx.number(index as f64).upcast(),
cx.number(total_files_to_scan_num as f64).upcast(),
cx.string(filename.clone()).upcast(),
];
on_progress_callback.call(cx, js_this, args)
}

fn handle_error<'a>(
cx: &mut FunctionContext<'a>,
error: &ScannerError,
on_error_callback: Handle<JsFunction>,
) -> JsResult<'a, JsValue> {
let js_this = cx.undefined();
let on_error_args = vec![
cx.string(error.path.clone()).upcast(),
cx.string(error.message.clone()).upcast(),
];
on_error_callback.call(cx, js_this, on_error_args)
}

fn scan_folders(mut cx: FunctionContext) -> JsResult<JsArray> {
let folders: Handle<JsArray> = cx.argument(0)?;
let supported_formats: Handle<JsArray> = cx.argument(1)?;
Expand Down Expand Up @@ -61,95 +92,63 @@ fn scan_folders(mut cx: FunctionContext) -> JsResult<JsArray> {
}

// First, create a directory for thumbnails
create_thumbnails_dir(thumbnails_dir_str.as_str());
create_thumbnails_dir(thumbnails_dir_str.as_str()).unwrap();

// All folders have been scanned, now scan the files
// All directories have been scanned, now scan the files
total_files_to_scan_num = files_to_scan_queue.len();
while !files_to_scan_queue.is_empty() {
// Get the next file to scan
let file = files_to_scan_queue.pop_front().unwrap();

// Scan the file
let track = visit_file(
file.clone(),
extractor_from_path,
thumbnails_dir_str.as_str(),
);

if track.is_err() {
// Call the progress callback
let this = cx.undefined();
let args = vec![
cx.number((total_files_to_scan_num - files_to_scan_queue.len()) as f64)
.upcast(),
cx.number(total_files_to_scan_num as f64).upcast(),
cx.string(file.clone()).upcast(),
];
on_progress_callback.call(&mut cx, this, args)?;

let error = track.err().unwrap();
let error_string = cx.string(error.message);
let on_error_args = vec![cx.string(file.clone()).upcast(), error_string.upcast()];
on_error_callback.call(&mut cx, this, on_error_args)?;
continue;
let scanned_local_tracks: Vec<Result<LocalTrack, ScannerError>> = files_to_scan_queue
.iter()
.enumerate()
.map(|(index, file)| {
let result = visit_file(
file.clone(),
extractor_from_path,
thumbnails_dir_str.as_str(),
);

// Send progress back to JS
handle_progress(
&mut cx,
total_files_to_scan_num,
index,
file.clone(),
on_progress_callback.clone(),
)
.unwrap();
return result;
})
.collect();

scanned_local_tracks.iter().for_each(|track| match track {
Ok(track) => {
let len = result.len(&mut cx);
let mut track_js_object = JsObject::new(&mut cx);
let track_uuid_js_string = cx.string(track.uuid.clone());
track_js_object
.set(&mut cx, "uuid", track_uuid_js_string)
.unwrap();

set_properties_from_metadata(&mut cx, &mut track_js_object, &track.metadata);

let track_filename_js_string = cx.string(track.filename.clone());
track_js_object
.set(&mut cx, "filename", track_filename_js_string)
.unwrap();

let track_path_js_string = cx.string(track.path.clone());
track_js_object
.set(&mut cx, "path", track_path_js_string)
.unwrap();

let track_local = cx.boolean(true);
track_js_object.set(&mut cx, "local", track_local).unwrap();

result.set(&mut cx, len, track_js_object).unwrap();
}

let track = track.unwrap();

let len = result.len(&mut cx);
let mut track_js_object = JsObject::new(&mut cx);
let track_uuid_js_string = cx.string(track.uuid);
track_js_object.set(&mut cx, "uuid", track_uuid_js_string)?;

set_optional_field_str(
&mut cx,
&mut track_js_object,
"artist",
track.metadata.artist,
);
set_optional_field_str(&mut cx, &mut track_js_object, "title", track.metadata.title);
set_optional_field_str(&mut cx, &mut track_js_object, "album", track.metadata.album);

let track_duration_js_number = cx.number(track.metadata.duration);
track_js_object.set(&mut cx, "duration", track_duration_js_number)?;

set_optional_field_str(
&mut cx,
&mut track_js_object,
"thumbnail",
track.metadata.thumbnail,
);

set_optional_field_u32(
&mut cx,
&mut track_js_object,
"position",
track.metadata.position,
);
set_optional_field_u32(&mut cx, &mut track_js_object, "disc", track.metadata.disc);
set_optional_field_u32(&mut cx, &mut track_js_object, "year", track.metadata.year);

let track_filename_js_string = cx.string(track.filename);
track_js_object.set(&mut cx, "filename", track_filename_js_string)?;

let track_path_js_string = cx.string(track.path);
track_js_object.set(&mut cx, "path", track_path_js_string)?;

let track_local = cx.boolean(true);
track_js_object.set(&mut cx, "local", track_local)?;

result.set(&mut cx, len, track_js_object)?;

// Call the progress callback
let this = cx.undefined();
let args = vec![
cx.number((total_files_to_scan_num - files_to_scan_queue.len()) as f64)
.upcast(),
cx.number(total_files_to_scan_num as f64).upcast(),
cx.string(file.clone()).upcast(),
];
on_progress_callback.call(&mut cx, this, args)?;
}
Err(error) => {
handle_error(&mut cx, error, on_error_callback.clone()).unwrap();
}
});

Ok(result)
}
Expand Down
8 changes: 4 additions & 4 deletions packages/scanner/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub struct AudioMetadata {
pub artist: Option<String>,
pub title: Option<String>,
pub album: Option<String>,
pub duration: u32,
pub duration: Option<u32>,
pub disc: Option<u32>,
pub position: Option<u32>,
pub year: Option<u32>,
Expand All @@ -27,7 +27,7 @@ impl AudioMetadata {
artist: None,
title: None,
album: None,
duration: 0,
duration: None,
disc: None,
position: None,
year: None,
Expand Down Expand Up @@ -59,7 +59,7 @@ impl MetadataExtractor for Mp3MetadataExtractor {
metadata.artist = tag.artist().map(|s| s.to_string());
metadata.title = tag.title().map(|s| s.to_string());
metadata.album = tag.album().map(|s| s.to_string());
metadata.duration = tag.duration().unwrap_or(0);
metadata.duration = tag.duration();
metadata.position = tag.track();
metadata.disc = tag.disc();
metadata.year = tag.year().map(|s| s as u32);
Expand Down Expand Up @@ -107,7 +107,7 @@ impl MetadataExtractor for FlacMetadataExtractor {
metadata.artist = Self::extract_string_metadata(&tag, "ARTIST", Some("ALBUMARTIST"));
metadata.title = Self::extract_string_metadata(&tag, "TITLE", None);
metadata.album = Self::extract_string_metadata(&tag, "ALBUM", None);
metadata.duration = Self::extract_numeric_metadata(&tag, "LENGTH").unwrap_or(0);
metadata.duration = Self::extract_numeric_metadata(&tag, "LENGTH");
metadata.position = Self::extract_numeric_metadata(&tag, "TRACKNUMBER");
metadata.disc = Self::extract_numeric_metadata(&tag, "DISCNUMBER");
metadata.year = Self::extract_numeric_metadata(&tag, "DATE");
Expand Down
31 changes: 17 additions & 14 deletions packages/scanner/src/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ where
F: Fn(&str) -> Option<Box<dyn MetadataExtractor>>,
{
let extractor: Box<dyn MetadataExtractor> = extractor_provider(&path)
.ok_or_else(|| ScannerError::new(&format!("Unsupported file format: {}", path)))?;
.ok_or_else(|| ScannerError::new(&format!("Unsupported file format: {}", path), &path))?;
let metadata = extractor.extract_metadata(&path, thumbnails_dir);

match metadata {
Expand All @@ -46,9 +46,10 @@ where
filename: path.split("/").last().map(|s| s.to_string()).unwrap(),
path: path.clone(),
}),
Err(e) => Err(ScannerError {
message: format!("Error reading file: {}", e),
}),
Err(e) => Err(ScannerError::new(
&format!("Error reading file: {}", e),
&path,
)),
}
}

Expand Down Expand Up @@ -84,9 +85,9 @@ mod tests {
pub test_metadata: AudioMetadata,
}
impl TestMetadataExtractor {
pub fn new() -> TestMetadataExtractor {
pub fn new(metadata: AudioMetadata) -> TestMetadataExtractor {
TestMetadataExtractor {
test_metadata: AudioMetadata::new(),
test_metadata: metadata,
}
}
}
Expand All @@ -96,7 +97,13 @@ mod tests {
_path: &str,
_thumbnails_dir: &str,
) -> Result<AudioMetadata, MetadataError> {
Ok(AudioMetadataBuilder::default()
return Ok(self.test_metadata.clone());
}
}

pub fn test_extractor_from_path(_path: &str) -> Option<Box<dyn MetadataExtractor>> {
Some(Box::new(TestMetadataExtractor::new(
AudioMetadataBuilder::default()
.artist("Test Artist".to_string())
.title("Test Title".to_string())
.album("Test Album".to_string())
Expand All @@ -106,12 +113,8 @@ mod tests {
.year(2020)
.thumbnail("http://localhost:8080/thumbnails/0b/0b0b0b0b0b0b0b0b.webp".to_string())
.build()
.unwrap())
}
}

pub fn test_extractor_from_path(_path: &str) -> Option<Box<dyn MetadataExtractor>> {
Some(Box::new(TestMetadataExtractor::new()))
.unwrap(),
)))
}

#[test]
Expand All @@ -123,7 +126,7 @@ mod tests {
assert_eq!(local_track.metadata.artist, Some("Test Artist".to_string()));
assert_eq!(local_track.metadata.title, Some("Test Title".to_string()));
assert_eq!(local_track.metadata.album, Some("Test Album".to_string()));
assert_eq!(local_track.metadata.duration, 10);
assert_eq!(local_track.metadata.duration, Some(10));
assert_eq!(local_track.metadata.position, Some(1));
assert_eq!(local_track.metadata.disc, Some(1));
assert_eq!(local_track.metadata.year, Some(2020));
Expand Down

0 comments on commit 37d35f8

Please sign in to comment.