use crate::cache::{Cache, FileMetadata, FileTreeNode}; use anyhow::Result; use std::path::{Path, PathBuf}; use walkdir::WalkDir; const AUDIO_EXTENSIONS: &[&str] = &["mp3", "flac", "wav", "ogg", "m4a", "aac", "opus", "wma"]; const VIDEO_EXTENSIONS: &[&str] = &["mp4", "mkv", "avi", "mov", "wmv", "flv", "webm", "m4v"]; pub fn is_media_file(path: &Path) -> bool { if let Some(ext) = path.extension() { let ext_str = ext.to_string_lossy().to_lowercase(); AUDIO_EXTENSIONS.contains(&ext_str.as_str()) || VIDEO_EXTENSIONS.contains(&ext_str.as_str()) } else { false } } pub fn is_audio_file(path: &Path) -> bool { if let Some(ext) = path.extension() { let ext_str = ext.to_string_lossy().to_lowercase(); AUDIO_EXTENSIONS.contains(&ext_str.as_str()) } else { false } } pub fn is_video_file(path: &Path) -> bool { if let Some(ext) = path.extension() { let ext_str = ext.to_string_lossy().to_lowercase(); VIDEO_EXTENSIONS.contains(&ext_str.as_str()) } else { false } } pub fn scan_directory(root_path: &Path) -> Result { let name = root_path .file_name() .unwrap_or_default() .to_string_lossy() .to_string(); let mut node = FileTreeNode { name, path: root_path.to_path_buf(), is_dir: true, children: Vec::new(), metadata: None, }; let mut entries: Vec<_> = WalkDir::new(root_path) .max_depth(1) .min_depth(1) .into_iter() .filter_map(|e| e.ok()) .collect(); entries.sort_by_key(|e| e.path().to_path_buf()); for entry in entries { let path = entry.path(); if entry.file_type().is_dir() { // Recursively scan subdirectories if let Ok(child_node) = scan_directory(path) { node.children.push(child_node); } } else if is_media_file(path) { // Add media file let file_name = path .file_name() .unwrap_or_default() .to_string_lossy() .to_string(); let size = entry.metadata().map(|m| m.len()).unwrap_or(0); let metadata = FileMetadata { path: path.to_path_buf(), size, is_video: is_video_file(path), is_audio: is_audio_file(path), }; let file_node = FileTreeNode { name: file_name, path: path.to_path_buf(), is_dir: false, children: Vec::new(), metadata: Some(metadata), }; node.children.push(file_node); } } Ok(node) } pub fn scan_paths(paths: &[PathBuf]) -> Result { let mut cache = Cache::new(); for path in paths { if path.exists() { tracing::info!("Scanning path: {:?}", path); let tree_node = scan_directory(path)?; // Collect all metadata from the tree collect_metadata(&tree_node, &mut cache); cache.file_tree.push(tree_node); } else { tracing::warn!("Path does not exist: {:?}", path); } } Ok(cache) } fn collect_metadata(node: &FileTreeNode, cache: &mut Cache) { if let Some(ref metadata) = node.metadata { cache.metadata.insert(node.path.clone(), metadata.clone()); } for child in &node.children { collect_metadata(child, cache); } }