fix: 添加工作流

This commit is contained in:
root
2025-07-12 12:45:21 +08:00
parent bc19461d8a
commit 81035caf0e
14 changed files with 1830 additions and 48 deletions

View File

@@ -0,0 +1,303 @@
#!/usr/bin/env python3
"""
JSON-RPC Server Command
JSON-RPC 服务器命令
Provides HTTP and WebSocket JSON-RPC server for scene detection services.
"""
from pathlib import Path
from typing import Optional
import signal
import sys
import typer
from python_core.cli.const import console
from python_core.utils.jsonrpc_server import JSONRPCServer, JSONRPCWebSocketServer, ServerConfig
# 创建子应用
jsonrpc_app = typer.Typer(help="🌐 JSON-RPC 服务器")
@jsonrpc_app.command()
def start(
host: str = typer.Option("localhost", help="🌐 服务器主机地址"),
port: int = typer.Option(8080, help="🔌 服务器端口"),
debug: bool = typer.Option(False, help="🐛 启用调试模式"),
cors: bool = typer.Option(True, help="🔗 启用CORS支持"),
websocket: bool = typer.Option(False, help="🔌 启用WebSocket服务器"),
max_request_size: int = typer.Option(1024*1024, help="📦 最大请求大小(字节)")
):
"""🚀 启动JSON-RPC服务器"""
config = ServerConfig(
host=host,
port=port,
debug=debug,
cors_enabled=cors,
max_request_size=max_request_size
)
console.print(f"🚀 [bold blue]启动JSON-RPC服务器[/bold blue]")
console.print(f"📍 地址: {host}:{port}")
console.print(f"🔧 模式: {'WebSocket' if websocket else 'HTTP'}")
console.print(f"🐛 调试: {'启用' if debug else '禁用'}")
console.print(f"🔗 CORS: {'启用' if cors else '禁用'}")
# 导入并注册所有JSON-RPC方法
console.print("📋 注册JSON-RPC方法...")
try:
# 导入场景检测模块以注册方法
from python_core.cli.scene_detect import detector
console.print("✅ 场景检测方法已注册")
except Exception as e:
console.print(f"⚠️ 注册方法时出错: {e}")
try:
if websocket:
# WebSocket服务器
import asyncio
server = JSONRPCWebSocketServer(config)
# 注册信号处理
def signal_handler(sig, frame):
console.print("\n🛑 [yellow]收到停止信号,正在关闭服务器...[/yellow]")
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# 启动异步服务器
asyncio.run(server.start())
else:
# HTTP服务器
server = JSONRPCServer(config)
# 注册信号处理
def signal_handler(sig, frame):
console.print("\n🛑 [yellow]收到停止信号,正在关闭服务器...[/yellow]")
server.stop()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# 启动服务器
server.start(blocking=True)
except Exception as e:
console.print(f"❌ [red]服务器启动失败: {e}[/red]")
raise typer.Exit(1)
@jsonrpc_app.command()
def test(
host: str = typer.Option("localhost", help="🌐 服务器主机地址"),
port: int = typer.Option(8080, help="🔌 服务器端口"),
method: str = typer.Option("scene.detect", help="🎯 测试方法名"),
video_path: Optional[str] = typer.Option(None, help="📹 测试视频路径")
):
"""🧪 测试JSON-RPC服务器"""
import requests
import json
if not video_path:
video_path = "assets/1/1752032011698.mp4" # 默认测试视频
console.print(f"🧪 [bold blue]测试JSON-RPC服务器[/bold blue]")
console.print(f"📍 服务器: http://{host}:{port}")
console.print(f"🎯 方法: {method}")
console.print(f"📹 视频: {video_path}")
# 准备测试请求
test_requests = {
"scene.detect": {
"video_path": video_path,
"detector_type": "content",
"threshold": 30.0,
"min_scene_length": 1.0
},
"scene.get_video_info": {
"video_path": video_path
},
"scene.detect_workflow": {
"video_path": video_path,
"detector_type": "content",
"threshold": 15.0,
"enable_ai_analysis": False
}
}
params = test_requests.get(method, {"video_path": video_path})
payload = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": 1
}
try:
console.print("📤 发送请求...")
response = requests.post(
f"http://{host}:{port}",
json=payload,
headers={'Content-Type': 'application/json'},
timeout=30
)
console.print(f"📥 响应状态: {response.status_code}")
if response.status_code == 200:
result = response.json()
console.print("✅ [green]请求成功[/green]")
console.print("📋 响应内容:")
console.print(json.dumps(result, indent=2, ensure_ascii=False))
else:
console.print(f"❌ [red]请求失败: {response.text}[/red]")
except requests.exceptions.ConnectionError:
console.print(f"❌ [red]无法连接到服务器 http://{host}:{port}[/red]")
console.print("💡 请确保服务器已启动")
raise typer.Exit(1)
except Exception as e:
console.print(f"❌ [red]测试失败: {e}[/red]")
raise typer.Exit(1)
@jsonrpc_app.command()
def methods():
"""📋 列出可用的JSON-RPC方法"""
console.print("📋 [bold blue]可用的JSON-RPC方法[/bold blue]")
console.print("=" * 60)
methods_info = [
{
"method": "scene.detect",
"description": "基础场景检测",
"params": ["video_path", "detector_type?", "threshold?", "min_scene_length?"]
},
{
"method": "scene.detect_workflow",
"description": "LangGraph工作流场景检测",
"params": ["video_path", "detector_type?", "threshold?", "min_scene_length?",
"output_path?", "output_format?", "enable_ai_analysis?"]
},
{
"method": "scene.get_video_info",
"description": "获取视频信息",
"params": ["video_path"]
},
{
"method": "scene.batch_detect",
"description": "批量场景检测",
"params": ["directory", "detector_type?", "threshold?", "min_scene_length?",
"output_dir?", "output_format?"]
}
]
for info in methods_info:
console.print(f"\n🎯 [bold]{info['method']}[/bold]")
console.print(f" 📝 {info['description']}")
console.print(f" 📋 参数: {', '.join(info['params'])}")
console.print("\n💡 [yellow]参数说明:[/yellow]")
console.print(" • ? 表示可选参数")
console.print(" • detector_type: content/threshold/adaptive")
console.print(" • output_format: json/csv/txt")
console.print(" • threshold: 0-100")
@jsonrpc_app.command()
def client_example():
"""📖 显示客户端调用示例"""
console.print("📖 [bold blue]JSON-RPC 客户端调用示例[/bold blue]")
console.print("=" * 60)
examples = [
{
"title": "Python requests 示例",
"code": '''import requests
import json
def call_scene_detect(video_path, threshold=30.0):
payload = {
"jsonrpc": "2.0",
"method": "scene.detect",
"params": {
"video_path": video_path,
"threshold": threshold
},
"id": 1
}
response = requests.post(
"http://localhost:8080",
json=payload,
headers={'Content-Type': 'application/json'}
)
return response.json()
# 调用示例
result = call_scene_detect("video.mp4", 15.0)
print(result)'''
},
{
"title": "curl 命令示例",
"code": '''curl -X POST http://localhost:8080 \\
-H "Content-Type: application/json" \\
-d '{
"jsonrpc": "2.0",
"method": "scene.detect",
"params": {
"video_path": "video.mp4",
"threshold": 15.0
},
"id": 1
}'
'''
},
{
"title": "JavaScript fetch 示例",
"code": '''async function detectScenes(videoPath, threshold = 30.0) {
const response = await fetch('http://localhost:8080', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'scene.detect',
params: {
video_path: videoPath,
threshold: threshold
},
id: 1
})
});
return await response.json();
}
// 调用示例
detectScenes('video.mp4', 15.0).then(result => {
console.log(result);
});'''
}
]
for example in examples:
console.print(f"\n📝 [bold]{example['title']}[/bold]")
console.print("```")
console.print(example['code'])
console.print("```")
if __name__ == "__main__":
jsonrpc_app()

