diff --git a/apps/indexer/src/scanner.rs b/apps/indexer/src/scanner.rs index 0699e25..30deccf 100644 --- a/apps/indexer/src/scanner.rs +++ b/apps/indexer/src/scanner.rs @@ -165,6 +165,13 @@ pub async fn scan_library_discovery( continue; } + // Skip macOS Apple Double resource fork files (._*) + let file_name_raw = entry.file_name().to_string_lossy(); + if file_name_raw.starts_with("._") { + trace!("[SCAN] Skipping macOS resource fork: {}", path.display()); + continue; + } + // Check if this file is under a skipped dir let under_skipped = skipped_dir_prefixes .iter() diff --git a/crates/parsers/src/lib.rs b/crates/parsers/src/lib.rs index 20536fd..0394856 100644 --- a/crates/parsers/src/lib.rs +++ b/crates/parsers/src/lib.rs @@ -190,7 +190,25 @@ fn analyze_cbr(path: &Path) -> Result<(i32, Vec)> { let mut image_names: Vec = { let archive = unrar::Archive::new(path) .open_for_listing() - .map_err(|e| anyhow::anyhow!("unrar listing failed for {}: {}", path.display(), e))?; + .map_err(|e| anyhow::anyhow!("unrar listing failed for {}: {}", path.display(), e)); + // Some .cbr files are actually ZIP archives with wrong extension — fallback to CBZ parser + let archive = match archive { + Ok(a) => a, + Err(e) => { + let e_str = e.to_string(); + if e_str.contains("Not a RAR archive") || e_str.contains("bad archive") { + return analyze_cbz(path).map_err(|zip_err| { + anyhow::anyhow!( + "not a RAR archive and ZIP fallback also failed for {}: RAR={}, ZIP={}", + path.display(), + e_str, + zip_err + ) + }); + } + return Err(e); + } + }; let mut names = Vec::new(); for entry in archive { let entry = entry.map_err(|e| anyhow::anyhow!("unrar entry error: {}", e))?; @@ -297,7 +315,18 @@ fn parse_cbz_page_count(path: &Path) -> Result { fn parse_cbr_page_count(path: &Path) -> Result { let archive = unrar::Archive::new(path) .open_for_listing() - .map_err(|e| anyhow::anyhow!("unrar listing failed for {}: {}", path.display(), e))?; + .map_err(|e| anyhow::anyhow!("unrar listing failed for {}: {}", path.display(), e)); + // Some .cbr files are actually ZIP archives with wrong extension — fallback to CBZ parser + let archive = match archive { + Ok(a) => a, + Err(e) => { + let e_str = e.to_string(); + if e_str.contains("Not a RAR archive") || e_str.contains("bad archive") { + return parse_cbz_page_count(path); + } + return Err(e); + } + }; let count = archive .filter(|r| { r.as_ref()