feat: 添加最大视频时长检测和二次切分功能

- 添加最大视频时长限制配置 (默认2秒)
- 实现二次切分功能,超过时长限制的视频自动切分
- 优化单场景检测逻辑,跳过不必要的切分
- 修复场景和片段统计逻辑,无场景切换时统计为1个场景
- 添加CLI参数 --max-duration 支持自定义时长限制
- 集成FfmpegSliceService同步版本,简化异步复杂性
- 完善错误处理和文件清理机制
This commit is contained in:
root 2025-07-12 15:24:29 +08:00
parent 5659bb34f3
commit 808c143bf8
5 changed files with 82 additions and 19 deletions

View File

@ -111,6 +111,7 @@ def batch_detect_and_split(
use_advanced_split: bool = typer.Option(True, "--advanced/--traditional", help="使用高效批量切分/传统逐个切分"),
split_quality: int = typer.Option(23, "--quality", "-q", help="切分质量 (CRF值, 18-28)"),
split_preset: str = typer.Option("fast", "--preset", help="编码预设 (ultrafast/fast/medium/slow)"),
max_duration: float = typer.Option(2.0, "--max-duration", "-d", help="最大视频时长限制(秒),超过将二次切分"),
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
):
"""批量场景检测和视频切分"""
@ -162,7 +163,8 @@ def batch_detect_and_split(
continue_on_error=continue_on_error,
use_advanced_split=use_advanced_split,
split_quality=split_quality,
split_preset=split_preset
split_preset=split_preset,
max_video_duration=max_duration
)
# 显示结果

View File

@ -120,11 +120,12 @@ class SceneDetector:
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]:
max_video_duration: float = 60.0, 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
max_concurrent, continue_on_error, use_advanced_split, split_quality, split_preset,
max_video_duration, request_id
)

View File

@ -41,7 +41,7 @@ class BatchSceneDetectionWorkflowState:
use_advanced_split: bool = True
split_quality: int = 23
split_preset: str = "fast"
max_video_duration: float = 60.0 # 最大视频时长默认60秒
max_video_duration: float = 2.0 # 最大视频时长默认60秒
# 批量处理配置
max_concurrent: int = 4

View File

@ -296,20 +296,6 @@ class BatchWorkflowNodes:
)
split_results_raw.append(split_result)
# 转换为字典格式
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
]
# 创建切分摘要
successful_results = [r for r in split_results_raw if r.success]
@ -333,6 +319,79 @@ class BatchWorkflowNodes:
# 切分失败不影响整体任务成功
# 4. 添加视频时长检查,如果时长大于 最大视频时长 那么就要进行二次切分 确保 视频不大于 最大视频时长
final_split_results = []
for split_result in split_results_raw:
try:
# 检查切分后的视频时长
video_path = str(split_result.output_path)
if split_result.duration > state.max_video_duration:
logger.info(f"⚠️ 片段 {split_result.scene_index + 1} 时长 {split_result.duration:.2f}s 超过限制 {state.max_video_duration:.2f}s进行二次切分")
# 进行二次切分
secondary_results = self.splitter_service.check_and_split_by_duration(
video_path=video_path,
max_duration=state.max_video_duration,
options=slice_options,
output_dir=str(split_result.output_path.parent)
)
# 处理二次切分结果
for i, (secondary_path, secondary_metadata) in enumerate(secondary_results):
if len(secondary_results) > 1:
# 如果进行了二次切分,创建新的结果对象
secondary_split_result = SplitResult(
scene_index=split_result.scene_index,
output_path=secondary_path,
start_time=split_result.start_time + (i * state.max_video_duration),
end_time=min(split_result.start_time + ((i + 1) * state.max_video_duration), split_result.end_time),
duration=secondary_metadata.duration,
file_size=secondary_metadata.size,
success=Path(secondary_path).exists(),
error=None if Path(secondary_path).exists() else "二次切分文件不存在"
)
final_split_results.append(secondary_split_result)
# 删除原始的过长片段(如果二次切分成功)
if Path(video_path).exists() and len(secondary_results) > 1:
try:
Path(video_path).unlink()
logger.info(f"🗑️ 已删除过长的原始片段: {Path(video_path).name}")
except Exception as e:
logger.warning(f"⚠️ 删除原始片段失败: {e}")
else:
# 如果没有进行二次切分(时长检查通过),保持原结果
final_split_results.append(split_result)
else:
# 时长未超过限制,直接添加到最终结果
final_split_results.append(split_result)
except Exception as e:
logger.error(f"❌ 处理片段 {split_result.scene_index + 1} 的时长检查时出错: {e}")
# 出错时保持原结果
final_split_results.append(split_result)
# 使用最终的切分结果
split_results_raw = final_split_results
# 转换为字典格式
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
]
# 更新统计信息
logger.info(f"📊 最终生成 {len(split_results_raw)} 个视频片段(包含二次切分)")
task.end_time = time.time()

View File

@ -184,7 +184,7 @@ class SceneDetectionWorkflowManager:
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]:
max_video_duration: float = 60.0, request_id: Optional[str] = None) -> Dict[str, Any]:
"""批量场景检测和视频切分"""
# 创建批量工作流状态
@ -200,6 +200,7 @@ class SceneDetectionWorkflowManager:
use_advanced_split=use_advanced_split,
split_quality=split_quality,
split_preset=split_preset,
max_video_duration=max_video_duration,
max_concurrent=max_concurrent,
continue_on_error=continue_on_error,
request_id=request_id,