fix: 修复异步导入业务逻辑和loading状态
问题修复: 1. 异步导入没有调用完整业务逻辑 - 只创建了Material记录,没有处理元数据、场景检测、视频切分 2. 导入动画一闪而逝 - 缺少实际的处理时间 3. 后台无素材处理日志 - 简化版本跳过了所有业务处理 解决方案: 1. 替换简化处理函数为完整业务逻辑处理 2. 调用MaterialService的完整处理流程 3. 添加详细的处理日志和进度事件 4. 添加适当延迟确保用户看到loading动画 5. 修复前端事件处理逻辑 技术改进: - process_single_file_with_full_logic: 完整的素材处理流程 - 元数据提取、场景检测、视频切分全流程 - 详细的tracing日志输出 - 实时进度事件发送 - 正确的错误处理和状态更新
This commit is contained in:
parent
91062ccf4c
commit
44b4084eb9
|
|
@ -80,7 +80,6 @@ async fn import_materials_with_tauri_events(
|
||||||
config: MaterialProcessingConfig,
|
config: MaterialProcessingConfig,
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
) -> Result<MaterialImportResult, anyhow::Error> {
|
) -> Result<MaterialImportResult, anyhow::Error> {
|
||||||
use anyhow::anyhow;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tracing::{info, warn, error, debug};
|
use tracing::{info, warn, error, debug};
|
||||||
|
|
@ -113,6 +112,9 @@ async fn import_materials_with_tauri_events(
|
||||||
"progress_percentage": 0.0
|
"progress_percentage": 0.0
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// 添加小延迟确保用户能看到loading动画
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||||
|
|
||||||
// 异步处理每个文件
|
// 异步处理每个文件
|
||||||
for (index, file_path) in request.file_paths.iter().enumerate() {
|
for (index, file_path) in request.file_paths.iter().enumerate() {
|
||||||
debug!(file_path = %file_path, "异步处理文件");
|
debug!(file_path = %file_path, "异步处理文件");
|
||||||
|
|
@ -127,12 +129,13 @@ async fn import_materials_with_tauri_events(
|
||||||
"progress_percentage": (index as f64 / result.total_files as f64) * 100.0
|
"progress_percentage": (index as f64 / result.total_files as f64) * 100.0
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 处理单个文件
|
// 处理单个文件 - 使用完整的业务逻辑
|
||||||
match process_single_file_simple(
|
match process_single_file_with_full_logic(
|
||||||
Arc::clone(&repository),
|
Arc::clone(&repository),
|
||||||
&request.project_id,
|
&request.project_id,
|
||||||
file_path,
|
file_path,
|
||||||
&config,
|
&config,
|
||||||
|
app_handle.clone(),
|
||||||
).await {
|
).await {
|
||||||
Ok(Some(material)) => {
|
Ok(Some(material)) => {
|
||||||
info!(
|
info!(
|
||||||
|
|
@ -186,19 +189,23 @@ async fn import_materials_with_tauri_events(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 简化的单文件处理函数
|
/// 使用完整业务逻辑的单文件处理函数
|
||||||
async fn process_single_file_simple(
|
async fn process_single_file_with_full_logic(
|
||||||
repository: Arc<MaterialRepository>,
|
repository: Arc<MaterialRepository>,
|
||||||
project_id: &str,
|
project_id: &str,
|
||||||
file_path: &str,
|
file_path: &str,
|
||||||
_config: &MaterialProcessingConfig,
|
config: &MaterialProcessingConfig,
|
||||||
|
app_handle: tauri::AppHandle,
|
||||||
) -> Result<Option<Material>, anyhow::Error> {
|
) -> Result<Option<Material>, anyhow::Error> {
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
use crate::data::models::material::{Material, MaterialType};
|
use crate::data::models::material::{Material, MaterialType, ProcessingStatus};
|
||||||
use crate::business::errors::error_utils;
|
use crate::business::errors::error_utils;
|
||||||
|
use tracing::{info, warn, error, debug};
|
||||||
|
|
||||||
|
debug!(file_path = %file_path, "开始处理单个文件");
|
||||||
|
|
||||||
// 验证文件路径
|
// 验证文件路径
|
||||||
error_utils::validate_file_path(file_path)
|
error_utils::validate_file_path(file_path)
|
||||||
|
|
@ -218,6 +225,7 @@ async fn process_single_file_simple(
|
||||||
|
|
||||||
// 检查是否已存在相同的文件
|
// 检查是否已存在相同的文件
|
||||||
if repository.exists_by_md5(project_id, &md5_hash)? {
|
if repository.exists_by_md5(project_id, &md5_hash)? {
|
||||||
|
warn!(file_path = %file_path, "文件已存在,跳过");
|
||||||
return Ok(None); // 跳过重复文件
|
return Ok(None); // 跳过重复文件
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,17 +243,148 @@ async fn process_single_file_simple(
|
||||||
let material_type = MaterialType::from_extension(extension);
|
let material_type = MaterialType::from_extension(extension);
|
||||||
|
|
||||||
// 创建素材对象
|
// 创建素材对象
|
||||||
let material = Material::new(
|
let mut material = Material::new(
|
||||||
project_id.to_string(),
|
project_id.to_string(),
|
||||||
file_name.clone(),
|
file_name.clone(),
|
||||||
file_path.to_string(),
|
file_path.to_string(),
|
||||||
file_size,
|
file_size,
|
||||||
md5_hash,
|
md5_hash,
|
||||||
material_type,
|
material_type.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
repository.create(&material)?;
|
repository.create(&material)?;
|
||||||
|
info!(material_id = %material.id, file_name = %file_name, "素材创建成功");
|
||||||
|
|
||||||
|
// 发送处理进度事件
|
||||||
|
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
|
||||||
|
"material_id": material.id,
|
||||||
|
"file_name": file_name,
|
||||||
|
"stage": "开始处理",
|
||||||
|
"progress_percentage": 0.0
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 如果启用自动处理,则进行完整的业务处理
|
||||||
|
if config.auto_process.unwrap_or(true) {
|
||||||
|
// 更新状态为处理中
|
||||||
|
MaterialService::update_material_status(
|
||||||
|
&repository,
|
||||||
|
&material.id,
|
||||||
|
ProcessingStatus::Processing,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// 1. 提取元数据
|
||||||
|
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
|
||||||
|
"material_id": material.id,
|
||||||
|
"file_name": file_name,
|
||||||
|
"stage": "提取元数据",
|
||||||
|
"progress_percentage": 25.0
|
||||||
|
}));
|
||||||
|
|
||||||
|
let original_path = material.original_path.clone();
|
||||||
|
let material_type_clone = material.material_type.clone();
|
||||||
|
|
||||||
|
match task::spawn_blocking(move || {
|
||||||
|
MaterialService::extract_metadata(&original_path, &material_type_clone)
|
||||||
|
}).await? {
|
||||||
|
Ok(metadata) => {
|
||||||
|
material.set_metadata(metadata);
|
||||||
|
repository.update(&material)?;
|
||||||
|
info!(material_id = %material.id, "元数据提取成功");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(material_id = %material.id, error = %e, "元数据提取失败");
|
||||||
|
MaterialService::update_material_status(
|
||||||
|
&repository,
|
||||||
|
&material.id,
|
||||||
|
ProcessingStatus::Failed,
|
||||||
|
Some(format!("元数据提取失败: {}", e)),
|
||||||
|
)?;
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 场景检测(如果是视频且启用了场景检测)
|
||||||
|
if matches!(material.material_type, MaterialType::Video) && config.enable_scene_detection {
|
||||||
|
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
|
||||||
|
"material_id": material.id,
|
||||||
|
"file_name": file_name,
|
||||||
|
"stage": "场景检测",
|
||||||
|
"progress_percentage": 50.0
|
||||||
|
}));
|
||||||
|
|
||||||
|
let original_path = material.original_path.clone();
|
||||||
|
let threshold = config.scene_detection_threshold;
|
||||||
|
|
||||||
|
match task::spawn_blocking(move || {
|
||||||
|
MaterialService::detect_video_scenes(&original_path, threshold)
|
||||||
|
}).await? {
|
||||||
|
Ok(scene_detection) => {
|
||||||
|
info!(material_id = %material.id, scenes = scene_detection.scenes.len(), "场景检测成功");
|
||||||
|
material.set_scene_detection(scene_detection);
|
||||||
|
repository.update(&material)?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// 场景检测失败不应该导致整个处理失败
|
||||||
|
warn!(material_id = %material.id, error = %e, "场景检测失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查是否需要切分视频
|
||||||
|
let should_segment = material.needs_segmentation(config.max_segment_duration) ||
|
||||||
|
(matches!(material.material_type, MaterialType::Video) && material.scene_detection.is_some());
|
||||||
|
|
||||||
|
if should_segment {
|
||||||
|
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
|
||||||
|
"material_id": material.id,
|
||||||
|
"file_name": file_name,
|
||||||
|
"stage": "视频切分",
|
||||||
|
"progress_percentage": 75.0
|
||||||
|
}));
|
||||||
|
|
||||||
|
let repository_clone = Arc::clone(&repository);
|
||||||
|
let material_clone = material.clone();
|
||||||
|
let config_clone = config.clone();
|
||||||
|
|
||||||
|
match task::spawn_blocking(move || {
|
||||||
|
MaterialService::segment_video(&repository_clone, &material_clone, &config_clone)
|
||||||
|
}).await? {
|
||||||
|
Ok(_) => {
|
||||||
|
info!(material_id = %material.id, "视频切分完成");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(material_id = %material.id, error = %e, "视频切分失败");
|
||||||
|
MaterialService::update_material_status(
|
||||||
|
&repository,
|
||||||
|
&material.id,
|
||||||
|
ProcessingStatus::Failed,
|
||||||
|
Some(format!("视频切分失败: {}", e)),
|
||||||
|
)?;
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记为完成
|
||||||
|
MaterialService::update_material_status(
|
||||||
|
&repository,
|
||||||
|
&material.id,
|
||||||
|
ProcessingStatus::Completed,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// 发送处理完成事件
|
||||||
|
let _ = app_handle.emit("material_processing_progress", serde_json::json!({
|
||||||
|
"material_id": material.id,
|
||||||
|
"file_name": file_name,
|
||||||
|
"stage": "处理完成",
|
||||||
|
"progress_percentage": 100.0
|
||||||
|
}));
|
||||||
|
|
||||||
|
info!(material_id = %material.id, "素材处理完成");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Some(material))
|
Ok(Some(material))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -183,13 +183,11 @@ export const MaterialImportDialog: React.FC<MaterialImportDialogProps> = ({
|
||||||
|
|
||||||
console.log('开始异步导入:', request);
|
console.log('开始异步导入:', request);
|
||||||
|
|
||||||
// 使用异步导入,进度更新通过事件监听器处理
|
// 使用异步导入,进度更新和完成状态完全通过事件监听器处理
|
||||||
const result = await importMaterialsAsync(request);
|
await importMaterialsAsync(request);
|
||||||
|
|
||||||
// 如果没有通过事件监听器收到完成事件,手动处理完成状态
|
// 注意:不在这里设置完成状态,完全依赖事件监听器
|
||||||
console.log('异步导入完成:', result);
|
console.log('异步导入命令执行完成,等待事件通知');
|
||||||
setStep('complete');
|
|
||||||
onImportComplete(result);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('导入失败:', error);
|
console.error('导入失败:', error);
|
||||||
setStep('configure'); // 返回配置步骤
|
setStep('configure'); // 返回配置步骤
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue