427 lines
11 KiB
Markdown
427 lines
11 KiB
Markdown
# PySceneDetect 视频拆分服务 JSON-RPC 接口
|
||
|
||
## 🎯 概述
|
||
|
||
PySceneDetect视频拆分服务现在支持JSON-RPC协议,提供标准化的API接口,便于与其他系统集成。
|
||
|
||
## 📡 JSON-RPC 协议
|
||
|
||
### 输出格式
|
||
所有命令的输出都遵循JSON-RPC 2.0规范:
|
||
|
||
#### 成功响应
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": null,
|
||
"result": {
|
||
// 具体的结果数据
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": null,
|
||
"error": {
|
||
"code": "ERROR_CODE",
|
||
"message": "错误描述"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔧 可用命令
|
||
|
||
### 1. analyze - 视频分析
|
||
|
||
#### 命令格式
|
||
```bash
|
||
python python_core/services/video_splitter.py analyze <video_path> [--threshold <value>]
|
||
```
|
||
|
||
#### 参数
|
||
- `video_path`: 视频文件路径
|
||
- `--threshold`: 检测阈值 (默认: 30.0)
|
||
|
||
#### 成功响应示例
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": null,
|
||
"result": {
|
||
"success": true,
|
||
"video_path": "/path/to/video.mp4",
|
||
"total_scenes": 3,
|
||
"total_duration": 10.04,
|
||
"average_scene_duration": 3.35,
|
||
"scenes": [
|
||
{
|
||
"scene_number": 1,
|
||
"start_time": 0.0,
|
||
"end_time": 4.04,
|
||
"duration": 4.04,
|
||
"start_frame": 0,
|
||
"end_frame": 97
|
||
},
|
||
{
|
||
"scene_number": 2,
|
||
"start_time": 4.04,
|
||
"end_time": 8.04,
|
||
"duration": 4.0,
|
||
"start_frame": 97,
|
||
"end_frame": 193
|
||
},
|
||
{
|
||
"scene_number": 3,
|
||
"start_time": 8.04,
|
||
"end_time": 10.04,
|
||
"duration": 2.0,
|
||
"start_frame": 193,
|
||
"end_frame": 241
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 错误响应示例
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": null,
|
||
"error": {
|
||
"code": "ANALYSIS_FAILED",
|
||
"message": "Video file not found: /path/to/video.mp4"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. detect_scenes - 场景检测
|
||
|
||
#### 命令格式
|
||
```bash
|
||
python python_core/services/video_splitter.py detect_scenes <video_path> [--threshold <value>] [--detector <type>]
|
||
```
|
||
|
||
#### 参数
|
||
- `video_path`: 视频文件路径
|
||
- `--threshold`: 检测阈值 (默认: 30.0)
|
||
- `--detector`: 检测器类型 ("content" 或 "threshold", 默认: "content")
|
||
|
||
#### 成功响应示例
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": null,
|
||
"result": {
|
||
"success": true,
|
||
"video_path": "/path/to/video.mp4",
|
||
"total_scenes": 3,
|
||
"scenes": [
|
||
{
|
||
"scene_number": 1,
|
||
"start_time": 0.0,
|
||
"end_time": 4.04,
|
||
"duration": 4.04,
|
||
"start_frame": 0,
|
||
"end_frame": 97
|
||
}
|
||
],
|
||
"detection_settings": {
|
||
"threshold": 30.0,
|
||
"detector_type": "content"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. split - 视频拆分
|
||
|
||
#### 命令格式
|
||
```bash
|
||
python python_core/services/video_splitter.py split <video_path> [options...]
|
||
```
|
||
|
||
#### 参数
|
||
- `video_path`: 视频文件路径
|
||
- `--threshold`: 检测阈值 (默认: 30.0)
|
||
- `--detector`: 检测器类型 (默认: "content")
|
||
- `--output-dir`: 输出目录
|
||
- `--output-base`: 输出基础目录
|
||
|
||
#### 成功响应示例
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": null,
|
||
"result": {
|
||
"success": true,
|
||
"message": "Successfully split video into 3 scenes",
|
||
"input_video": "/path/to/video.mp4",
|
||
"output_directory": "/tmp/video_splits/video_20250711_201530",
|
||
"scenes": [
|
||
{
|
||
"scene_number": 1,
|
||
"start_time": 0.0,
|
||
"end_time": 4.04,
|
||
"duration": 4.04,
|
||
"start_frame": 0,
|
||
"end_frame": 97
|
||
}
|
||
],
|
||
"output_files": [
|
||
"/tmp/video_splits/video_20250711_201530/video-Scene-001.mp4",
|
||
"/tmp/video_splits/video_20250711_201530/video-Scene-002.mp4",
|
||
"/tmp/video_splits/video_20250711_201530/video-Scene-003.mp4"
|
||
],
|
||
"total_scenes": 3,
|
||
"total_duration": 10.04,
|
||
"processing_time": 3.02
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 错误响应示例
|
||
```json
|
||
{
|
||
"jsonrpc": "2.0",
|
||
"id": null,
|
||
"error": {
|
||
"code": "SPLIT_FAILED",
|
||
"message": "FFmpeg failed with return code: 1"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔍 错误代码
|
||
|
||
| 错误代码 | 描述 | 可能原因 |
|
||
|---------|------|----------|
|
||
| `ANALYSIS_FAILED` | 视频分析失败 | 文件不存在、格式不支持 |
|
||
| `SPLIT_FAILED` | 视频拆分失败 | FFmpeg错误、磁盘空间不足 |
|
||
| `INVALID_COMMAND` | 无效命令 | 命令名称错误 |
|
||
| `INTERNAL_ERROR` | 内部错误 | 程序异常、依赖缺失 |
|
||
|
||
## 💻 编程接口使用
|
||
|
||
### Python 示例
|
||
```python
|
||
import subprocess
|
||
import json
|
||
|
||
def call_video_splitter(command, video_path, **kwargs):
|
||
"""调用视频拆分服务"""
|
||
cmd = [
|
||
"python", "python_core/services/video_splitter.py",
|
||
command, video_path
|
||
]
|
||
|
||
# 添加参数
|
||
for key, value in kwargs.items():
|
||
cmd.extend([f"--{key.replace('_', '-')}", str(value)])
|
||
|
||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||
|
||
if result.returncode == 0:
|
||
# 解析JSON-RPC响应
|
||
if result.stdout.startswith("JSONRPC:"):
|
||
json_str = result.stdout[8:]
|
||
return json.loads(json_str)
|
||
else:
|
||
return json.loads(result.stdout)
|
||
else:
|
||
raise Exception(f"Command failed: {result.stderr}")
|
||
|
||
# 使用示例
|
||
try:
|
||
# 分析视频
|
||
response = call_video_splitter("analyze", "video.mp4", threshold=30.0)
|
||
if "result" in response:
|
||
result = response["result"]
|
||
print(f"检测到 {result['total_scenes']} 个场景")
|
||
|
||
# 拆分视频
|
||
response = call_video_splitter("split", "video.mp4", threshold=30.0)
|
||
if "result" in response:
|
||
result = response["result"]
|
||
if result["success"]:
|
||
print(f"拆分成功: {len(result['output_files'])} 个文件")
|
||
else:
|
||
print(f"拆分失败: {result['message']}")
|
||
|
||
except Exception as e:
|
||
print(f"调用失败: {e}")
|
||
```
|
||
|
||
### Node.js 示例
|
||
```javascript
|
||
const { spawn } = require('child_process');
|
||
|
||
function callVideoSplitter(command, videoPath, options = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
const args = [
|
||
'python_core/services/video_splitter.py',
|
||
command,
|
||
videoPath
|
||
];
|
||
|
||
// 添加参数
|
||
for (const [key, value] of Object.entries(options)) {
|
||
args.push(`--${key.replace(/_/g, '-')}`, String(value));
|
||
}
|
||
|
||
const process = spawn('python3', args);
|
||
let stdout = '';
|
||
let stderr = '';
|
||
|
||
process.stdout.on('data', (data) => {
|
||
stdout += data.toString();
|
||
});
|
||
|
||
process.stderr.on('data', (data) => {
|
||
stderr += data.toString();
|
||
});
|
||
|
||
process.on('close', (code) => {
|
||
if (code === 0) {
|
||
try {
|
||
// 解析JSON-RPC响应
|
||
let jsonStr = stdout.trim();
|
||
if (jsonStr.startsWith('JSONRPC:')) {
|
||
jsonStr = jsonStr.substring(8);
|
||
}
|
||
const response = JSON.parse(jsonStr);
|
||
resolve(response);
|
||
} catch (error) {
|
||
reject(new Error(`JSON parse error: ${error.message}`));
|
||
}
|
||
} else {
|
||
reject(new Error(`Command failed: ${stderr}`));
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// 使用示例
|
||
async function example() {
|
||
try {
|
||
// 分析视频
|
||
const analyzeResponse = await callVideoSplitter('analyze', 'video.mp4', {
|
||
threshold: 30.0
|
||
});
|
||
|
||
if (analyzeResponse.result) {
|
||
console.log(`检测到 ${analyzeResponse.result.total_scenes} 个场景`);
|
||
}
|
||
|
||
// 拆分视频
|
||
const splitResponse = await callVideoSplitter('split', 'video.mp4', {
|
||
threshold: 30.0,
|
||
'output-dir': './output'
|
||
});
|
||
|
||
if (splitResponse.result && splitResponse.result.success) {
|
||
console.log(`拆分成功: ${splitResponse.result.output_files.length} 个文件`);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('调用失败:', error.message);
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔧 集成建议
|
||
|
||
### 1. 错误处理
|
||
```python
|
||
def safe_call_video_splitter(command, video_path, **kwargs):
|
||
try:
|
||
response = call_video_splitter(command, video_path, **kwargs)
|
||
|
||
if "error" in response:
|
||
# JSON-RPC错误
|
||
error = response["error"]
|
||
raise Exception(f"[{error['code']}] {error['message']}")
|
||
|
||
if "result" in response:
|
||
result = response["result"]
|
||
if isinstance(result, dict) and not result.get("success", True):
|
||
# 业务逻辑错误
|
||
raise Exception(f"Operation failed: {result.get('error', 'Unknown error')}")
|
||
|
||
return result
|
||
|
||
return response
|
||
|
||
except json.JSONDecodeError as e:
|
||
raise Exception(f"Invalid JSON response: {e}")
|
||
except subprocess.CalledProcessError as e:
|
||
raise Exception(f"Process error: {e}")
|
||
```
|
||
|
||
### 2. 异步处理
|
||
```python
|
||
import asyncio
|
||
import concurrent.futures
|
||
|
||
async def async_video_splitter(command, video_path, **kwargs):
|
||
"""异步调用视频拆分服务"""
|
||
loop = asyncio.get_event_loop()
|
||
|
||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||
future = executor.submit(call_video_splitter, command, video_path, **kwargs)
|
||
return await loop.run_in_executor(None, lambda: future.result())
|
||
|
||
# 使用示例
|
||
async def process_videos(video_list):
|
||
tasks = []
|
||
for video_path in video_list:
|
||
task = async_video_splitter("analyze", video_path)
|
||
tasks.append(task)
|
||
|
||
results = await asyncio.gather(*tasks)
|
||
return results
|
||
```
|
||
|
||
### 3. 批量处理
|
||
```python
|
||
def batch_process_videos(video_list, command="analyze", **kwargs):
|
||
"""批量处理视频"""
|
||
results = []
|
||
|
||
for video_path in video_list:
|
||
try:
|
||
result = call_video_splitter(command, video_path, **kwargs)
|
||
results.append({
|
||
"video_path": video_path,
|
||
"success": True,
|
||
"result": result
|
||
})
|
||
except Exception as e:
|
||
results.append({
|
||
"video_path": video_path,
|
||
"success": False,
|
||
"error": str(e)
|
||
})
|
||
|
||
return results
|
||
```
|
||
|
||
## 🎉 总结
|
||
|
||
PySceneDetect视频拆分服务的JSON-RPC接口提供了:
|
||
|
||
- ✅ **标准化协议**: 遵循JSON-RPC 2.0规范
|
||
- ✅ **完整功能**: 支持分析、检测、拆分三种操作
|
||
- ✅ **详细响应**: 包含完整的场景信息和处理结果
|
||
- ✅ **错误处理**: 标准化的错误代码和消息
|
||
- ✅ **易于集成**: 支持多种编程语言调用
|
||
|
||
现在可以轻松地将视频拆分功能集成到任何系统中!
|
||
|
||
---
|
||
|
||
*JSON-RPC接口 - 让视频拆分服务更易集成!*
|