fix
This commit is contained in:
@@ -114,115 +114,17 @@ class SceneDetector:
|
||||
|
||||
return results
|
||||
|
||||
# JSON-RPC方法
|
||||
def jsonrpc_detect_scenes(self, video_path: str, detector_type: str = "content",
|
||||
threshold: float = 30.0, min_scene_length: float = 1.0) -> Dict[str, Any]:
|
||||
"""JSON-RPC方法:基础场景检测"""
|
||||
try:
|
||||
result = self.detect_scenes(
|
||||
Path(video_path),
|
||||
DetectorType(detector_type),
|
||||
threshold,
|
||||
min_scene_length
|
||||
)
|
||||
|
||||
return asdict(result)
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
def batch_detect_and_split(self, video_paths: List[Path], output_base_dir: Optional[Path] = None,
|
||||
detector_type: DetectorType = DetectorType.CONTENT, threshold: float = 30.0,
|
||||
min_scene_length: float = 1.0, output_format: OutputFormat = OutputFormat.JSON,
|
||||
enable_ai_analysis: bool = False, enable_video_splitting: bool = True,
|
||||
max_concurrent: int = 2, continue_on_error: bool = True,
|
||||
use_advanced_split: bool = True, split_quality: int = 23, split_preset: str = "fast",
|
||||
request_id: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""批量场景检测和视频切分"""
|
||||
return self.workflow_manager.batch_detect_and_split(
|
||||
video_paths, output_base_dir, detector_type, threshold, min_scene_length,
|
||||
output_format, enable_ai_analysis, enable_video_splitting,
|
||||
max_concurrent, continue_on_error, use_advanced_split, split_quality, split_preset, request_id
|
||||
)
|
||||
|
||||
def jsonrpc_get_video_info(self, video_path: str) -> Dict[str, Any]:
|
||||
"""JSON-RPC方法:获取视频信息"""
|
||||
try:
|
||||
return self.get_video_info(Path(video_path))
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
def jsonrpc_detect_with_workflow(self, video_path: str, detector_type: str = "content",
|
||||
threshold: float = 30.0, min_scene_length: float = 1.0,
|
||||
output_path: Optional[str] = None, output_format: str = "json",
|
||||
enable_ai_analysis: bool = True, request_id: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
||||
"""JSON-RPC方法:工作流场景检测"""
|
||||
try:
|
||||
output_path_obj = Path(output_path) if output_path else None
|
||||
|
||||
result = self.detect_with_workflow(
|
||||
Path(video_path),
|
||||
DetectorType(detector_type),
|
||||
threshold,
|
||||
min_scene_length,
|
||||
output_path_obj,
|
||||
OutputFormat(output_format),
|
||||
enable_ai_analysis,
|
||||
request_id
|
||||
)
|
||||
|
||||
# 检查是否是JSON-RPC模式
|
||||
if result.get("jsonrpc_mode"):
|
||||
# JSON-RPC模式:结果已经通过工作流发送,返回None
|
||||
return None
|
||||
else:
|
||||
# 非JSON-RPC模式:序列化并返回结果
|
||||
serialized_result = {}
|
||||
for key, value in result.items():
|
||||
if key == "detection_result" and value:
|
||||
serialized_result[key] = asdict(value)
|
||||
else:
|
||||
serialized_result[key] = value
|
||||
return serialized_result
|
||||
|
||||
except Exception as e:
|
||||
# 如果有request_id,错误已经在detect_with_workflow中发送
|
||||
if request_id:
|
||||
return None
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
def jsonrpc_batch_detect(self, video_paths: List[str], detector_type: str = "content",
|
||||
threshold: float = 30.0, min_scene_length: float = 1.0,
|
||||
output_dir: Optional[str] = None, output_format: str = "json") -> Dict[str, Any]:
|
||||
"""JSON-RPC方法:批量检测"""
|
||||
try:
|
||||
paths = [Path(p) for p in video_paths]
|
||||
output_dir_obj = Path(output_dir) if output_dir else None
|
||||
|
||||
results = self.batch_detect(
|
||||
paths,
|
||||
DetectorType(detector_type),
|
||||
threshold,
|
||||
min_scene_length,
|
||||
output_dir_obj,
|
||||
OutputFormat(output_format)
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"results": results,
|
||||
"total_videos": len(video_paths),
|
||||
"successful_detections": sum(1 for r in results if r["success"])
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
def register_jsonrpc_methods(self):
|
||||
"""注册JSON-RPC方法到全局注册器"""
|
||||
from python_core.utils.jsonrpc_enhanced import method_registry
|
||||
|
||||
# 直接注册方法
|
||||
method_registry.register_function(self.jsonrpc_detect_scenes, "scene.detect")
|
||||
method_registry.register_function(self.jsonrpc_detect_with_workflow, "scene.detect_workflow")
|
||||
method_registry.register_function(self.jsonrpc_get_video_info, "scene.get_video_info")
|
||||
method_registry.register_function(self.jsonrpc_batch_detect, "scene.batch_detect")
|
||||
|
||||
@@ -9,9 +9,11 @@ Scene Detection Services
|
||||
from .detector_service import SceneDetectorService
|
||||
from .ai_analysis_service import AIAnalysisService
|
||||
from .video_info_service import VideoInfoService
|
||||
from .video_splitter_service import VideoSplitterService
|
||||
|
||||
__all__ = [
|
||||
"SceneDetectorService",
|
||||
"AIAnalysisService",
|
||||
"VideoInfoService"
|
||||
"AIAnalysisService",
|
||||
"VideoInfoService",
|
||||
"VideoSplitterService"
|
||||
]
|
||||
|
||||
163
python_core/scene_detection/types/batch_workflow_state.py
Normal file
163
python_core/scene_detection/types/batch_workflow_state.py
Normal file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Batch Workflow State
|
||||
批量工作流状态定义
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Any, List, Optional
|
||||
from pathlib import Path
|
||||
from .models import SceneInfo, DetectionResult
|
||||
from python_core.utils.jsonrpc_enhanced import EnhancedJSONRPCResponse, ProgressLevel
|
||||
|
||||
|
||||
@dataclass
|
||||
class BatchVideoTask:
|
||||
"""批量视频处理任务"""
|
||||
video_path: Path
|
||||
output_dir: Optional[Path] = None
|
||||
status: str = "pending" # pending, processing, completed, failed
|
||||
detection_result: Optional[DetectionResult] = None
|
||||
split_results: List[Dict[str, Any]] = field(default_factory=list)
|
||||
error: Optional[str] = None
|
||||
start_time: Optional[float] = None
|
||||
end_time: Optional[float] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class BatchSceneDetectionWorkflowState:
|
||||
"""批量场景检测工作流状态"""
|
||||
# 输入参数
|
||||
video_paths: List[Path] = field(default_factory=list)
|
||||
output_base_dir: Optional[Path] = None
|
||||
detector_type: str = "content"
|
||||
threshold: float = 30.0
|
||||
min_scene_length: float = 1.0
|
||||
output_format: str = "json"
|
||||
enable_ai_analysis: bool = False
|
||||
enable_video_splitting: bool = True
|
||||
|
||||
# 视频切分配置
|
||||
use_advanced_split: bool = True
|
||||
split_quality: int = 23
|
||||
split_preset: str = "fast"
|
||||
|
||||
# 批量处理配置
|
||||
max_concurrent: int = 2
|
||||
continue_on_error: bool = True
|
||||
|
||||
# 工作流状态
|
||||
current_stage: str = "init"
|
||||
current_task_index: int = 0
|
||||
total_tasks: int = 0
|
||||
completed_tasks: int = 0
|
||||
failed_tasks: int = 0
|
||||
|
||||
# JSON-RPC支持
|
||||
request_id: Optional[str] = None
|
||||
enable_jsonrpc: bool = False
|
||||
|
||||
# 任务列表
|
||||
tasks: List[BatchVideoTask] = field(default_factory=list)
|
||||
|
||||
# 全局结果
|
||||
batch_results: Dict[str, Any] = field(default_factory=dict)
|
||||
global_errors: List[str] = field(default_factory=list)
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.tasks and self.video_paths:
|
||||
# 从视频路径创建任务
|
||||
self.tasks = [
|
||||
BatchVideoTask(
|
||||
video_path=video_path,
|
||||
output_dir=self.output_base_dir / video_path.stem if self.output_base_dir else None
|
||||
)
|
||||
for video_path in self.video_paths
|
||||
]
|
||||
self.total_tasks = len(self.tasks)
|
||||
|
||||
def get_jsonrpc_handler(self) -> Optional[EnhancedJSONRPCResponse]:
|
||||
"""获取JSON-RPC响应处理器"""
|
||||
if self.enable_jsonrpc and self.request_id:
|
||||
return EnhancedJSONRPCResponse(self.request_id)
|
||||
return None
|
||||
|
||||
def send_progress(self, step: str, message: str, level: ProgressLevel = ProgressLevel.INFO,
|
||||
data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""发送进度更新"""
|
||||
handler = self.get_jsonrpc_handler()
|
||||
if handler:
|
||||
# 计算总体进度
|
||||
if self.total_tasks > 0:
|
||||
progress_percent = int((self.completed_tasks / self.total_tasks * 100))
|
||||
else:
|
||||
progress_percent = -1
|
||||
|
||||
# 添加批量处理特定数据
|
||||
progress_data = {
|
||||
"current_task": self.current_task_index + 1,
|
||||
"total_tasks": self.total_tasks,
|
||||
"completed_tasks": self.completed_tasks,
|
||||
"failed_tasks": self.failed_tasks,
|
||||
"current_stage": self.current_stage
|
||||
}
|
||||
|
||||
if data:
|
||||
progress_data.update(data)
|
||||
|
||||
handler.progress(step, progress_percent, message, level, progress_data)
|
||||
|
||||
def send_final_result(self, result: Dict[str, Any]) -> None:
|
||||
"""发送最终结果"""
|
||||
handler = self.get_jsonrpc_handler()
|
||||
if handler:
|
||||
handler.success(result)
|
||||
|
||||
def send_error_result(self, error_code: int, error_message: str, error_data: Any = None) -> None:
|
||||
"""发送错误结果"""
|
||||
handler = self.get_jsonrpc_handler()
|
||||
if handler:
|
||||
handler.error(error_code, error_message, error_data)
|
||||
|
||||
def get_current_task(self) -> Optional[BatchVideoTask]:
|
||||
"""获取当前任务"""
|
||||
if 0 <= self.current_task_index < len(self.tasks):
|
||||
return self.tasks[self.current_task_index]
|
||||
return None
|
||||
|
||||
def mark_task_completed(self, task_index: int, result: DetectionResult, split_results: List[Dict[str, Any]] = None):
|
||||
"""标记任务完成"""
|
||||
if 0 <= task_index < len(self.tasks):
|
||||
task = self.tasks[task_index]
|
||||
task.status = "completed"
|
||||
task.detection_result = result
|
||||
task.split_results = split_results or []
|
||||
self.completed_tasks += 1
|
||||
|
||||
def mark_task_failed(self, task_index: int, error: str):
|
||||
"""标记任务失败"""
|
||||
if 0 <= task_index < len(self.tasks):
|
||||
task = self.tasks[task_index]
|
||||
task.status = "failed"
|
||||
task.error = error
|
||||
self.failed_tasks += 1
|
||||
|
||||
def get_summary(self) -> Dict[str, Any]:
|
||||
"""获取批量处理摘要"""
|
||||
return {
|
||||
"total_tasks": self.total_tasks,
|
||||
"completed_tasks": self.completed_tasks,
|
||||
"failed_tasks": self.failed_tasks,
|
||||
"success_rate": (self.completed_tasks / self.total_tasks * 100) if self.total_tasks > 0 else 0,
|
||||
"current_stage": self.current_stage,
|
||||
"tasks": [
|
||||
{
|
||||
"video_path": str(task.video_path),
|
||||
"status": task.status,
|
||||
"total_scenes": task.detection_result.total_scenes if task.detection_result else 0,
|
||||
"split_count": len(task.split_results),
|
||||
"error": task.error
|
||||
}
|
||||
for task in self.tasks
|
||||
]
|
||||
}
|
||||
254
python_core/scene_detection/workflows/batch_workflow_nodes.py
Normal file
254
python_core/scene_detection/workflows/batch_workflow_nodes.py
Normal file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Batch Workflow Nodes
|
||||
批量工作流节点
|
||||
"""
|
||||
|
||||
import time
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, List
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
from python_core.utils.logger import logger
|
||||
from python_core.utils.jsonrpc_enhanced import ProgressLevel
|
||||
from ..types.batch_workflow_state import BatchSceneDetectionWorkflowState, BatchVideoTask
|
||||
from ..types.enums import DetectorType, OutputFormat
|
||||
from ..services.detector_service import SceneDetectorService
|
||||
from ..services.video_info_service import VideoInfoService
|
||||
from ..services.ai_analysis_service import AIAnalysisService
|
||||
from python_core.services.ffmpeg_slice_service import FfmpegSliceService, SliceOptions
|
||||
from ..utils.result_saver import ResultSaver
|
||||
|
||||
|
||||
class BatchWorkflowNodes:
|
||||
"""批量工作流节点"""
|
||||
|
||||
def __init__(self):
|
||||
# 初始化服务
|
||||
self.detector_service = SceneDetectorService()
|
||||
self.video_info_service = VideoInfoService()
|
||||
self.ai_analysis_service = AIAnalysisService()
|
||||
self.splitter_service = FfmpegSliceService()
|
||||
self.result_saver = ResultSaver()
|
||||
|
||||
def validate_batch_input(self, state: BatchSceneDetectionWorkflowState) -> Dict[str, Any]:
|
||||
"""验证批量输入"""
|
||||
state.current_stage = "validation"
|
||||
state.send_progress("validate_input", "验证批量输入参数", ProgressLevel.INFO)
|
||||
|
||||
errors = []
|
||||
|
||||
# 检查视频文件
|
||||
valid_videos = []
|
||||
for video_path in state.video_paths:
|
||||
if not video_path.exists():
|
||||
errors.append(f"视频文件不存在: {video_path}")
|
||||
continue
|
||||
|
||||
if video_path.suffix.lower() not in {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'}:
|
||||
errors.append(f"不支持的视频格式: {video_path}")
|
||||
continue
|
||||
|
||||
valid_videos.append(video_path)
|
||||
|
||||
if not valid_videos:
|
||||
errors.append("没有有效的视频文件")
|
||||
|
||||
# 检查输出目录
|
||||
if state.output_base_dir:
|
||||
try:
|
||||
state.output_base_dir.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
errors.append(f"无法创建输出目录: {e}")
|
||||
|
||||
# 检查FFmpeg(如果需要切分)
|
||||
if state.enable_video_splitting:
|
||||
if not self.splitter_service.check_ffmpeg_available():
|
||||
errors.append("FFmpeg不可用,无法进行视频切分")
|
||||
|
||||
if errors:
|
||||
state.global_errors.extend(errors)
|
||||
return {
|
||||
"workflow_state": "failed",
|
||||
"errors": errors,
|
||||
"valid_videos": valid_videos
|
||||
}
|
||||
|
||||
# 更新有效视频列表
|
||||
state.video_paths = valid_videos
|
||||
state.tasks = [
|
||||
BatchVideoTask(
|
||||
video_path=video_path,
|
||||
output_dir=state.output_base_dir / video_path.stem if state.output_base_dir else None
|
||||
)
|
||||
for video_path in valid_videos
|
||||
]
|
||||
state.total_tasks = len(state.tasks)
|
||||
|
||||
state.send_progress("validate_input", f"验证完成,找到 {len(valid_videos)} 个有效视频", ProgressLevel.SUCCESS)
|
||||
|
||||
return {
|
||||
"workflow_state": "validated",
|
||||
"valid_videos": valid_videos,
|
||||
"total_tasks": state.total_tasks
|
||||
}
|
||||
|
||||
def process_videos_batch(self, state: BatchSceneDetectionWorkflowState) -> Dict[str, Any]:
|
||||
"""批量处理视频"""
|
||||
state.current_stage = "batch_processing"
|
||||
state.send_progress("batch_process", "开始批量处理视频", ProgressLevel.INFO)
|
||||
|
||||
# 使用线程池进行并发处理
|
||||
with ThreadPoolExecutor(max_workers=state.max_concurrent) as executor:
|
||||
# 提交所有任务
|
||||
future_to_index = {}
|
||||
for i, task in enumerate(state.tasks):
|
||||
future = executor.submit(self._process_single_video, state, i, task)
|
||||
future_to_index[future] = i
|
||||
|
||||
# 处理完成的任务
|
||||
for future in as_completed(future_to_index):
|
||||
task_index = future_to_index[future]
|
||||
try:
|
||||
result = future.result()
|
||||
if result["success"]:
|
||||
state.mark_task_completed(
|
||||
task_index,
|
||||
result["detection_result"],
|
||||
result.get("split_results", [])
|
||||
)
|
||||
state.send_progress(
|
||||
"task_completed",
|
||||
f"任务 {task_index + 1}/{state.total_tasks} 完成: {state.tasks[task_index].video_path.name}",
|
||||
ProgressLevel.SUCCESS,
|
||||
{"task_index": task_index + 1, "video_name": state.tasks[task_index].video_path.name}
|
||||
)
|
||||
else:
|
||||
state.mark_task_failed(task_index, result["error"])
|
||||
state.send_progress(
|
||||
"task_failed",
|
||||
f"任务 {task_index + 1}/{state.total_tasks} 失败: {result['error']}",
|
||||
ProgressLevel.ERROR,
|
||||
{"task_index": task_index + 1, "error": result["error"]}
|
||||
)
|
||||
except Exception as e:
|
||||
state.mark_task_failed(task_index, str(e))
|
||||
state.send_progress(
|
||||
"task_error",
|
||||
f"任务 {task_index + 1}/{state.total_tasks} 异常: {str(e)}",
|
||||
ProgressLevel.ERROR,
|
||||
{"task_index": task_index + 1, "error": str(e)}
|
||||
)
|
||||
|
||||
# 生成批量处理结果
|
||||
summary = state.get_summary()
|
||||
|
||||
state.send_progress(
|
||||
"batch_complete",
|
||||
f"批量处理完成: {state.completed_tasks}/{state.total_tasks} 成功",
|
||||
ProgressLevel.SUCCESS,
|
||||
summary
|
||||
)
|
||||
|
||||
return {
|
||||
"workflow_state": "completed",
|
||||
"summary": summary,
|
||||
"completed_tasks": state.completed_tasks,
|
||||
"failed_tasks": state.failed_tasks,
|
||||
"total_tasks": state.total_tasks
|
||||
}
|
||||
|
||||
def _process_single_video(self, state: BatchSceneDetectionWorkflowState,
|
||||
task_index: int, task: BatchVideoTask) -> Dict[str, Any]:
|
||||
"""处理单个视频"""
|
||||
try:
|
||||
task.status = "processing"
|
||||
task.start_time = time.time()
|
||||
|
||||
logger.info(f"🎬 处理视频 {task_index + 1}/{state.total_tasks}: {task.video_path.name}")
|
||||
|
||||
# 1. 场景检测
|
||||
detection_result = self.detector_service.detect_scenes(
|
||||
task.video_path,
|
||||
DetectorType(state.detector_type),
|
||||
state.threshold,
|
||||
state.min_scene_length
|
||||
)
|
||||
|
||||
if not detection_result.success:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"场景检测失败: {detection_result.error}"
|
||||
}
|
||||
|
||||
# 2. 保存检测结果
|
||||
if task.output_dir:
|
||||
task.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
result_file = task.output_dir / f"scenes.{state.output_format}"
|
||||
self.result_saver.save_results(
|
||||
detection_result,
|
||||
result_file,
|
||||
OutputFormat(state.output_format)
|
||||
)
|
||||
|
||||
# 3. 视频切分(如果启用)
|
||||
split_results = []
|
||||
if state.enable_video_splitting and task.output_dir:
|
||||
try:
|
||||
# 创建切分选项
|
||||
slice_options = SliceOptions(
|
||||
crf=state.split_quality,
|
||||
preset=state.split_preset
|
||||
)
|
||||
|
||||
split_results_raw = self.splitter_service.split_video_by_scenes(
|
||||
task.video_path,
|
||||
detection_result.scenes,
|
||||
task.output_dir / "scenes",
|
||||
use_advanced=state.use_advanced_split,
|
||||
options=slice_options
|
||||
)
|
||||
|
||||
# 转换为字典格式
|
||||
split_results = [
|
||||
{
|
||||
"scene_index": r.scene_index + 1,
|
||||
"output_path": str(r.output_path),
|
||||
"start_time": r.start_time,
|
||||
"end_time": r.end_time,
|
||||
"duration": r.duration,
|
||||
"file_size": r.file_size,
|
||||
"success": r.success,
|
||||
"error": r.error
|
||||
}
|
||||
for r in split_results_raw
|
||||
]
|
||||
|
||||
# 保存切分摘要
|
||||
split_summary = self.splitter_service.create_split_summary(
|
||||
task.video_path, split_results_raw
|
||||
)
|
||||
summary_file = task.output_dir / "split_summary.json"
|
||||
import json
|
||||
with open(summary_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(split_summary, f, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"视频切分失败: {e}")
|
||||
# 切分失败不影响整体任务成功
|
||||
|
||||
task.end_time = time.time()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"detection_result": detection_result,
|
||||
"split_results": split_results
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
task.end_time = time.time()
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
@@ -6,12 +6,14 @@ Workflow Manager
|
||||
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional, Literal
|
||||
from typing import Dict, Any, Optional, List, Literal
|
||||
|
||||
from python_core.utils.logger import logger
|
||||
from python_core.utils.jsonrpc_enhanced import EnhancedJSONRPCResponse
|
||||
from ..types import SceneDetectionWorkflowState, DetectorType, OutputFormat
|
||||
from .workflow_nodes import WorkflowNodes
|
||||
from .batch_workflow_nodes import BatchWorkflowNodes
|
||||
from ..types.batch_workflow_state import BatchSceneDetectionWorkflowState
|
||||
|
||||
|
||||
class SceneDetectionWorkflowManager:
|
||||
@@ -19,6 +21,7 @@ class SceneDetectionWorkflowManager:
|
||||
|
||||
def __init__(self):
|
||||
self.nodes = WorkflowNodes()
|
||||
self.batch_nodes = BatchWorkflowNodes()
|
||||
self.workflow = None
|
||||
|
||||
def create_detection_workflow(self):
|
||||
@@ -174,3 +177,111 @@ class SceneDetectionWorkflowManager:
|
||||
# 非JSON-RPC模式:抛出异常
|
||||
logger.error(f"❌ {error_msg}")
|
||||
raise
|
||||
|
||||
def batch_detect_and_split(self, video_paths: List[Path], output_base_dir: Optional[Path] = None,
|
||||
detector_type: DetectorType = DetectorType.CONTENT, threshold: float = 30.0,
|
||||
min_scene_length: float = 1.0, output_format: OutputFormat = OutputFormat.JSON,
|
||||
enable_ai_analysis: bool = False, enable_video_splitting: bool = True,
|
||||
max_concurrent: int = 2, continue_on_error: bool = True,
|
||||
use_advanced_split: bool = True, split_quality: int = 23, split_preset: str = "fast",
|
||||
request_id: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""批量场景检测和视频切分"""
|
||||
|
||||
# 创建批量工作流状态
|
||||
state = BatchSceneDetectionWorkflowState(
|
||||
video_paths=video_paths,
|
||||
output_base_dir=output_base_dir,
|
||||
detector_type=detector_type.value,
|
||||
threshold=threshold,
|
||||
min_scene_length=min_scene_length,
|
||||
output_format=output_format.value,
|
||||
enable_ai_analysis=enable_ai_analysis,
|
||||
enable_video_splitting=enable_video_splitting,
|
||||
use_advanced_split=use_advanced_split,
|
||||
split_quality=split_quality,
|
||||
split_preset=split_preset,
|
||||
max_concurrent=max_concurrent,
|
||||
continue_on_error=continue_on_error,
|
||||
request_id=request_id,
|
||||
enable_jsonrpc=request_id is not None
|
||||
)
|
||||
|
||||
try:
|
||||
logger.info(f"🚀 开始批量场景检测和切分")
|
||||
logger.info(f"📁 视频数量: {len(video_paths)}")
|
||||
logger.info(f"📂 输出目录: {output_base_dir}")
|
||||
logger.info(f"🎯 检测器: {detector_type.value}, 阈值: {threshold}")
|
||||
logger.info(f"✂️ 视频切分: {'启用' if enable_video_splitting else '禁用'}")
|
||||
logger.info(f"🔄 并发数: {max_concurrent}")
|
||||
|
||||
# 1. 验证输入
|
||||
validation_result = self.batch_nodes.validate_batch_input(state)
|
||||
if validation_result["workflow_state"] == "failed":
|
||||
error_msg = f"批量输入验证失败: {'; '.join(validation_result['errors'])}"
|
||||
if request_id:
|
||||
state.send_error_result(-32602, error_msg, validation_result["errors"])
|
||||
return {
|
||||
"jsonrpc_mode": True,
|
||||
"request_id": request_id,
|
||||
"error_sent": True,
|
||||
"error": error_msg
|
||||
}
|
||||
else:
|
||||
raise ValueError(error_msg)
|
||||
|
||||
# 2. 批量处理
|
||||
processing_result = self.batch_nodes.process_videos_batch(state)
|
||||
|
||||
# 3. 构建最终结果
|
||||
final_result = {
|
||||
"workflow_state": processing_result["workflow_state"],
|
||||
"summary": processing_result["summary"],
|
||||
"batch_results": {
|
||||
"total_videos": state.total_tasks,
|
||||
"completed_videos": state.completed_tasks,
|
||||
"failed_videos": state.failed_tasks,
|
||||
"success_rate": (state.completed_tasks / state.total_tasks * 100) if state.total_tasks > 0 else 0,
|
||||
"output_base_dir": str(output_base_dir) if output_base_dir else None,
|
||||
"enable_video_splitting": enable_video_splitting,
|
||||
"tasks": [
|
||||
{
|
||||
"video_path": str(task.video_path),
|
||||
"status": task.status,
|
||||
"output_dir": str(task.output_dir) if task.output_dir else None,
|
||||
"total_scenes": task.detection_result.total_scenes if task.detection_result else 0,
|
||||
"detection_time": task.detection_result.detection_time if task.detection_result else 0,
|
||||
"split_count": len(task.split_results) if task.split_results else 0,
|
||||
"error": task.error,
|
||||
"processing_time": (task.end_time - task.start_time) if task.start_time and task.end_time else 0
|
||||
}
|
||||
for task in state.tasks
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if request_id:
|
||||
state.send_final_result(final_result)
|
||||
return {
|
||||
"jsonrpc_mode": True,
|
||||
"request_id": request_id,
|
||||
"result_sent": True,
|
||||
"result": final_result
|
||||
}
|
||||
else:
|
||||
return final_result
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"批量工作流执行失败: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
|
||||
if request_id:
|
||||
state.send_error_result(-32603, error_msg, str(e))
|
||||
return {
|
||||
"jsonrpc_mode": True,
|
||||
"request_id": request_id,
|
||||
"error_sent": True,
|
||||
"error": error_msg
|
||||
}
|
||||
else:
|
||||
logger.error(f"❌ {error_msg}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user