feat: 添加最大视频时长检测和二次切分功能
- 添加最大视频时长限制配置 (默认2秒) - 实现二次切分功能,超过时长限制的视频自动切分 - 优化单场景检测逻辑,跳过不必要的切分 - 修复场景和片段统计逻辑,无场景切换时统计为1个场景 - 添加CLI参数 --max-duration 支持自定义时长限制 - 集成FfmpegSliceService同步版本,简化异步复杂性 - 完善错误处理和文件清理机制
This commit is contained in:
parent
5659bb34f3
commit
808c143bf8
|
|
@ -111,6 +111,7 @@ def batch_detect_and_split(
|
||||||
use_advanced_split: bool = typer.Option(True, "--advanced/--traditional", help="使用高效批量切分/传统逐个切分"),
|
use_advanced_split: bool = typer.Option(True, "--advanced/--traditional", help="使用高效批量切分/传统逐个切分"),
|
||||||
split_quality: int = typer.Option(23, "--quality", "-q", help="切分质量 (CRF值, 18-28)"),
|
split_quality: int = typer.Option(23, "--quality", "-q", help="切分质量 (CRF值, 18-28)"),
|
||||||
split_preset: str = typer.Option("fast", "--preset", help="编码预设 (ultrafast/fast/medium/slow)"),
|
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="详细输出")
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||||
):
|
):
|
||||||
"""批量场景检测和视频切分"""
|
"""批量场景检测和视频切分"""
|
||||||
|
|
@ -162,7 +163,8 @@ def batch_detect_and_split(
|
||||||
continue_on_error=continue_on_error,
|
continue_on_error=continue_on_error,
|
||||||
use_advanced_split=use_advanced_split,
|
use_advanced_split=use_advanced_split,
|
||||||
split_quality=split_quality,
|
split_quality=split_quality,
|
||||||
split_preset=split_preset
|
split_preset=split_preset,
|
||||||
|
max_video_duration=max_duration
|
||||||
)
|
)
|
||||||
|
|
||||||
# 显示结果
|
# 显示结果
|
||||||
|
|
|
||||||
|
|
@ -120,11 +120,12 @@ class SceneDetector:
|
||||||
enable_ai_analysis: bool = False, enable_video_splitting: bool = True,
|
enable_ai_analysis: bool = False, enable_video_splitting: bool = True,
|
||||||
max_concurrent: int = 2, continue_on_error: bool = True,
|
max_concurrent: int = 2, continue_on_error: bool = True,
|
||||||
use_advanced_split: bool = True, split_quality: int = 23, split_preset: str = "fast",
|
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(
|
return self.workflow_manager.batch_detect_and_split(
|
||||||
video_paths, output_base_dir, detector_type, threshold, min_scene_length,
|
video_paths, output_base_dir, detector_type, threshold, min_scene_length,
|
||||||
output_format, enable_ai_analysis, enable_video_splitting,
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class BatchSceneDetectionWorkflowState:
|
||||||
use_advanced_split: bool = True
|
use_advanced_split: bool = True
|
||||||
split_quality: int = 23
|
split_quality: int = 23
|
||||||
split_preset: str = "fast"
|
split_preset: str = "fast"
|
||||||
max_video_duration: float = 60.0 # 最大视频时长(秒),默认60秒
|
max_video_duration: float = 2.0 # 最大视频时长(秒),默认60秒
|
||||||
|
|
||||||
# 批量处理配置
|
# 批量处理配置
|
||||||
max_concurrent: int = 4
|
max_concurrent: int = 4
|
||||||
|
|
|
||||||
|
|
@ -296,20 +296,6 @@ class BatchWorkflowNodes:
|
||||||
)
|
)
|
||||||
split_results_raw.append(split_result)
|
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]
|
successful_results = [r for r in split_results_raw if r.success]
|
||||||
|
|
@ -333,6 +319,79 @@ class BatchWorkflowNodes:
|
||||||
# 切分失败不影响整体任务成功
|
# 切分失败不影响整体任务成功
|
||||||
|
|
||||||
# 4. 添加视频时长检查,如果时长大于 最大视频时长 那么就要进行二次切分 确保 视频不大于 最大视频时长
|
# 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()
|
task.end_time = time.time()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ class SceneDetectionWorkflowManager:
|
||||||
enable_ai_analysis: bool = False, enable_video_splitting: bool = True,
|
enable_ai_analysis: bool = False, enable_video_splitting: bool = True,
|
||||||
max_concurrent: int = 2, continue_on_error: bool = True,
|
max_concurrent: int = 2, continue_on_error: bool = True,
|
||||||
use_advanced_split: bool = True, split_quality: int = 23, split_preset: str = "fast",
|
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,
|
use_advanced_split=use_advanced_split,
|
||||||
split_quality=split_quality,
|
split_quality=split_quality,
|
||||||
split_preset=split_preset,
|
split_preset=split_preset,
|
||||||
|
max_video_duration=max_video_duration,
|
||||||
max_concurrent=max_concurrent,
|
max_concurrent=max_concurrent,
|
||||||
continue_on_error=continue_on_error,
|
continue_on_error=continue_on_error,
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue