绿联影视中心数据守护工具。保护你的播放记录、收藏、合集和手动编辑数据,防止重新刮削导致数据丢失。
一、项目介绍
核心目标
绿联影视中心在重新刮削或媒体库重建时,会重置视频元数据表,导致:
你手动编辑的标题、简介、评分 —— 消失
你和家人的播放进度 —— 断链
收藏、合集归属 —— 丢失
ug-videokeeper 的目标很简单:刮削前后,数据一致。
实现方式
项目在每部视频目录下自动维护一个隐藏文件 .ugreen.json(绿联 NAS 默认不显示 . 开头的文件),全量备份 ug_video_info 全部字段 + 播放记录 + 收藏 + 合集 + 剧集列表。当数据库被刮削重置后,从这些备份文件中恢复全部数据。
项目不写入、不修改、不依赖任何 NFO 文件。 数据流转完全基于 .ugreen.json。
绿联重刮
→ 检测 category_id 变化或 ctime 增大
→ 从 .ugreen.json 恢复 35 个字段到 DB
用户播放/收藏/编辑合集
→ 检测 max_mtime(5 张表最新时间戳)增大
→ 从 DB 备份到 .ugreen.json
用户编辑视频元数据(utime 不变)
→ 检测 9 个关键字段的 MD5 哈希变化
→ 从 DB 备份到 .ugreen.json
手动编辑 movie.nfo
→ Watchdog 检测到变化
→ 合并 NFO 的 7 个字段到 .ugreen.json
→ 恢复合并后的 .ugreen.json 到 DB核心功能
二、实际场景
场景 1:刮削后数据还在
你在影视中心里编辑了电影标题、修改了简介、标记了收藏——然后点了"重新刮削"。
有了 ug-videokeeper,刮削后:
✅ 你编辑的标题、简介、评分——恢复
✅ 你和家人的播放进度——恢复
✅ 收藏、合集归属——恢复
场景 2:你手动改了 NFO 文件
你打开 movie.nfo,把标题改成自己喜欢的名字,保存。
Watchdog 合并 NFO 到 .ugreen.json,写入 DB。 周期同步也保护这个值。
场景 3:你把视频文件夹移到了新硬盘
从 /volume1/media/电影/肖申克的救赎 移到 /volume2/media/电影/肖申克的救赎。
绿联重新扫描后,category_id 会变。程序从 file_info 获取最新 category_id,.ugreen.json 跟着目录走,播放记录通过哈希匹配,全部恢复。
场景 4:你重命名了视频文件
Inception_4K.mkv → 盗梦空间 4K.mkv
播放记录基于文件内容哈希匹配文件,改名不影响匹配。
数据保护范围
三、开发与部署
前置条件
绿联 NAS(或任何运行绿联影视中心的设备)
Docker(推荐)或 Python 3.11+
PostgreSQL 数据库(绿联影视中心使用,默认端口 5433)
配置项
Docker Compose
services:
sync:
image: knifez/ug-videokeeper:latest
container_name: ug-videokeeper-sync
restart: unless-stopped
environment:
- DB_HOST=127.0.0.1
- DB_PORT=5433
- DB_NAME=video
- DB_USER=postgres
- DB_PASSWORD=
- SCAN_INTERVAL=3600
- SYNC_MODE=bidirectional
- DRY_RUN=false
# - TARGET_PATH=/volume1/media/movie
- LOG_LEVEL=INFO
volumes:
- 你的绿联媒体目录:/你的绿联媒体目录
- ./data:/app/data
network_mode: "host" # 使用主机网络模式(保证服务发现正常)
command: ["python", "main.py"]
web:
image: knifez/ug-videokeeper:latest
container_name: ug-videokeeper-web
restart: unless-stopped
depends_on:
- sync
ports:
- "8088:8088"
volumes:
- ./data:/app/data
command: ["python", "main.py", "--serve"]首次运行建议
先用
DRY_RUN=true跑一次,查看日志确认程序能正常连接数据库、识别视频文件确认无误后关闭
DRY_RUN,正式运行首次同步会为每部视频创建
.ugreen.json备份文件,建立本地缓存之后每次周期同步只检查有变化的记录
目录结构
ug-videokeeper/
├── main.py # 入口
├── checks.py # 启动自检
├── config.py # 配置解析
├── models.py # 数据模型
├── utils.py # 工具函数
├── scheduler.py # 定时调度 + Watchdog 管理
├── state.py # 状态缓存
├── watcher.py # Watchdog 实时监控
├── db/
│ ├── connection.py # 数据库连接
│ ├── queries.py # 数据库查询
│ └── sync.py # 数据库写入
├── nfo/
│ ├── reader.py # NFO 文件读取
│ ├── writer.py # .ugreen.json 写入
│ └── ugreen.py # .ugreen.json 读写
├── sync/
│ ├── strategy.py # 同步决策
│ └── executor.py # 同步执行
└── data/
└── state.json # 同步缓存FAQ
Q: 为什么用 python:3.11-slim 而不是自建镜像?
A: 依赖只有三个包(psycopg2-binary、schedule、watchdog),启动时 pip 安装即可。
Q: .ugreen.json 和 NFO 文件是什么关系?
A: 互不干扰。项目不写入 NFO,全部数据存到 .ugreen.json。Watchdog 监控 NFO 变化时,将 NFO 编辑的字段合并到 .ugreen.json,再写回数据库。
Q: 每次运行都会读所有文件吗?
A: 不会。首次运行建立基线后,后续通过缓存对比时间戳和内容哈希,无变化则跳过。
Q: 播放记录回写时如何找到正确的文件?
A: 三级匹配:1) 文件内容哈希(改名/移动后仍可匹配);2) strm 文件自行计算哈希;3) 文件名前缀匹配。
Q: 播放记录会丢历史吗?
A: 不会。.ugreen.json 保存完整播放历史,DB 只保留最新一条。每次同步合并新旧记录并去重。
Q: 电视剧的数据写在什么地方?
A: 电视剧目录的 .ugreen.json,video_type == 2。包含 ug_video_info 全字段 + 剧集列表 + 播放记录 + 收藏 + 合集。
Q: 移动目录后数据会丢吗?
A: 不会。.ugreen.json 跟着目录走,程序从 file_info 获取最新 category_id,播放记录通过哈希匹配恢复。
Q: 手动编辑 ug_video_info(如改名字)utime 不变怎么办?
A: 程序对 9 个用户可编辑字段计算 MD5 哈希,哈希变化即触发 DB→JSON 同步。
Q: DRY_RUN 模式做什么?
A: 扫描文件、比对数据、输出日志,但不执行任何数据库写入。