View File

@@ -19,7 +19,9 @@ def detect(
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)")
format: str = typer.Option("json", help="📋 输出格式 (json/csv/txt)"),
use_workflow: bool = typer.Option(False, "--workflow", help="🔄 使用LangGraph工作流"),
enable_ai: bool = typer.Option(True, "--ai/--no-ai", help="🧠 启用AI分析")
):
"""🎯 检测单个视频的场景"""
try:
@@ -40,39 +42,97 @@ def detect(
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
# 选择执行方式
if use_workflow:
# 使用LangGraph工作流
progress_reporter.info("🔄 使用LangGraph工作流进行检测...")
workflow_result = scene_detector.detect_with_workflow(
video_path, detector_type, threshold, min_scene_length,
output, output_format, enable_ai
)
result = workflow_result.get("detection_result")
ai_analysis = workflow_result.get("ai_analysis")
video_info = workflow_result.get("video_info")
errors = workflow_result.get("errors", [])
if errors:
for error in errors:
progress_reporter.error(f"{error}")
raise typer.Exit(1)
if not result or not result.success:
progress_reporter.error(f"❌ 工作流检测失败: {result.error if result else '未知错误'}")
raise typer.Exit(1)
# 显示工作流结果
console.print(f"🔄 LangGraph工作流检测完成")
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 video_info:
console.print(f"\n📹 视频信息:")
console.print(f" 分辨率: {video_info.get('resolution', 'Unknown')}")
console.print(f" 帧率: {video_info.get('fps', 0):.2f} fps")
console.print(f" 总帧数: {video_info.get('frame_count', 0)}")
# 显示AI分析结果
if ai_analysis and enable_ai:
console.print(f"\n🧠 AI分析结果:")
console.print(f"{ai_analysis}")
# 显示场景详情
if result.scenes:
console.print(f"\n🎬 场景列表:")
for scene in result.scenes[: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} 个场景")
return workflow_result
else:
# 使用传统方法
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}")
@@ -342,3 +402,148 @@ def info(
except Exception as e:
progress_reporter.error(f"❌ 获取视频信息失败: {e}")
raise typer.Exit(1)
@scene_app.command()
def workflow(
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)"),
enable_ai: bool = typer.Option(True, "--ai/--no-ai", help="🧠 启用AI分析"),
interactive: bool = typer.Option(False, "--interactive", "-i", help="🔄 交互式工作流")
):
"""🔄 使用LangGraph工作流进行智能场景检测"""
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)
console.print("🔄 [bold blue]LangGraph智能场景检测工作流[/bold blue]")
console.print("=" * 60)
if interactive:
# 交互式模式
console.print("🎯 交互式模式启动...")
# 确认参数
console.print(f"\n📋 检测参数:")
console.print(f" 视频文件: {video_path}")
console.print(f" 检测器: {detector}")
console.print(f" 阈值: {threshold}")
console.print(f" 最小场景长度: {min_scene_length}")
console.print(f" AI分析: {'启用' if enable_ai else '禁用'}")
if not typer.confirm("\n是否继续执行?"):
console.print("❌ 用户取消操作")
return
# 执行工作流
progress_reporter.info("🚀 启动LangGraph工作流...")
workflow_result = scene_detector.detect_with_workflow(
video_path, detector_type, threshold, min_scene_length,
output, output_format, enable_ai
)
result = workflow_result.get("detection_result")
ai_analysis = workflow_result.get("ai_analysis")
video_info = workflow_result.get("video_info")
workflow_state = workflow_result.get("workflow_state")
errors = workflow_result.get("errors", [])
# 检查错误
if errors:
console.print("\n❌ [red]工作流执行中发现错误:[/red]")
for error in errors:
console.print(f"{error}")
raise typer.Exit(1)
if not result or not result.success:
progress_reporter.error(f"❌ 工作流检测失败: {result.error if result else '未知错误'}")
raise typer.Exit(1)
# 显示完整结果
console.print("\n" + "=" * 60)
console.print("🎉 [bold green]LangGraph工作流执行完成[/bold green]")
console.print("=" * 60)
# 工作流状态
console.print(f"\n🔄 工作流状态: [bold]{workflow_state}[/bold]")
# 视频信息
if video_info:
console.print(f"\n📹 [bold]视频信息[/bold]:")
console.print(f" 文件名: {result.filename}")
console.print(f" 分辨率: {video_info.get('resolution', 'Unknown')}")
console.print(f" 帧率: {video_info.get('fps', 0):.2f} fps")
console.print(f" 总帧数: {video_info.get('frame_count', 0):,}")
console.print(f" 时长: {result.total_duration:.2f}")
# 检测结果
console.print(f"\n🎯 [bold]检测结果[/bold]:")
console.print(f" 检测器类型: {result.detector_type}")
console.print(f" 检测阈值: {result.threshold}")
console.print(f" 场景数量: [bold green]{result.total_scenes}[/bold green]")
console.print(f" 检测耗时: {result.detection_time:.2f}")
# 场景详情
if result.scenes:
console.print(f"\n🎬 [bold]场景详情[/bold]:")
for scene in result.scenes:
duration_color = "green" if scene.duration >= 2.0 else "yellow" if scene.duration >= 1.0 else "red"
console.print(
f" 场景 {scene.index:2d}: "
f"{scene.start_time:6.2f}s - {scene.end_time:6.2f}s "
f"([{duration_color}]{scene.duration:5.2f}s[/{duration_color}])"
)
# AI分析结果
if ai_analysis and enable_ai:
console.print(f"\n🧠 [bold]AI智能分析[/bold]:")
console.print("-" * 50)
console.print(ai_analysis)
console.print("-" * 50)
elif enable_ai:
console.print(f"\n⚠️ AI分析不可用")
# 保存信息
if output:
console.print(f"\n💾 结果已保存到: [bold]{output}[/bold]")
# 交互式后续操作
if interactive:
console.print(f"\n🎯 [bold]后续操作选项[/bold]:")
console.print("1. 保存结果到文件")
console.print("2. 调整参数重新检测")
console.print("3. 分割视频")
console.print("4. 退出")
choice = typer.prompt("请选择操作 (1-4)", type=int, default=4)
if choice == 1 and not output:
output_path = typer.prompt("请输入输出文件路径", type=str)
scene_detector.save_results(result, Path(output_path), output_format)
console.print(f"✅ 结果已保存到: {output_path}")
elif choice == 2:
console.print("🔄 参数调整功能开发中...")
elif choice == 3:
console.print("✂️ 视频分割功能开发中...")
else:
console.print("👋 感谢使用LangGraph工作流")
return workflow_result
except Exception as e:
progress_reporter.error(f"❌ 工作流命令执行失败: {e}")
raise typer.Exit(1)