use std::fs; use std::path::Path; use std::sync::Arc; use ::image::GenericImageView; use tokio::task; use uuid::Uuid; use crate::config::Config; use crate::db::Database; use crate::models::*; /// 批量插入图片并设置封面的辅助函数 async fn batch_insert_images_and_set_cover( db: &Database, images_to_insert: Vec, album_id: Uuid, _album_path: &Path, _folder_name: &str, ) -> Result> { if images_to_insert.is_empty() { return Ok(0); } println!(" 批量插入 {} 张图片...", images_to_insert.len()); match db.add_images(images_to_insert).await { Ok(ids) => { println!(" 批量插入成功,共插入 {} 张图片", ids.len()); if let Some(first_image_id) = ids.first() { println!(" 设置第一张图片为封面图片 (ID: {})...", first_image_id); if let Err(e) = db.update_album_cover(album_id, *first_image_id).await { eprintln!(" 更新相册封面失败: {}", e); } if let Ok(Some(first_image)) = db.get_image(*first_image_id).await { println!(" 封面图片已设置: {}", first_image.filename); } } Ok(ids.len()) } Err(e) => { eprintln!(" 批量插入失败: {}", e); Ok(0) } } } pub async fn import_existing_data(config: &Config, db: Arc, _task_queue: Option>) -> Result<(), Box> { println!("开始扫描图片目录并导入新相册..."); let albums_dir = Path::new(&config.storage.images_dir); if !albums_dir.exists() { println!("albums目录不存在,跳过导入"); return Ok(()); } let existing_albums = db.get_all_albums().await?; let existing_titles: Vec = existing_albums.iter() .map(|album| album.document.to_lowercase()) .collect(); println!("数据库中现有相册数量: {}", existing_albums.len()); let mut reimported_count = 0; let mut deleted_count = 0; for album in &existing_albums { let album_folder_path = albums_dir.join(&album.document); if !album_folder_path.exists() { println!("🗑️ 相册文件夹不存在,删除相册: {}", album.document); if let Err(e) = db.delete_album(album.id).await { eprintln!("❌ 删除相册失败: {}", e); } else { deleted_count += 1; } continue; } let image_count = db.get_album_image_count(album.id).await.unwrap_or(0); if image_count == 0 { println!("🔄 重新导入相册: {} (图片数量: 0)", album.document); match reimport_single_album(&db, &album.document, &album_folder_path, album.id).await { Ok(count) => { if count > 0 { reimported_count += 1; println!("✅ 相册 {} 重新导入成功,导入 {} 张图片", album.document, count); } else { println!("⚠️ 相册 {} 重新导入但没有找到图片", album.document); } } Err(e) => { eprintln!("❌ 相册 {} 重新导入失败: {}", album.document, e); } } } } if reimported_count > 0 || deleted_count > 0 { println!("🎯 重新导入完成: 重新导入 {} 个相册,删除 {} 个相册", reimported_count, deleted_count); } let entries: Vec<_> = fs::read_dir(albums_dir)?.collect::, _>>()?; let mut album_folders = Vec::new(); for entry in entries { let path = entry.path(); if path.is_dir() { if let Some(folder_name) = path.file_name().and_then(|s| s.to_str()) { if existing_titles.contains(&folder_name.to_lowercase()) { continue; } album_folders.push((folder_name.to_string(), path)); } } } println!("发现 {} 个新相册需要导入", album_folders.len()); if album_folders.is_empty() { println!("没有新相册需要导入"); return Ok(()); } println!("📁 开始导入新相册..."); let mut total_imported = 0; let mut successful_albums = 0; for (folder_name, path) in album_folders { println!("📁 开始导入相册: {}", folder_name); match import_single_album(&db, &folder_name, &path).await { Ok(count) => { if count > 0 { successful_albums += 1; total_imported += count; println!("✅ 相册 {} 导入完成,共导入 {} 张图片", folder_name, count); } else { println!("⚠️ 相册 {} 没有图片需要导入", folder_name); } } Err(e) => { eprintln!("❌ 相册 {} 导入失败: {}", folder_name, e); } } } println!("🎉 导入完成!成功导入 {} 个相册,共 {} 张图片", successful_albums, total_imported); Ok(()) } async fn import_single_album(db: &Database, folder_name: &str, path: &Path) -> Result> { println!("📁 开始导入相册: {}", folder_name); let index_file_path = path.join("index.json"); let album_title = if index_file_path.exists() { match fs::read_to_string(&index_file_path) { Ok(content) => { match serde_json::from_str::(&content) { Ok(index) => { println!(" 从index.json读取相册标题: {}", index.title); index.title } Err(e) => { println!(" 解析index.json失败: {}, 使用文件夹名作为标题", e); folder_name.to_string() } } } Err(e) => { println!(" 读取index.json失败: {}, 使用文件夹名作为标题", e); folder_name.to_string() } } } else { println!(" index.json不存在,使用文件夹名作为标题"); folder_name.to_string() }; let new_album = CreateAlbum { title: album_title.clone(), document: folder_name.to_string(), description: Some(format!("从文件夹 {} 自动导入的相册", folder_name)), }; let album_id = db.create_album(new_album).await?; println!(" 创建相册: {} (ID: {})", album_title, album_id); let image_urls = if index_file_path.exists() { match fs::read_to_string(&index_file_path) { Ok(content) => { match serde_json::from_str::(&content) { Ok(index) => { println!(" 从index.json读取到 {} 个图片URL", index.images.len()); Some(index.images) } Err(_) => None } } Err(_) => None } } else { None }; let image_files = scan_image_files(path, folder_name)?; if image_files.is_empty() { println!(" 相册 {} 中没有找到图片文件", folder_name); return Ok(0); } println!(" 发现 {} 张图片,开始处理...", image_files.len()); let mut image_tasks = Vec::new(); for (index, (filename, image_path)) in image_files.iter().enumerate() { let image_url = image_urls.as_ref().and_then(|urls| { if urls.len() > index { Some(urls[index].clone()) } else { None } }); let filename_clone = filename.clone(); let image_path_clone = image_path.clone(); let album_id_clone = album_id; let folder_name_clone = folder_name.to_string(); let task = task::spawn_blocking(move || { let (file_size, width, height) = match get_image_info(&image_path_clone) { Ok(info) => info, Err(e) => { eprintln!("获取图片信息失败: {} - {}", image_path_clone.display(), e); (0, 0, 0) } }; CreateImage { album_id: album_id_clone, filename: format!("{}/{}", folder_name_clone, filename_clone), original_url: image_url, title: Some(format!("图片 {}", index + 1)), description: Some(format!("从文件夹 {} 导入的图片", folder_name_clone)), file_size, width, height, mime_type: match image_path_clone.extension().and_then(|s| s.to_str()).unwrap_or("jpg").to_lowercase().as_str() { "jpg" | "jpeg" => "image/jpeg".to_string(), "png" => "image/png".to_string(), "gif" => "image/gif".to_string(), "webp" => "image/webp".to_string(), "bmp" => "image/bmp".to_string(), _ => "image/jpeg".to_string(), }, } }); image_tasks.push(task); } let mut images_to_insert = Vec::new(); for task in image_tasks { match task.await { Ok(image) => images_to_insert.push(image), Err(e) => eprintln!("图片处理任务失败: {}", e), } } batch_insert_images_and_set_cover(&db, images_to_insert, album_id, path, folder_name).await } pub(crate) fn scan_image_files(path: &Path, _folder_name: &str) -> Result, Box> { let mut image_files = Vec::new(); for entry in fs::read_dir(path)? { let entry = entry?; let image_path = entry.path(); if image_path.is_file() { if let Some(extension) = image_path.extension().and_then(|s| s.to_str()) { let valid_extensions = ["jpg", "jpeg", "png", "gif", "webp", "bmp"]; if valid_extensions.contains(&extension.to_lowercase().as_str()) { let filename = image_path.file_name() .and_then(|s| s.to_str()) .unwrap_or("unknown.jpg") .to_string(); image_files.push((filename, image_path)); } } } } image_files.sort_by(|a, b| { let num_a = a.0.chars() .take_while(|c| c.is_ascii_digit()) .collect::() .parse::() .unwrap_or(0); let num_b = b.0.chars() .take_while(|c| c.is_ascii_digit()) .collect::() .parse::() .unwrap_or(0); num_a.cmp(&num_b) }); Ok(image_files) } fn get_image_info(image_path: &Path) -> Result<(i64, i32, i32), Box> { let metadata = fs::metadata(image_path)?; let file_size = metadata.len() as i64; let img = ::image::open(image_path)?; let (width, height) = img.dimensions(); Ok((file_size, width as i32, height as i32)) } async fn reimport_single_album( db: &Database, folder_name: &str, path: &Path, album_id: Uuid, ) -> Result> { println!("📁 重新导入相册: {} (ID: {})", folder_name, album_id); let index_file_path = path.join("index.json"); let album_title = if index_file_path.exists() { match fs::read_to_string(&index_file_path) { Ok(content) => { match serde_json::from_str::(&content) { Ok(index) => index.title, Err(_) => folder_name.to_string(), } } Err(_) => folder_name.to_string(), } } else { folder_name.to_string() }; println!(" 相册标题: {}", album_title); let mut images_to_insert = Vec::new(); let image_extensions = ["jpg", "jpeg", "png", "gif", "webp", "bmp"]; let entries: Vec<_> = fs::read_dir(path)?.collect::, _>>()?; for entry in entries { let file_path = entry.path(); if file_path.is_file() { if let Some(extension) = file_path.extension().and_then(|ext| ext.to_str()) { if image_extensions.contains(&extension.to_lowercase().as_str()) { if let Some(filename) = file_path.file_name().and_then(|s| s.to_str()) { if filename == "index.json" { continue; } let (file_size, width, height) = match get_image_info(&file_path) { Ok(info) => info, Err(e) => { eprintln!(" 获取图片信息失败 {}: {}", filename, e); continue; } }; let image = CreateImage { album_id, filename: format!("{}/{}", folder_name, filename), original_url: Some(format!("/images/{}/{}", folder_name, filename)), title: Some(filename.to_string()), description: Some(String::new()), file_size, width, height, mime_type: match extension.to_lowercase().as_str() { "jpg" | "jpeg" => "image/jpeg".to_string(), "png" => "image/png".to_string(), "gif" => "image/gif".to_string(), "webp" => "image/webp".to_string(), "bmp" => "image/bmp".to_string(), _ => "application/octet-stream".to_string(), }, }; images_to_insert.push(image); } } } } } println!(" 发现 {} 张图片需要导入", images_to_insert.len()); batch_insert_images_and_set_cover(&db, images_to_insert, album_id, path, folder_name).await }