Compare commits
2 Commits
f422eb32c1
...
b318144230
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b318144230 | ||
|
|
56490460fa |
250
README.md
250
README.md
@@ -1,97 +1,215 @@
|
||||
---
|
||||
GMapiServer
|
||||
|
||||
# GMapiServer
|
||||
GMapiServer 是一个基于 Python 和 Flask 构建的高性能 API 服务器,支持通过 URL 输入文件并输出下载链接,提供同步和异步两种处理模式。内置缓存自动清理机制(默认保留 2 小时),支持多种工具接口调用,包括实时任务状态查询和视频帧处理功能。
|
||||
|
||||
**GMapiServer** 是一个基于 Python 和 Flask 构建的高性能 API 服务器,支持通过 URL 输入文件并输出下载链接,提供异步处理功能,确保主进程不被阻塞。内置缓存自动清理机制(默认保留 2 小时),支持多种工具接口调用。
|
||||
🚀 项目简介
|
||||
|
||||
---
|
||||
GMapiServer 是一个轻量级、模块化的 API 服务框架,旨在为开发者提供以下核心功能:
|
||||
|
||||
## 🚀 项目简介
|
||||
• URL 输入与输出:支持通过 URL 提交文件,返回生成文件的下载链接。
|
||||
|
||||
**GMapiServer** 是一个轻量级、模块化的 API 服务框架,旨在为开发者提供以下核心功能:
|
||||
• 异步处理:任务在后台异步执行,不阻塞主进程,提升服务器并发性能。
|
||||
|
||||
- **URL 输入与输出**:支持通过 URL 提交文件,返回生成文件的下载链接。
|
||||
- **异步处理**:任务在后台异步执行,不阻塞主进程,提升服务器并发性能。
|
||||
- **自动缓存清理**:内置定时清理机制,自动删除过期的缓存文件(包括输出文件),默认保留时间 2 小时。
|
||||
- **多工具接口集成**:集成 FFmpeg 和 Sanjuuni 工具接口,支持在线调用。
|
||||
• 自动缓存清理:内置定时清理机制,自动删除过期的缓存文件(包括输出文件),默认保留时间 2 小时。
|
||||
|
||||
---
|
||||
• 多工具接口集成:集成 FFmpeg 和 Sanjuuni 工具接口,支持在线调用。
|
||||
|
||||
## 🛠️ 技术栈
|
||||
• 实时任务状态:提供任务状态查询接口,支持日志实时推送。
|
||||
|
||||
- **编程语言**: Python
|
||||
- **Web 框架**: Flask
|
||||
- **异步处理**: 基于 `threading`
|
||||
- **缓存清理**: 定时任务
|
||||
- **依赖管理**: pip
|
||||
• 视频帧提取:支持从视频中提取指定分辨率、帧率的图片帧,可打包为二进制格式。
|
||||
|
||||
---
|
||||
🛠️ 技术栈
|
||||
|
||||
## 🧩 支持的接口
|
||||
• 编程语言: Python
|
||||
|
||||
### 1. FFmpeg 工具接口
|
||||
- **功能**: 在线调用 FFmpeg 工具进行视频/音频处理(如转码、裁剪、合并等)。
|
||||
- **接口文档**: [FFmpegApi 文档](https://www.liulikeji.cn/archives/FFmpegApi)
|
||||
• Web 框架: Flask
|
||||
|
||||
### 2. Sanjuuni 工具接口
|
||||
- **功能**: 在线调用 [Sanjuuni 工具](https://github.com/MCJack123/sanjuuni/tree/master)(具体功能需参考其官方文档)。
|
||||
- **接口文档**: [SanjuuniApi 文档](https://www.liulikeji.cn/archives/SanjuuniApi)
|
||||
• 异步处理: 基于 threading
|
||||
|
||||
• 缓存清理: 定时任务
|
||||
|
||||
### 新增异步接口调用
|
||||
- **接口文档**: [异步任务处理接口文档](https://www.liulikeji.cn/archives/wei-ming-ming-wen-zhang)
|
||||
• 依赖管理: pip
|
||||
|
||||
---
|
||||
🧩 支持的接口
|
||||
|
||||
## 📦 部署与使用
|
||||
1. FFmpeg 工具接口
|
||||
|
||||
• 功能: 在线调用 FFmpeg 工具进行视频/音频处理(如转码、裁剪、合并等)。
|
||||
|
||||
• 接口文档: https://www.liulikeji.cn/archives/FFmpegApi
|
||||
|
||||
• 同步接口: POST /api/ffmpeg
|
||||
|
||||
• 异步接口: POST /api/ffmpeg/async
|
||||
|
||||
2. Sanjuuni 工具接口
|
||||
|
||||
• 功能: 在线调用 https://github.com/MCJack123/sanjuuni/tree/master(具体功能需参考其官方文档)。
|
||||
|
||||
• 接口文档: https://www.liulikeji.cn/archives/SanjuuniApi
|
||||
|
||||
• 同步接口: POST /api/sanjuuni
|
||||
|
||||
• 异步接口: POST /api/sanjuuni/async
|
||||
|
||||
3. 视频帧提取接口
|
||||
|
||||
• 功能: 从视频中提取指定分辨率、帧率的图片帧,支持强制分辨率调整和填充。
|
||||
|
||||
• 异步接口: POST /api/video_frame/async
|
||||
|
||||
• 参数:
|
||||
|
||||
• video_url: 视频文件 URL
|
||||
|
||||
• w: 输出宽度
|
||||
|
||||
• h: 输出高度
|
||||
|
||||
• fps: 输出帧率
|
||||
|
||||
• force_resolution: 是否强制调整分辨率
|
||||
|
||||
• pad_to_target: 是否填充到目标分辨率
|
||||
|
||||
4. 异步任务状态查询
|
||||
|
||||
• 功能: 查询异步任务状态和进度,支持日志实时推送。
|
||||
|
||||
• 接口: GET /api/task/<task_id>
|
||||
|
||||
• 返回字段:
|
||||
|
||||
• status: 任务状态(pending/running/completed/error)
|
||||
|
||||
• progress: 进度百分比
|
||||
|
||||
• new_logs: 新增的日志条目
|
||||
|
||||
• result: 完成后的结果(包含下载URL)
|
||||
|
||||
• error: 错误信息(如果状态为error)
|
||||
|
||||
5. 帧打包接口
|
||||
|
||||
• 功能: 将多个帧图片打包为二进制格式,便于批量下载。
|
||||
|
||||
• 接口: POST /api/framepack
|
||||
|
||||
• 参数:
|
||||
|
||||
• urls: 帧图片URL列表,格式为 /frames/<job_id>/<filename>.png
|
||||
|
||||
• 返回: 二进制流,格式为帧数(4字节) + 各帧数据
|
||||
|
||||
6. 文件下载接口
|
||||
|
||||
• 功能: 下载处理完成的文件。
|
||||
|
||||
• 接口: GET /download/<file_id>/<filename>
|
||||
|
||||
7. 任务列表查询
|
||||
|
||||
• 功能: 列出所有任务状态(调试用)。
|
||||
|
||||
• 接口: GET /api/tasks
|
||||
|
||||
8. 健康检查
|
||||
|
||||
• 功能: 服务健康检查。
|
||||
|
||||
• 接口: GET /health
|
||||
|
||||
📦 部署与使用
|
||||
|
||||
1. 安装依赖
|
||||
|
||||
### 1. 安装依赖
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 2. 启动服务器
|
||||
```bash
|
||||
|
||||
2. 启动服务器
|
||||
|
||||
python main.py --port 5000
|
||||
```
|
||||
### 3. 指定公网地址
|
||||
```python
|
||||
##sanjuuni_utils.py
|
||||
return {
|
||||
'status': 'success',
|
||||
'download_url': f"http://ffmpeg.liulikeji.cn/download/{output_id}/{output_filename}", #外部访问地址
|
||||
'file_id': output_id,
|
||||
'temp_dir': temp_dir # 返回临时目录路径
|
||||
}
|
||||
|
||||
##ffmpeg_utils.py
|
||||
return {
|
||||
'status': 'success',
|
||||
'download_url': f"http://ffmpeg.liulikeji.cn/download/{output_id}/{output_filename}", #外部访问地址
|
||||
'file_id': output_id,
|
||||
'temp_dir': temp_dir # 返回临时目录路径
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## ⏰ 自动缓存清理配置
|
||||
- **默认保留时间**: 2 小时
|
||||
- **自定义配置**:
|
||||
```python
|
||||
|
||||
3. 异步任务使用示例
|
||||
|
||||
创建异步FFmpeg任务
|
||||
|
||||
curl -X POST http://localhost:5000/api/ffmpeg/async \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input_url": "http://example.com/input.mp4",
|
||||
"output_format": "mp4",
|
||||
"args": ["-c:v", "libx264", "-crf", "23"]
|
||||
}'
|
||||
|
||||
|
||||
查询任务状态
|
||||
|
||||
curl http://localhost:5000/api/task/<task_id>
|
||||
|
||||
|
||||
4. 指定公网地址
|
||||
|
||||
在相应工具模块中配置下载URL:
|
||||
# sanjuuni_utils.py
|
||||
return {
|
||||
'status': 'success',
|
||||
'download_url': f"http://ffmpeg.liulikeji.cn/download/{output_id}/{output_filename}", # 外部访问地址
|
||||
'file_id': output_id,
|
||||
'temp_dir': temp_dir
|
||||
}
|
||||
|
||||
# ffmpeg_utils.py
|
||||
return {
|
||||
'status': 'success',
|
||||
'download_url': f"http://ffmpeg.liulikeji.cn/download/{output_id}/{output_filename}", # 外部访问地址
|
||||
'file_id': output_id,
|
||||
'temp_dir': temp_dir
|
||||
}
|
||||
|
||||
|
||||
⏰ 自动缓存清理配置
|
||||
|
||||
• 默认保留时间: 2 小时
|
||||
|
||||
• 自定义配置:
|
||||
# 在logging_config.py文件中修改缓存清理时间
|
||||
CLEANUP_INTERVAL = 3600 # 清理临时文件的间隔(秒)
|
||||
FILE_EXPIRY = 7200 # 文件过期时间(秒)
|
||||
```
|
||||
---
|
||||
|
||||
|
||||
## ⚠️ 注意事项
|
||||
1. **文件合法性**: 请确保上传文件符合法律法规,不得用于非法用途。
|
||||
2. **缓存文件**: 系统会自动清理过期文件,请及时下载生成的输出文件。
|
||||
🔧 异步任务处理流程
|
||||
|
||||
---
|
||||
1. 任务创建: 客户端提交任务请求,服务器返回任务ID和状态查询URL
|
||||
2. 后台处理: 任务在独立线程中执行,不阻塞主进程
|
||||
3. 状态查询: 客户端可轮询状态接口获取任务进度和实时日志
|
||||
4. 结果获取: 任务完成后可通过状态接口或文件下载接口获取结果
|
||||
5. 自动清理: 任务结果文件在指定时间后自动清理
|
||||
|
||||
⚠️ 注意事项
|
||||
|
||||
1. 文件合法性: 请确保上传文件符合法律法规,不得用于非法用途。
|
||||
2. 缓存文件: 系统会自动清理过期文件,请及时下载生成的输出文件。
|
||||
3. 异步任务: 异步任务结果在服务器重启后会丢失,请及时处理完成的任务。
|
||||
4. 资源限制: 视频帧提取和转码操作可能消耗较多CPU和内存资源。
|
||||
|
||||
📝 贡献与反馈
|
||||
|
||||
## 📝 贡献与反馈
|
||||
欢迎提交 Issue 或 Pull Request!
|
||||
如有问题,请联系:[xingluo01@liulikeji.cn] 或 [qq:180877430]
|
||||
|
||||
---
|
||||
📁 项目结构
|
||||
|
||||
|
||||
GMapiServer/
|
||||
├── main.py # 主程序入口
|
||||
├── shared_utils.py # 共享工具函数
|
||||
├── ffmpeg_utils.py # FFmpeg处理模块
|
||||
├── sanjuuni_utils.py # Sanjuuni处理模块
|
||||
├── video_frame_utils.py # 视频帧提取模块
|
||||
├── file_cleanup.py # 文件清理模块
|
||||
├── logging_config.py # 日志配置
|
||||
├── requirements.txt # 依赖包列表
|
||||
└── README.md # 项目说明文档
|
||||
56
main.py
56
main.py
@@ -388,6 +388,62 @@ def serve_video_frames(job_id, filename):
|
||||
except Exception as e:
|
||||
logging.error(f"提供文件时出错: {e}")
|
||||
return jsonify({"error": "文件访问失败"}), 500
|
||||
|
||||
|
||||
@app.route('/api/framepack', methods=['POST'])
|
||||
def create_framepack():
|
||||
"""接收一组相对路径(如 /frames/abc123/frame_0001.png),打包返回二进制"""
|
||||
import struct
|
||||
from video_frame_utils import FRAMES_ROOT
|
||||
import os
|
||||
|
||||
data = request.get_json()
|
||||
if not data or 'urls' not in data or not isinstance(data['urls'], list):
|
||||
return jsonify({'error': 'Missing urls list'}), 400
|
||||
|
||||
# 构建完整本地路径,验证安全性
|
||||
file_paths = []
|
||||
for url in data['urls']:
|
||||
# 只允许以 /frames/ 开头的路径
|
||||
if not url.startswith('/frames/'):
|
||||
return jsonify({'error': f'Invalid URL prefix: {url}'}), 400
|
||||
# 提取 job_id 和 filename
|
||||
parts = url[len('/frames/'):].split('/', 1)
|
||||
if len(parts) != 2:
|
||||
return jsonify({'error': f'Malformed URL: {url}'}), 400
|
||||
job_id, filename = parts
|
||||
# 安全路径拼接
|
||||
safe_job = os.path.basename(job_id)
|
||||
safe_file = os.path.basename(filename)
|
||||
if not safe_file.endswith('.png'):
|
||||
return jsonify({'error': f'Only .png allowed: {filename}'}), 400
|
||||
full_path = os.path.join(FRAMES_ROOT, safe_job, safe_file)
|
||||
if not os.path.isfile(full_path):
|
||||
return jsonify({'error': f'File not found: {full_path}'}), 404
|
||||
file_paths.append(full_path)
|
||||
|
||||
# 构建 FramePack 二进制
|
||||
pack_data = bytearray()
|
||||
frame_count = len(file_paths)
|
||||
pack_data.extend(struct.pack('>I', frame_count)) # big-endian uint32
|
||||
|
||||
# 先读所有文件到内存(小批量安全)
|
||||
frames = []
|
||||
for path in file_paths:
|
||||
with open(path, 'rb') as f:
|
||||
frames.append(f.read())
|
||||
|
||||
for frame in frames:
|
||||
pack_data.extend(struct.pack('>I', len(frame)))
|
||||
pack_data.extend(frame)
|
||||
|
||||
response = app.response_class(
|
||||
response=bytes(pack_data),
|
||||
status=200,
|
||||
mimetype='application/octet-stream'
|
||||
)
|
||||
response.headers['Content-Disposition'] = 'inline; filename="framepack.bin"'
|
||||
return response
|
||||
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
|
||||
@@ -109,9 +109,6 @@ def process_video_frame_extraction(data, file_registry, file_lock, task_id=None,
|
||||
sys.executable, '-m', 'yt_dlp',
|
||||
video_url,
|
||||
'-o', temp_base,
|
||||
'-f', 'bv*[height<=720]+ba/b',
|
||||
'--no-warnings',
|
||||
'--progress', # 显式启用进度(可选)
|
||||
]
|
||||
|
||||
# 使用 Popen 实时捕获 stderr
|
||||
|
||||
Reference in New Issue
Block a user