This commit is contained in:
root
2025-07-12 11:31:03 +08:00
parent 6157976b85
commit bc19461d8a
13 changed files with 2393 additions and 1 deletions

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python3
"""
MixVideo 统一命令行接口
"""
from .cli import main
__all__ = ["main"]

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env python3
from .cli import main
if __name__ == "__main__":
main()

58
python_core/cli/cli.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
MixVideo 主命令行接口
"""
import sys
from pathlib import Path
from typing import Optional
from python_core.cli.const import progress_reporter, console, project_root
import typer
# 导入命令模块
from python_core.cli.commands import scene_app
app = typer.Typer(
name="mixvideo",
help="""
🎬 MixVideo - 智能视频处理平台
功能完整的视频处理和管理工具套件:
• 🎯 场景检测 - 智能识别视频场景变化
• 📤 媒体管理 - 上传、处理、组织视频文件
• 📋 模板管理 - 视频模板导入导出
• ⚙️ 系统管理 - 配置、状态、存储管理
快速开始:
mixvideo scene detect video.mp4 # 检测场景
mixvideo scene batch-detect /videos # 批量检测
mixvideo scene split video.mp4 # 分割视频
mixvideo scene info video.mp4 # 视频信息
""",
rich_markup_mode="rich",
no_args_is_help=True
)
# 添加场景检测命令组到主应用
app.add_typer(scene_app, name="scene")
@app.command()
def init():
"""🚀 初始化MixVideo工作环境"""
progress_reporter.info("🚀 初始化MixVideo环境...")
# TODO: 实现初始化逻辑
progress_reporter.success("✅ 初始化完成")
def main():
"""主入口函数"""
try:
app()
except KeyboardInterrupt:
progress_reporter.error("\n👋 用户取消操作")
sys.exit(0)
except Exception as e:
progress_reporter.error(f"\n❌ [red]程序异常: {e}[/red]")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python3
"""
CLI 命令模块
"""
from .scene import scene_app
__all__ = ["scene_app"]

View File

@@ -0,0 +1,344 @@
#!/usr/bin/env python3
"""
场景检测命令模块
"""
from pathlib import Path
from typing import Optional
import typer
from python_core.cli.const import progress_reporter, console
from json import dumps
# 创建场景检测命令组
scene_app = typer.Typer(help="🎯 场景检测工具")
@scene_app.command()
def detect(
video_path: Path = typer.Argument(..., help="📹 视频文件路径", exists=True),
detector: str = typer.Option("content", help="🔧 检测器类型 (content/threshold/adaptive)"),
threshold: float = typer.Option(30.0, help="🎚️ 检测阈值 (0-100)"),
min_scene_length: float = typer.Option(1.0, help="⏱️ 最小场景长度(秒)"),
output: Optional[Path] = typer.Option(None, "--output", "-o", help="📄 输出文件路径"),
format: str = typer.Option("json", help="📋 输出格式 (json/csv/txt)")
):
"""🎯 检测单个视频的场景"""
try:
from python_core.cli.scene_detect import detector as scene_detector, DetectorType, OutputFormat
# 验证参数
try:
detector_type = DetectorType(detector)
except ValueError:
progress_reporter.error(f"❌ 无效的检测器类型: {detector}")
progress_reporter.info("💡 可用类型: content, threshold, adaptive")
raise typer.Exit(1)
try:
output_format = OutputFormat(format)
except ValueError:
progress_reporter.error(f"❌ 无效的输出格式: {format}")
progress_reporter.info("💡 可用格式: json, csv, txt")
raise typer.Exit(1)
# 执行检测
result = scene_detector.detect_scenes(
video_path, detector_type, threshold, min_scene_length
)
if not result.success:
progress_reporter.error(f"❌ 检测失败: {result.error}")
raise typer.Exit(1)
# 显示结果摘要
console.print(f"📊 检测结果摘要:")
console.print(f" 文件: {result.filename}")
console.print(f" 检测器: {result.detector_type}")
console.print(f" 阈值: {result.threshold}")
console.print(f" 场景数: {result.total_scenes}")
console.print(f" 总时长: {result.total_duration:.2f}")
console.print(f" 检测时间: {result.detection_time:.2f}")
# 显示场景详情
if result.scenes:
console.print(f"\n🎬 场景列表:")
for scene in result.scenes[:10]: # 只显示前10个场景
console.print(f" 场景 {scene.index}: {scene.start_time:.2f}s - {scene.end_time:.2f}s ({scene.duration:.2f}s)")
if len(result.scenes) > 10:
console.print(f" ... 还有 {len(result.scenes) - 10} 个场景")
# 保存结果
if output:
scene_detector.save_results(result, output, output_format)
progress_reporter.success(f"📄 结果已保存到: {output}")
return result
except Exception as e:
progress_reporter.error(f"❌ 命令执行失败: {e}")
raise typer.Exit(1)
@scene_app.command()
def batch_detect(
input_directory: Path = typer.Argument(..., help="📁 输入目录路径", exists=True),
detector: str = typer.Option("content", help="🔧 检测器类型 (content/threshold/adaptive)"),
threshold: float = typer.Option(30.0, help="🎚️ 检测阈值 (0-100)"),
recursive: bool = typer.Option(False, "--recursive", "-r", help="🔄 递归扫描子目录"),
output: Optional[Path] = typer.Option(None, "--output", "-o", help="📄 输出文件路径"),
format: str = typer.Option("json", help="📋 输出格式 (json/csv/txt)")
):
"""📦 批量检测目录中的所有视频"""
try:
from python_core.cli.scene_detect import detector as scene_detector, DetectorType, OutputFormat
# 验证参数
try:
detector_type = DetectorType(detector)
output_format = OutputFormat(format)
except ValueError as e:
progress_reporter.error(f"❌ 参数错误: {e}")
raise typer.Exit(1)
# 执行批量检测
results = scene_detector.batch_detect(
input_directory, detector_type, threshold, recursive
)
if not results:
progress_reporter.warning("⚠️ 没有检测到任何视频文件")
return
# 统计结果
successful = len([r for r in results if r.success])
failed = len(results) - successful
total_scenes = sum(r.total_scenes for r in results if r.success)
total_duration = sum(r.total_duration for r in results if r.success)
console.print(f"📊 批量检测结果:")
console.print(f" 总文件数: {len(results)}")
console.print(f" 成功: {successful}")
console.print(f" 失败: {failed}")
console.print(f" 总场景数: {total_scenes}")
console.print(f" 总时长: {total_duration:.2f}")
# 显示详细的场景信息
console.print(f"\n🎬 详细场景信息:")
for result in results:
if result.success and result.scenes:
console.print(f"\n📹 {result.filename} ({result.total_scenes} 个场景):")
for scene in result.scenes:
console.print(f" 场景 {scene.index}: {scene.start_time:.2f}s - {scene.end_time:.2f}s (时长: {scene.duration:.2f}s)")
elif result.success:
console.print(f"\n📹 {result.filename}: 无场景数据")
# 显示失败的文件
failed_files = [r for r in results if not r.success]
if failed_files:
console.print(f"\n❌ 失败的文件:")
for result in failed_files[:5]: # 只显示前5个失败文件
console.print(f" {result.filename}: {result.error}")
if len(failed_files) > 5:
console.print(f" ... 还有 {len(failed_files) - 5} 个失败文件")
# 保存结果
if output:
scene_detector.save_results(results, output, output_format)
progress_reporter.success(f"📄 结果已保存到: {output}")
return results
except Exception as e:
progress_reporter.error(f"❌ 批量检测失败: {e}")
raise typer.Exit(1)
@scene_app.command()
def compare(
video_path: Path = typer.Argument(..., help="📹 视频文件路径", exists=True),
thresholds: str = typer.Option("20,30,40", help="🎚️ 测试阈值列表(逗号分隔)"),
output: Optional[Path] = typer.Option(None, "--output", "-o", help="📄 输出文件路径")
):
"""🔬 比较不同检测器的效果"""
try:
from python_core.cli.scene_detect import detector as scene_detector
# 解析阈值列表
try:
threshold_list = [float(t.strip()) for t in thresholds.split(",")]
except ValueError:
progress_reporter.error("❌ 无效的阈值格式,请使用逗号分隔的数字")
raise typer.Exit(1)
# 执行比较
result = scene_detector.compare_detectors(video_path, threshold_list)
# 显示分析结果
analysis = result["analysis"]
console.print(f"🔬 检测器比较结果:")
console.print(f" 视频: {Path(result['video_path']).name}")
console.print(f" 总测试数: {result['total_tests']}")
console.print(f" 成功测试数: {analysis['total_successful']}")
console.print(f" 推荐检测器: {analysis['best_detector']}")
console.print(f" 建议: {analysis['recommendation']}")
# 显示详细分析
console.print(f"\n📊 各检测器表现:")
for detector_name, stats in analysis["detector_analysis"].items():
console.print(f" 🔧 {detector_name}:")
console.print(f" 平均场景数: {stats['average_scenes']:.1f}")
console.print(f" 平均检测时间: {stats['average_detection_time']:.2f}")
console.print(f" 测试次数: {stats['test_count']}")
# 显示详细测试结果
console.print(f"\n🧪 详细测试结果:")
for test_result in result["results"]:
if test_result["success"]:
console.print(f" {test_result['detector']} (阈值: {test_result['threshold']}): "
f"{test_result['scenes']} 场景, {test_result['detection_time']:.2f}s")
else:
console.print(f" {test_result['detector']} (阈值: {test_result['threshold']}): "
f"{test_result['error']}")
# 保存结果
if output:
scene_detector.save_results(result, output)
progress_reporter.success(f"📄 结果已保存到: {output}")
return result
except Exception as e:
progress_reporter.error(f"❌ 比较测试失败: {e}")
raise typer.Exit(1)
@scene_app.command()
def split(
video_path: Path = typer.Argument(..., help="📹 视频文件路径", exists=True),
detector: str = typer.Option("content", help="🔧 检测器类型 (content/threshold/adaptive)"),
threshold: float = typer.Option(30.0, help="🎚️ 检测阈值 (0-100)"),
output_dir: Optional[Path] = typer.Option(None, "--output-dir", "-d", help="📁 输出目录"),
filename_template: str = typer.Option("scene_{:03d}.mp4", help="📝 文件名模板")
):
"""✂️ 根据场景检测结果分割视频"""
try:
from python_core.cli.scene_detect import detector as scene_detector, DetectorType
from scenedetect.video_splitter import split_video_ffmpeg
# 验证参数
try:
detector_type = DetectorType(detector)
except ValueError:
progress_reporter.error(f"❌ 无效的检测器类型: {detector}")
raise typer.Exit(1)
# 设置输出目录
if output_dir is None:
output_dir = video_path.parent / f"{video_path.stem}_scenes"
output_dir.mkdir(parents=True, exist_ok=True)
# 先检测场景
progress_reporter.info("🎯 正在检测场景...")
result = scene_detector.detect_scenes(video_path, detector_type, threshold)
if not result.success:
progress_reporter.error(f"❌ 场景检测失败: {result.error}")
raise typer.Exit(1)
if not result.scenes:
progress_reporter.warning("⚠️ 未检测到任何场景")
return
# 构建场景列表PySceneDetect格式
from scenedetect import FrameTimecode
scene_list = []
# 假设视频帧率(实际应该从视频中获取)
fps = 25.0 # 默认帧率,实际使用时应该从视频文件中获取
for scene in result.scenes:
start_tc = FrameTimecode(timecode=scene.start_time, fps=fps)
end_tc = FrameTimecode(timecode=scene.end_time, fps=fps)
scene_list.append((start_tc, end_tc))
# 分割视频
progress_reporter.info(f"✂️ 正在分割视频到 {len(scene_list)} 个场景...")
try:
split_video_ffmpeg(
input_video_path=str(video_path),
scene_list=scene_list,
output_file_template=str(output_dir / filename_template),
video_name=video_path.stem,
arg_override=None,
hide_progress=False
)
progress_reporter.success(f"✅ 视频分割完成,输出到: {output_dir}")
console.print(f"📁 输出目录: {output_dir}")
console.print(f"🎬 场景数量: {len(scene_list)}")
# 列出生成的文件
output_files = list(output_dir.glob("*.mp4"))
if output_files:
console.print(f"\n📄 生成的文件:")
for file_path in sorted(output_files)[:10]:
console.print(f" {file_path.name}")
if len(output_files) > 10:
console.print(f" ... 还有 {len(output_files) - 10} 个文件")
except Exception as e:
progress_reporter.error(f"❌ 视频分割失败: {e}")
raise typer.Exit(1)
except Exception as e:
progress_reporter.error(f"❌ 分割命令执行失败: {e}")
raise typer.Exit(1)
@scene_app.command()
def info(
video_path: Path = typer.Argument(..., help="📹 视频文件路径", exists=True)
):
"""📋 显示视频基本信息"""
try:
import cv2
progress_reporter.info(f"📋 获取视频信息: {video_path.name}")
# 使用OpenCV获取视频信息
cap = cv2.VideoCapture(str(video_path))
if not cap.isOpened():
raise Exception("无法打开视频文件")
# 获取视频信息
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
duration = frame_count / fps if fps > 0 else 0
resolution = (width, height)
cap.release()
# 显示信息
console.print(f"📹 视频信息:")
console.print(f" 文件名: {video_path.name}")
console.print(f" 文件大小: {video_path.stat().st_size / (1024*1024):.2f} MB")
console.print(f" 分辨率: {resolution[0]}x{resolution[1]}")
console.print(f" 帧率: {fps:.2f} fps")
console.print(f" 总帧数: {frame_count}")
console.print(f" 时长: {duration:.2f}秒 ({duration//60:.0f}{duration%60:.0f}秒)")
progress_reporter.success(dumps({
"filename": video_path.name,
"file_size_mb": video_path.stat().st_size / (1024*1024),
"resolution": resolution,
"fps": fps,
"frame_count": frame_count,
"duration": duration
}))
except Exception as e:
progress_reporter.error(f"❌ 获取视频信息失败: {e}")
raise typer.Exit(1)

6
python_core/cli/const.py Normal file
View File

@@ -0,0 +1,6 @@
from python_core.utils.jsonrpc import create_progress_reporter
from rich.console import Console
from python_core.config import settings
console = Console()
progress_reporter = create_progress_reporter()
project_root = settings.project_root

View File

@@ -0,0 +1,365 @@
#!/usr/bin/env python3
"""
PySceneDetect 场景检测命令行工具
"""
import os
import json
import time
from pathlib import Path
from typing import Optional, List
from enum import Enum
from dataclasses import dataclass, asdict
import typer
from python_core.cli.const import progress_reporter, console, project_root
# 检查 PySceneDetect 依赖
from scenedetect import open_video, SceneManager
from scenedetect.detectors import ContentDetector, ThresholdDetector
from scenedetect.video_splitter import split_video_ffmpeg
class DetectorType(str, Enum):
"""检测器类型"""
CONTENT = "content"
THRESHOLD = "threshold"
ADAPTIVE = "adaptive"
class OutputFormat(str, Enum):
"""输出格式"""
JSON = "json"
CSV = "csv"
TXT = "txt"
@dataclass
class SceneInfo:
"""场景信息"""
index: int
start_time: float
end_time: float
duration: float
start_frame: int
end_frame: int
@dataclass
class DetectionResult:
"""检测结果"""
video_path: str
filename: str
detector_type: str
threshold: float
total_scenes: int
total_duration: float
detection_time: float
scenes: List[SceneInfo]
success: bool
error: Optional[str] = None
class SceneDetector:
"""场景检测器"""
def __init__(self):
self.supported_formats = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'}
def detect_scenes(self, video_path: Path, detector_type: DetectorType = DetectorType.CONTENT,
threshold: float = 30.0, min_scene_length: float = 1.0) -> DetectionResult:
"""检测单个视频的场景"""
start_time = time.time()
filename = video_path.name
try:
progress_reporter.info(f"🎬 开始检测: {filename}")
# 打开视频文件
video = open_video(str(video_path))
scene_manager = SceneManager()
# 根据类型添加检测器
if detector_type == DetectorType.CONTENT:
scene_manager.add_detector(ContentDetector(threshold=threshold))
progress_reporter.info(f"📊 使用内容检测器,阈值: {threshold}")
elif detector_type == DetectorType.THRESHOLD:
scene_manager.add_detector(ThresholdDetector(threshold=threshold))
progress_reporter.info(f"📊 使用阈值检测器,阈值: {threshold}")
elif detector_type == DetectorType.ADAPTIVE:
# 自适应:同时使用两种检测器
scene_manager.add_detector(ContentDetector(threshold=threshold))
scene_manager.add_detector(ThresholdDetector(threshold=threshold * 0.8))
progress_reporter.info(f"📊 使用自适应检测器,阈值: {threshold}")
# 开始检测
progress_reporter.info("🔍 正在分析视频帧...")
scene_manager.detect_scenes(video)
scene_list = scene_manager.get_scene_list()
progress_reporter.info(f"✅ 检测到 {len(scene_list)} 个场景")
# 获取视频信息
fps = video.frame_rate
total_duration = video.duration.get_seconds()
progress_reporter.info(f"📊 视频信息: {fps:.2f}fps, {total_duration:.2f}")
# 构建场景信息
scenes = []
if not scene_list:
# 如果没有检测到场景变化,整个视频就是一个场景
scene_info = SceneInfo(
index=0,
start_time=0.0,
end_time=total_duration,
duration=total_duration,
start_frame=0,
end_frame=int(total_duration * fps) if fps > 0 else 0
)
scenes.append(scene_info)
progress_reporter.info(f"📝 无场景变化,整个视频作为单一场景: {total_duration:.2f}")
else:
# 处理检测到的场景
for start_time_scene, end_time_scene in scene_list:
start_seconds = start_time_scene.get_seconds()
end_seconds = end_time_scene.get_seconds()
duration = end_seconds - start_seconds
# 跳过太短的场景
if duration < min_scene_length:
continue
scene_info = SceneInfo(
index=len(scenes),
start_time=start_seconds,
end_time=end_seconds,
duration=duration,
start_frame=start_time_scene.get_frames(),
end_frame=end_time_scene.get_frames()
)
scenes.append(scene_info)
detection_time = time.time() - start_time
result = DetectionResult(
video_path=str(video_path),
filename=filename,
detector_type=detector_type.value,
threshold=threshold,
total_scenes=len(scenes),
total_duration=total_duration,
detection_time=detection_time,
scenes=scenes,
success=True
)
progress_reporter.success(f"🎯 检测完成: {len(scenes)} 个场景,耗时 {detection_time:.2f}")
return result
except Exception as e:
detection_time = time.time() - start_time
error_msg = str(e)
progress_reporter.error(f"❌ 检测失败: {error_msg}")
return DetectionResult(
video_path=str(video_path),
filename=filename,
detector_type=detector_type.value,
threshold=threshold,
total_scenes=0,
total_duration=0.0,
detection_time=detection_time,
scenes=[],
success=False,
error=error_msg
)
def batch_detect(self, input_directory: Path, detector_type: DetectorType = DetectorType.CONTENT,
threshold: float = 30.0, recursive: bool = False) -> List[DetectionResult]:
"""批量检测场景"""
progress_reporter.info(f"📦 开始批量检测: {input_directory}")
# 扫描视频文件
video_files = self._scan_video_files(input_directory, recursive)
if not video_files:
progress_reporter.warning("⚠️ 未找到视频文件")
return []
progress_reporter.info(f"📋 找到 {len(video_files)} 个视频文件")
results = []
for i, video_file in enumerate(video_files):
progress_reporter.info(f"📊 处理进度: {i+1}/{len(video_files)} - {video_file.name}")
result = self.detect_scenes(video_file, detector_type, threshold)
results.append(result)
successful = len([r for r in results if r.success])
progress_reporter.success(f"🎉 批量检测完成: {successful}/{len(results)} 成功")
return results
def compare_detectors(self, video_path: Path, thresholds: List[float] = None) -> dict:
"""比较不同检测器效果"""
if thresholds is None:
thresholds = [20.0, 30.0, 40.0]
progress_reporter.info(f"🔬 开始检测器比较: {video_path.name}")
detectors = [DetectorType.CONTENT, DetectorType.THRESHOLD, DetectorType.ADAPTIVE]
results = []
total_tests = len(detectors) * len(thresholds)
current_test = 0
for detector in detectors:
for threshold in thresholds:
current_test += 1
progress_reporter.info(f"🧪 测试 {current_test}/{total_tests}: {detector.value} (阈值: {threshold})")
result = self.detect_scenes(video_path, detector, threshold)
results.append({
"detector": detector.value,
"threshold": threshold,
"scenes": result.total_scenes,
"duration": result.total_duration,
"detection_time": result.detection_time,
"success": result.success,
"error": result.error
})
# 分析结果
analysis = self._analyze_comparison(results)
comparison_result = {
"video_path": str(video_path),
"total_tests": total_tests,
"results": results,
"analysis": analysis
}
progress_reporter.success("🔬 检测器比较完成")
return comparison_result
def _scan_video_files(self, directory: Path, recursive: bool = False) -> List[Path]:
"""扫描视频文件"""
video_files = []
if recursive:
for ext in self.supported_formats:
video_files.extend(directory.rglob(f"*{ext}"))
else:
for ext in self.supported_formats:
video_files.extend(directory.glob(f"*{ext}"))
return sorted(video_files)
def _analyze_comparison(self, results: List[dict]) -> dict:
"""分析比较结果"""
successful_results = [r for r in results if r["success"]]
if not successful_results:
return {"message": "所有测试都失败了"}
# 按检测器分组
by_detector = {}
for result in successful_results:
detector = result["detector"]
if detector not in by_detector:
by_detector[detector] = []
by_detector[detector].append(result)
# 分析每个检测器
detector_analysis = {}
for detector, detector_results in by_detector.items():
avg_scenes = sum(r["scenes"] for r in detector_results) / len(detector_results)
avg_time = sum(r["detection_time"] for r in detector_results) / len(detector_results)
detector_analysis[detector] = {
"average_scenes": avg_scenes,
"average_detection_time": avg_time,
"test_count": len(detector_results)
}
# 推荐最佳检测器
best_detector = max(detector_analysis.keys(),
key=lambda d: detector_analysis[d]["average_scenes"])
return {
"total_successful": len(successful_results),
"detector_analysis": detector_analysis,
"best_detector": best_detector,
"recommendation": f"推荐使用 {best_detector} 检测器"
}
def save_results(self, results, output_path: Path, format: OutputFormat = OutputFormat.JSON):
"""保存检测结果"""
output_path.parent.mkdir(parents=True, exist_ok=True)
if format == OutputFormat.JSON:
self._save_json(results, output_path)
elif format == OutputFormat.CSV:
self._save_csv(results, output_path)
elif format == OutputFormat.TXT:
self._save_txt(results, output_path)
progress_reporter.success(f"📄 结果已保存: {output_path}")
def _save_json(self, results, output_path: Path):
"""保存JSON格式"""
if isinstance(results, list):
# 批量结果
data = [asdict(result) for result in results]
else:
# 单个结果或比较结果
if hasattr(results, '__dict__'):
data = asdict(results)
else:
data = results
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
def _save_csv(self, results, output_path: Path):
"""保存CSV格式"""
import csv
with open(output_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
if isinstance(results, list) and results:
# 批量结果
writer.writerow(['filename', 'detector', 'threshold', 'scenes', 'duration', 'detection_time', 'success'])
for result in results:
writer.writerow([
result.filename,
result.detector_type,
result.threshold,
result.total_scenes,
result.total_duration,
result.detection_time,
result.success
])
def _save_txt(self, results, output_path: Path):
"""保存文本格式"""
with open(output_path, 'w', encoding='utf-8') as f:
f.write("PySceneDetect 场景检测结果\n")
f.write("=" * 50 + "\n\n")
if isinstance(results, list):
# 批量结果
for result in results:
f.write(f"文件: {result.filename}\n")
f.write(f" 检测器: {result.detector_type}\n")
f.write(f" 阈值: {result.threshold}\n")
f.write(f" 场景数: {result.total_scenes}\n")
f.write(f" 总时长: {result.total_duration:.2f}\n")
f.write(f" 检测时间: {result.detection_time:.2f}\n")
f.write(f" 状态: {'成功' if result.success else '失败'}\n")
if result.scenes:
f.write(" 场景列表:\n")
for scene in result.scenes:
f.write(f" 场景 {scene.index}: {scene.start_time:.2f}s - {scene.end_time:.2f}s ({scene.duration:.2f}s)\n")
f.write("\n")
# 创建全局检测器实例
detector = SceneDetector()