Compare commits

...

2 Commits

Author SHA1 Message Date
nnwang
b318144230 更新README 2026-01-10 02:10:10 +08:00
nnwang
56490460fa 添加打包下载 2026-01-10 02:07:34 +08:00
3 changed files with 240 additions and 69 deletions

248
README.md
View File

@@ -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
View File

@@ -389,6 +389,62 @@ def serve_video_frames(job_id, filename):
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():
"""健康检查接口,直接返回 'ok'"""

View File

@@ -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