use std::path::{Path, PathBuf};
use crate::iso;
use super::ScanItem;
pub struct ScanResult {
pub items: Vec<ScanItem>,
pub errors: u32,
}
pub fn expand(files: &[PathBuf]) -> ScanResult {
let mut items = Vec::with_capacity(files.len());
let mut errors = 0u32;
for file in files {
if iso::is_disc_image(file) {
let iso_name = file.file_name().unwrap_or_default().to_string_lossy();
let analysis = match iso::analyze_disc(file) {
Ok(a) => a,
Err(e) => {
eprintln!(" skip: {}: {}", iso_name, e);
errors += 1;
continue;
}
};
if let iso::DiscType::Encrypted { ref reason } = analysis.disc_type {
eprintln!(" skip: {}: {}", iso_name, reason);
continue;
}
if analysis.main_feature.is_empty() {
eprintln!(" skip: {}: no media files inside", iso_name);
continue;
}
if let Some(groups) = analysis.multi_title_groups.as_ref() {
eprintln!(
" iso: {} ({:?}, {} titles, {} extras)",
iso_name,
analysis.disc_type,
groups.len(),
analysis.extras.len(),
);
for (i, group) in groups.iter().enumerate() {
if group.is_empty() {
continue;
}
let suffix = format!("title{:02}", i + 1);
items.push(iso_item_from_group(file, group, Some(suffix)));
}
continue;
}
eprintln!(
" iso: {} ({:?}, {} main feature files, {} extras)",
iso_name,
analysis.disc_type,
analysis.main_feature.len(),
analysis.extras.len(),
);
items.push(iso_item_from_group(file, &analysis.main_feature, None));
continue;
}
items.push(ScanItem {
file: file.clone(),
iso_path: None,
inner_path: None,
inner_paths: None,
title_suffix: None,
});
}
ScanResult { items, errors }
}
fn iso_item_from_group(
iso_file: &Path,
group: &[iso::IsoMediaFile],
suffix: Option<String>,
) -> ScanItem {
let paths: Vec<String> = group.iter().map(|f| f.path.clone()).collect();
let (inner_paths, inner_path) = if paths.len() == 1 {
(None, Some(paths[0].clone()))
} else {
(Some(paths.clone()), Some(paths[0].clone()))
};
ScanItem {
file: iso_file.to_path_buf(),
iso_path: Some(iso_file.to_path_buf()),
inner_path,
inner_paths,
title_suffix: suffix,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn expand_regular_files_pass_through_unchanged() {
let dir = tempdir().unwrap();
let mkv = dir.path().join("video.mkv");
let mp4 = dir.path().join("clip.mp4");
fs::write(&mkv, b"x").unwrap();
fs::write(&mp4, b"x").unwrap();
let r = expand(&[mkv.clone(), mp4.clone()]);
assert_eq!(r.items.len(), 2);
assert_eq!(r.errors, 0);
assert_eq!(r.items[0].file, mkv);
assert!(r.items[0].iso_path.is_none());
assert!(r.items[0].inner_paths.is_none());
assert!(r.items[0].title_suffix.is_none());
}
#[test]
fn expand_skips_non_iso_when_iso_path_present() {
let dir = tempdir().unwrap();
let mkv = dir.path().join("normal.mkv");
fs::write(&mkv, b"x").unwrap();
let r = expand(&[mkv]);
assert!(r.items[0].iso_path.is_none());
assert!(r.items[0].inner_path.is_none());
}
#[test]
fn iso_item_from_group_single_file_uses_inner_path_not_paths() {
let iso = Path::new("/media/Movie.iso");
let group = vec![iso::IsoMediaFile {
path: "BDMV/STREAM/00000.M2TS".to_string(),
size: 1000,
}];
let item = iso_item_from_group(iso, &group, None);
assert_eq!(item.file, Path::new("/media/Movie.iso"));
assert_eq!(item.iso_path.as_deref(), Some(iso));
assert_eq!(item.inner_path.as_deref(), Some("BDMV/STREAM/00000.M2TS"));
assert!(
item.inner_paths.is_none(),
"single-file ISO must not populate inner_paths"
);
assert!(item.title_suffix.is_none());
}
#[test]
fn iso_item_from_group_multi_file_populates_inner_paths() {
let iso = Path::new("/media/Movie.iso");
let group = vec![
iso::IsoMediaFile {
path: "VIDEO_TS/VTS_01_1.VOB".to_string(),
size: 1000,
},
iso::IsoMediaFile {
path: "VIDEO_TS/VTS_01_2.VOB".to_string(),
size: 1000,
},
];
let item = iso_item_from_group(iso, &group, None);
assert_eq!(
item.inner_path.as_deref(),
Some("VIDEO_TS/VTS_01_1.VOB"),
"inner_path must be the first file (the representative for probing)"
);
let paths = item.inner_paths.as_ref().expect("multi-file → Some");
assert_eq!(paths.len(), 2);
assert_eq!(paths[0], "VIDEO_TS/VTS_01_1.VOB");
assert_eq!(paths[1], "VIDEO_TS/VTS_01_2.VOB");
}
#[test]
fn iso_item_from_group_carries_title_suffix() {
let iso = Path::new("/media/Disc.iso");
let group = vec![iso::IsoMediaFile {
path: "VIDEO_TS/VTS_03_1.VOB".to_string(),
size: 1,
}];
let item = iso_item_from_group(iso, &group, Some("title03".to_string()));
assert_eq!(item.title_suffix.as_deref(), Some("title03"));
}
}