STM(Smart Transcode Manager)是一个全自动视频转码系统,专为 NAS 环境设计。它能够在夜间利用闲置算力自动将视频文件压缩为高效的 H.264 格式,节省存储空间的同时保持良好的画质。
- 🌙 智能时间调度:夜间自动转码(22:00-07:00),不影响日常使用
- 🗑️ 双层垃圾桶:7天缓冲期 + 30天彻底删除,支持跨分区安全移动
- 🔄 增量扫描:基于元数据的去重策略,智能识别新文件和更新
- ⚡ Worker Pool 并发:可配置并发数,充分利用多核 CPU
- 📊 现代化 Web 界面:实时仪表盘、任务列表、垃圾桶管理
- 📈 Prometheus 监控:内置 metrics 导出,支持 Grafana 可视化
- 🐳 Docker 一键部署:多阶段构建,开箱即用
- 💾 群晖优化:自动过滤 Synology Photos 缩略图和系统文件
- ✅ NAS 存储空间紧张,需要压缩旧视频
- ✅ 夜间算力闲置,希望自动化利用
- ✅ 需要保留原始文件的缓冲期(误删恢复)
- ✅ 大量视频文件需要批量转码
- ✅ 需要监控转码进度和历史记录
- 克隆项目
git clone https://github.com/yourname/stm.git
cd stm- 修改配置
编辑 docker-compose.yml,设置你的输入/输出目录:
volumes:
- /mnt/nas/media/downloads:/input # 源视频目录
- /mnt/nas/media/archive:/output # 转码后输出目录
- ./data:/data # 数据库和日志编辑 configs/config.yaml 调整转码参数(可选)。
- 启动服务
# 构建镜像
docker-compose build
# 启动服务
docker-compose up -d
# 查看日志
docker-compose logs -f- 访问 Web 界面
打开浏览器访问:http://your-server-ip:8080
前置要求:
- Go 1.24+
- FFmpeg 6.1+
# 安装依赖
go mod download
# 编译
go build -o stm ./cmd/stm
# 修改配置
cp configs/config.yaml my-config.yaml
# 编辑 my-config.yaml 设置路径
# 运行
./stm --config my-config.yaml# 路径配置
path:
input: "/input" # 输入目录
output: "/output" # 输出目录
trash: ".stm_trash" # 垃圾桶目录名
# 系统配置
system:
db_path: "/data/stm.db" # 数据库路径
max_workers: 3 # 并发转码数
cron_start: 22 # 工作开始时间(小时)
cron_end: 7 # 工作结束时间(小时)
scheduler_interval: 10 # 调度器检查间隔(秒)
task_queue_size: 10 # 任务队列容量
min_disk_space_gb: 5 # 最小磁盘空间要求(GB)
# FFmpeg 配置
ffmpeg:
codec: "libx264" # 视频编码器
preset: "veryslow" # 预设(slow/veryslow)
crf: 27 # 质量(18-28,越大体积越小)
audio: "aac" # 音频编码器
audio_bitrate: "128k" # 音频比特率
# 清理配置
cleaning:
soft_delete_days: 7 # 移入垃圾桶天数
hard_delete_days: 30 # 彻底删除天数
# Web 配置
web:
port: ":8080" # Web 端口STM_MAX_WORKERS=3 # 覆盖并发数
STM_INPUT_PATH=/path/to/input
STM_OUTPUT_PATH=/path/to/output- 实时统计卡片:
- 待处理任务数(黄色)
- 处理中任务数(蓝色)
- 已完成任务数(绿色)
- 节省空间 GB(紫色)
- Worker 状态:实时显示工作模式(自动运行/强制运行/休眠中)
- 失败任务列表:快速查看和重试失败任务
- 控制按钮:扫描目录、强制启动、停止强制
- 状态筛选:全部/待处理/处理中/已完成/失败
- 任务表格:文件名、状态、进度条、大小变化、创建时间
- 操作按钮:重试失败任务、删除任务记录
- 分页控件:每页 20 条,支持翻页
- 警告提示:30 天自动删除提醒
- 文件列表:文件名、大小、删除时间、剩余天数
- 倒计时提示:3天内红色,7天内黄色
- 手动删除:二次确认对话框
# 获取统计信息
GET /api/stats
# 获取任务列表
GET /api/tasks?status=pending&page=1&limit=20
# 手动触发扫描
POST /api/scan
# 强制启动 Worker
POST /api/worker/force-start
# 停止强制运行
POST /api/worker/force-stop
# 获取 Worker 状态
GET /api/worker/status
# 获取垃圾桶列表
GET /api/trash
# 删除垃圾桶文件
DELETE /api/trash/:filename
# 健康检查
GET /api/health
# Prometheus Metrics
GET /metrics在 prometheus.yml 中添加:
scrape_configs:
- job_name: 'stm'
static_configs:
- targets: ['localhost:8080']
metrics_path: /metrics
scrape_interval: 15s可用指标:
stm_tasks_total{status}- 任务总数(按状态分类)stm_workers_active- 当前活跃 Worker 数stm_transcode_duration_seconds- 转码耗时分布stm_transcode_success_total- 转码成功总数stm_transcode_failed_total- 转码失败总数stm_space_saved_bytes- 节省存储空间(字节)stm_files_soft_deleted_total- 移入垃圾桶文件数stm_files_hard_deleted_total- 彻底删除文件数
- 频率:每 10 分钟扫描一次输入目录
- 过滤规则:
- 群晖系统文件:
@eaDir,#recycle,.DS_Store - 缩略图:
SYNOPHOTO_FILM_*,SYNOPHOTO_THUMB_* - 临时文件:
.tmp,.part,.lock
- 群晖系统文件:
- 去重策略:基于文件路径、大小、修改时间(MD5)
- 时间窗口:配置的工作时间(默认 22:00-07:00)
- 并发控制:Worker Pool 模式(默认 3 并发)
- 任务队列:缓冲 10 个待处理任务
- 进度解析:FFmpeg
-progress pipe:1实时输出 - 进度优化:仅当变化 ≥5% 或间隔 ≥5s 时更新数据库
- 磁盘检查:转码前检查可用空间(默认最少 5GB)
-
一级清理(7天后):
- 移入同级目录的
.stm_trash文件夹 - 文件命名:
原文件名_del_20260105_120000 - 同分区:
os.Rename()(毫秒级) - 跨分区:
io.Copy()+os.Remove()(自动降级)
- 移入同级目录的
-
二级清理(30天后):
- 解析文件名时间戳
- 超过阈值直接
os.Remove() - 彻底删除,不可恢复
stm/
├── cmd/
│ └── stm/
│ └── main.go # 主程序入口
├── internal/
│ ├── config/ # 配置管理
│ │ ├── config.go
│ │ └── config_test.go
│ ├── database/ # 数据库层
│ │ ├── db.go
│ │ ├── models.go
│ │ └── db_test.go
│ ├── scanner/ # 扫描器
│ │ ├── scanner.go
│ │ └── scanner_test.go
│ ├── worker/ # 转码执行器
│ │ ├── worker.go
│ │ └── worker_test.go
│ ├── cleaner/ # 清理模块
│ │ ├── cleaner.go
│ │ └── cleaner_test.go
│ ├── web/ # Web 服务
│ │ ├── server.go
│ │ └── templates/
│ │ ├── index.html # 仪表盘
│ │ ├── tasks.html # 任务列表
│ │ └── trash.html # 垃圾桶
│ └── metrics/ # Prometheus 指标
│ └── metrics.go
├── configs/
│ └── config.yaml # 默认配置
├── Dockerfile # 多阶段构建
├── docker-compose.yml # Docker Compose 配置
├── go.mod
├── go.sum
└── README.md
- 语言:Go 1.24
- 数据库:SQLite (modernc.org/sqlite)
- Web 框架:Gin
- 定时任务:robfig/cron/v3
- 监控指标:Prometheus client_golang
- 前端:HTML5 + TailwindCSS CDN + Fetch API
- 视频处理:FFmpeg 6.1
- 部署:Docker + Docker Compose
| 视频规格 | 原始大小 | 转码后 | 压缩率 | 耗时 |
|---|---|---|---|---|
| 1080p/30fps/10min | 2.5 GB | 450 MB | 82% | ~20 分钟 |
| 1080p/60fps/10min | 4 GB | 750 MB | 81% | ~35 分钟 |
| 4K/30fps/10min | 6 GB | 1.0 GB | 83% | ~60 分钟 |
| 4K/60fps/10min | 10 GB | 1.8 GB | 82% | ~120 分钟 |
- CPU:80%-90% (3 workers,转码期间)
- 内存:
- 运行时:30-50 MB
- 休眠时:15-20 MB
- 磁盘 I/O:50-100 MB/s (读写)
- 网络:0 (本地处理)
- 1万任务:数据库大小 < 10MB
- 查询速度:< 1ms (索引优化)
- 进度更新:98% 减少(5% 变化或 5s 间隔)
Q: 转码失败,提示找不到 FFmpeg
A: 确保 Docker 镜像使用 jrottenberg/ffmpeg:6.1-alpine 作为基础镜像。
Q: 群晖缩略图被转码
A: Scanner 已内置过滤 SYNOPHOTO_* 文件,检查日志确认是否跳过。
Q: 跨分区移动失败
A: 程序会自动降级为复制+删除模式,检查日志中是否有 "跨分区" 提示。
Q: Worker 在白天不停止
A: 检查配置 cron_start 和 cron_end,确保时间窗口正确(例如 22-7 表示 22:00-07:00)。
Q: 磁盘空间不足导致转码失败
A: 转码前会检查磁盘空间(默认最少 5GB),可调整 min_disk_space_gb 配置。
Q: 数据库锁定错误
A: SQLite 已启用 WAL 模式,如仍有问题请减少并发数。
# Docker 日志
docker-compose logs -f stm
# 应用日志
tail -f ./data/stm.log欢迎提交 Issue 和 Pull Request!
MIT License
- FFmpeg - 视频处理核心
- Gin - Web 框架
- modernc.org/sqlite - 纯 Go SQLite 实现
开发者: STM Team
版本: v1.0
最后更新: 2026-01-05