# emby-duplicate-tos **Repository Path**: chenshaogui/emby-duplicate-tos ## Basic Information - **Project Name**: emby-duplicate-tos - **Description**: Emby 重复媒体清理工具的铁威马(TerraMaster)TOS 系统适配版。针对 TOS 7 系统的文件系统特性与 Docker 环境进行优化部署,支持 ARM64/x86_64 双架构运行,保持原版全部功能的同时提供 TOS 专属安装脚本与系统服务集成。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-17 - **Last Updated**: 2026-11-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Emby 视频去重器 —— 铁威马 TOS 7 Deb 应用 连接 Emby 服务的 **数据目录**,读取其中的 SQLite 库(`library.db` / `Emby.db`), 按 **视频指纹(大小+编码+分辨率+时长+帧率)** 或 **严格匹配(大小+创建时间+格式+创作者)** 判定重复视频; 在 Web 界面中可视化展示重复列表(含 Emby 缓存缩略图、文件信息、在线播放), 支持 **勾选批量删除**、**目录级重复标记与递归删除** 以及 **库内绝对路径迁移**; **停/启 Emby 与删除后重新扫描媒体库均由用户手动完成**(应用给出清晰分步提示), 应用只负责 **改库 → 删除磁盘文件**,保持库与磁盘一致(**不再自动备份数据库**,删除前请用户自行备份 library.db)。 本项目是 [`emby-duplicate`](https://gitee.com/chshaogui/emby-duplicate)(飞牛 fnOS 版)向 **TOS 7 Deb 应用** 的移植, **后端逻辑与前端界面功能与其完全一致**,仅将打包格式、服务管理、前后端通信方式适配为 TOS 7 规范。 后端仅用 **Python 标准库**(含 `sqlite3`),前端为 **单文件 HTML**。 ## 应用子类型与选型 按《TOS 7 Application Development Guide》第 8 章,本应用为 **Deb / WebUI External Open(新标签页打开)**: - 后端是一个本地 HTTP 服务,**默认仅监听回环地址 `127.0.0.1:18966`**(端口在推荐区间 8000-19999,避开系统保留端口); 服务**不直接暴露到局域网**,用户必须经 TOS 平台 nginx 反代访问,由 **TOS 登录鉴权**充当本应用的**访问控制层**(审核项 S4); - `config.ini` 使用 `open_path: true` + `path: /emby-duplicate-tos/`(Deb 外部打开的标准写法:经 TOS 平台 nginx 反代访问, 点图标打开 `http(s)://:/emby-duplicate-tos/`,无需额外端口;不含 `type` 字段,二者互斥); - 随包提供 `nginx/emby-duplicate-tos.conf`(`location /emby-duplicate-tos/` 反代到 `127.0.0.1:18966`,平台自动 include); - 服务由 **systemd**(`emby-duplicate-tos.service`)管理,App Center 通过它显示运行状态与启停。 > 因服务仅绑定 `127.0.0.1`,`http://:18966` 的局域网直连方式默认不可用——这是刻意的访问控制设计; > 若 `/emby-duplicate-tos/` 路径 404(部分 TOS 版本不会自动 include 应用自带的 nginx 片段),请用下方 `bin/nginx-proxy.py enable` 接入反代。 > 后端对挂载前缀自适应(`/emby-duplicate-tos/api/...` 与 `/api/...` 均可直达),前端 `BASE` 按浏览器 URL 自动推导。 > 之所以选「外部打开 / HTTP 端口」而非「iframe 内部打开 / Unix Socket 平台代理」:本应用前端通过 > `` 直接加载 `/api/thumb` 缩略图并用 `fetch` 走相对路径访问 `/api/*`,端口模式下同源即可, > 无需平台代理的自定义 `Cookie`/`X-Csrf-Token` 头改造,最贴近原有实现、改动最小。 ## 目录结构 ``` emby-duplicate-tos/ ├── src/ 源码(唯一事实来源,改这里) │ ├── emby_db.py 去重引擎:库探测 / schema 内省 / 分组 / 目录标记 / 删除改库 / 迁移 │ ├── server.py HTTP 服务(默认仅监听 127.0.0.1 端口模式 + 保留 Unix Socket 模式)+ REST API + /health + 隐私数据 API │ └── ui/index.html 单文件前端(参数设置 / 去重结果 / 目录重复 / 路径迁移 / 日志) ├── config.ini 应用核心元信息(JSON 内容,.ini 后缀) ├── PRIVACY.md 隐私政策(数据流/存储/权利/访问控制,随包部署) ├── emby-duplicate-tos.lang 14 语言文案 ├── emby-duplicate-tos.env systemd EnvironmentFile(DUP_VAR_DIR / DUP_HOST / DUP_PORT) ├── bin/emby-duplicate-tos 后端服务启动器(bash,systemd ExecStart 调用) ├── bin/nginx-proxy.py TOS nginx 反代一键接入/诊断/撤销(SSH 经 `python3` 手动执行,无执行位) ├── images/icons/emby-duplicate-tos.svg 应用图标(SVG 512×512) ├── init.d/emby-duplicate-tos.service systemd 服务单元(安全加固,ReadWritePaths 指向 data/logs) ├── nginx/emby-duplicate-tos.conf 外部打开所需的反代 location 片段 ├── DEBIAN/ deb 元数据与生命周期脚本 │ ├── control Package/Version/Architecture/Depends/Description │ ├── postinst 建用户/目录、解压 webui.bz2、enable+start 服务 │ ├── prerm 结束进程、stop+disable 服务 │ └── postrm purge 时清理用户与 data/logs 目录 ├── build.sh 打包脚本(在 Linux/WSL/NAS 上用 dpkg-deb 生成 .deb) ├── _test/ 本地端到端冒烟测试(与 fnOS 版共用,44 项断言) └── dist/ 打包产物(.gitignore 已忽略) ``` 安装后(平台把 `/usr/local/emby-duplicate-tos/` 映射到用户所选卷 `/Volume/@apps/emby-duplicate-tos/`)的物理布局: ``` /usr/local/emby-duplicate-tos/ ├── config.ini ├── PRIVACY.md ├── emby-duplicate-tos.lang ├── emby-duplicate-tos.env ├── server.py emby_db.py # Python 引擎 ├── webui.bz2 → web/index.html # 前端(postinst 解压) ├── bin/emby-duplicate-tos # 启动器(可执行) ├── images/icons/emby-duplicate-tos.svg ├── init.d/emby-duplicate-tos.service ├── nginx/emby-duplicate-tos.conf └── data/ logs/ # 运行期(settings.json / scan_result.json) ``` ## 环境要求 | 环节 | 依赖 | |---|---| | 打包 | Linux / WSL / NAS 终端:`dpkg-deb`、`tar`(bzip2)、`python3`(脚本内校验用) | | 运行(TNAS) | TOS 7.0 及以上;系统自带 **python3**(TOS 7.0 兼容层为 Ubuntu 22.04,Python 3.10);systemd | | 本地开发/测试 | Python ≥ 3.7(Windows 下授权路径分隔符自动改用 `;`) | | 停/启 Emby、重扫描 | 均在 **铁威马 TOS 应用中心 / Emby 后台手动完成**,应用给出分步提示 | ## 打包 `build.sh` 需在具备 `dpkg-deb` 的 Linux 环境执行(WSL / NAS SSH / 容器均可): ```bash # 从 Windows 用 WSL 构建(示例,先安装一个 WSL 发行版) wsl bash -c "cd /mnt/d/project/emby-duplicate-tos && chmod +x build.sh && ./build.sh x86_64" # 或在 NAS / 任意 Linux 上 ./build.sh x86_64 # 产物:dist/emby-duplicate-tos_1.7.3_amd64.deb ./build.sh aarch64 # 产物:dist/emby-duplicate-tos_1.7.3_arm64.deb(ARM 机型) ``` 脚本流程:校验 `config.ini` JSON 合法性与 `type/open_path` 互斥 → 由 `src/ui` 生成 `webui.bz2` → 把 `src/server.py`、`src/emby_db.py` 与 TOS 元数据组装进 `build//staging/usr/local/emby-duplicate-tos/` → 统一去 BOM 并把文本行尾规范为 LF → 映射 `amd64/arm64` → `dpkg-deb --build` 输出到 `dist/`。 修改版本号请编辑 `config.ini` 的 `version`,并同步 `DEBIAN/control` 的 `Version`、`src/server.py` 的 `APP_VERSION` (三者必须一致,审核项 C9:版本描述不得互相矛盾;`build.sh` 会校验随包内无编译型二进制)。 > ⚠ 常见致命错误:`config.ini`、`emby-duplicate-tos.lang`、`images/` 必须位于 deb 内的 > `usr/local/emby-duplicate-tos/` 下(解析器从 `data.tar` 该路径读取),不能放在 deb 根目录。`build.sh` 已按此布局组装。 ## 本地开发与测试 ```powershell # 端到端冒烟测试(自动构造模拟库、起服务、走全部 API、校验删除/迁移,44 项断言) python _test\smoke_test.py # 手工调试界面(本地端口模式,浏览器访问 http://127.0.0.1:8899/) $env:DUP_VAR_DIR="$PWD\_test\var" python src\server.py --port 8899 ``` ## 安装部署(TOS 7) 1. 应用中心 → 本地安装 / 上传,选择 `emby-duplicate-tos__.deb`; (平台会自动创建专用运行用户 `emby-duplicate-tos`,安装后 `postinst` 会拉起服务) 2. **文件权限(关键)**:TOS 禁止应用以 root 运行,本应用以专用非 root 用户 `emby-duplicate-tos` 运行,因此该用户 必须对 Emby 的 `data` 目录(含 `library.db`)**同时具备读与写权限**(只读能扫描,但删除/迁移要改库会报 `attempt to write a readonly database`),并对要删除的媒体目录有写权限。两种方式任选其一: - TOS「文件管理 / 共享文件夹」→ 对应文件夹权限 → 给用户 `emby-duplicate-tos` 勾选 **读+写**; - 或经 SSH(需 root/sudo)执行:`setfacl -R -m u:emby-duplicate-tos:rwX /Volume/.../Emby的data目录`(媒体目录同理)。 Emby 若以 Docker 安装,其数据一般位于 `/Volume/DockerAppData//data`。 3. 打开应用卡片(新标签页 `http(s)://:/emby-duplicate-tos/`,由 TOS nginx 反代到 `127.0.0.1:18966`; 服务仅绑定回环地址,**无法也不应通过 `http://:18966` 局域网直连**,访问控制由 TOS 登录承担)→ 「参数设置」→ 填写或「自动检测 / 浏览」选择 **Emby 数据目录** → 选择判定依据与创建时间精度 → 保存 → **扫描去重**; 若 `/emby-duplicate-tos/` 路径 404(部分 TOS 版本不会自动 include 应用自带的 nginx 片段),SSH 登录设备执行一次: `sudo python3 /usr/local/emby-duplicate-tos/bin/nginx-proxy.py enable`(先 `status` 可诊断;带备份与 `nginx -t` 校验,失败自动回滚;`disable` 可撤销;TOS 升级或改 Web 端口后若失效需重跑); 4. 「去重结果」每组自动标注「建议保留」,其余为冗余可勾;用「▶ 打开/播放」核对内容 → **先在 TOS 应用中心手动停止 Emby** → 「预览删除」确认计划 → 勾选「我已手动停止 Emby」后「执行删除」→ 按弹窗提示 **启动 Emby 并在后台执行「扫描媒体库」**; 5. 「目录重复」勾选目录可整体递归删除;「路径迁移」在搬迁后改写库内旧绝对路径前缀(先预览后执行;本应用不自动备份,执行前请自行备份 db)。 ## 关键行为说明 - **只读扫描**:扫描走 `mode=ro` 打开 SQLite,与运行中的 Emby 不争锁;仅删除/迁移时才写库。 - **删除安全链**:(用户手动停服并自行备份 library.db 后)`事务删除条目与子表 → 删磁盘文件`;**本应用不再自动备份数据库**。 未勾选「我已手动停止 Emby」时拒绝执行删除(返回 409)。 - **子表清理 / 字段内省 / GUID 兼容 / 目录级重复上卷 / 创建时间精度**:与 fnOS 版完全一致,详见 `src/` 源码注释。 - **运行数据**:`settings.json` / `scan_result.json` 均写入 `/usr/local/emby-duplicate-tos/data`(systemd `ReadWritePaths` 显式放行),升级保留;**卸载(`dpkg --remove`)与 `--purge` 均由 `postrm` 清理**运行期目录与残留(审核项 F11)。 - **健康检查**:`GET /health` 返回 `{status,app,version}`,可用于探活。 - **访问控制(S4)**:服务默认仅监听 `127.0.0.1:18966`,不对局域网直接开放;所有访问必须经 TOS nginx 反代 `/emby-duplicate-tos/` 到达,由 TOS 登录鉴权统一把关(仅已登录的授权用户可操作);如需受信内网直连端口, 可显式设 `DUP_HOST=0.0.0.0` 覆盖(自担风险)。 - **访问方式**:TOS 平台加载 `nginx/emby-duplicate-tos.conf` 后经 `//` 前缀访问(前缀在 nginx 侧剥离); 若代理保留前缀,后端也会自动剥离 `/xxx/api/...` 中的挂载前缀,两种模式均可用; 平台未自动加载时用 `python3 bin/nginx-proxy.py enable` 一键把片段 include 进 TOS nginx 的 server 块。 ## 隐私与数据(审核项 C3 / C4 / C7 / C8) 完整隐私政策见随包的 [`PRIVACY.md`](PRIVACY.md)。要点: - **数据流(C8)**:本应用**完全本地运行**,不向任何外部服务器发送数据;**不使用 TURN / STUN / 中继,也不含 WebRTC / P2P**,无任何对外数据流。 - **访问 / 更正 / 删除(C7)**:界面新增「**隐私与数据**」面板,可查看应用保存的全部本地数据(`GET /api/privacy/data`)、 修改参数与清除扫描缓存以更正、一键删除本应用全部数据(`POST /api/privacy/wipe`,仅清参数/扫描结果/日志,**绝不**触碰 Emby 媒体与数据库)。 ## 安全与依赖(审核项 S4 / T1 / V6) - **访问控制(S4)**:服务默认仅监听 `127.0.0.1`,经 TOS 登录鉴权反代访问(见上)。 - **依赖与漏洞(T1)**:后端仅用 **Python 标准库**(含 `sqlite3`),前端为单文件 HTML,**无任何第三方运行库 / SDK / 追踪代码**, 因此不存在需扫描的第三方 CVE 面;提交前可在 Linux/WSL 用 `bandit -r src/` 与 `python3 -m pip_audit`(本项目无 pip 依赖,预期为空) 做静态与依赖扫描,结果无高危漏洞。 - **无随包二进制(V6)**:本应用不含任何编译型二进制(ELF),仅解释型脚本;`build.sh` 在打包前会扫描并拒绝任何 ELF; `bin/nginx-proxy.py` 已改为无执行位(经 `python3` 调用)。 ## 运行文件清单(§12.9.6,相对 `/usr/local/emby-duplicate-tos/`) | 路径 | 用途 | 格式 | 创建时机 | 增长/轮转 | 生命周期 | |---|---|---|---|---|---| | `data/settings.json` | 参数设置(数据目录/判定模式/阈值等) | JSON | 首次保存配置时 | < 1 MB | 持久,升级保留,卸载清理 | | `data/scan_result.json` | 最近一次扫描结果缓存 | JSON | 扫描后 | 随重复组数量,可手动清除 | 持久,升级保留,卸载清理 | | `data/backups/` | 预留目录(本应用不自动备份) | — | postinst 创建 | — | 卸载清理 | | `appdata/` | `data` 不可写时的自动回退目录 | JSON | 按需 | 同上 | 卸载清理 | | `logs/` | 应用日志(若有) | 文本 | 按需 | 可手动删除 | 卸载清理 | | `/var/api/emby-duplicate-tos.sock` | 仅内部代理模式使用 | Unix socket | 按需 | — | 启动前清理、卸载清理 | > 本应用不向共享系统 `/tmp` 直写(systemd `PrivateTmp=true` 隔离);所有运行期文件均位于应用自有目录,由 `postrm` 在卸载时清理。 ## 常见问题 | 现象 | 处理 | |---|---| | 打开应用「无法访问 / 连接被拒」 | 服务未启动:`systemctl status emby-duplicate-tos`;确认端口 18966 未被占用 | | 访问 `/emby-duplicate-tos/` 返回 404 | TOS 的 nginx 未加载反代片段:SSH 执行 `sudo python3 /usr/local/emby-duplicate-tos/bin/nginx-proxy.py enable`(详见上文安装部署第 3 步) | | 扫描报「未找到 .db 库文件」 | 数据目录填错,应指向含 `library.db` 的 **data** 目录 | | 「无权限访问」/ 检测不到 Emby 目录 | 运行用户 `emby-duplicate-tos` 对目标目录无读权限,按上文第 2 步授权 | | 删除/迁移报 `attempt to write a readonly database` / 无权限写库 | `emby-duplicate-tos` 用户对 Emby `data` 目录只读不可写;按第 2 步授予 **读+写**(或 `setfacl -R -m u:emby-duplicate-tos:rwX `)后重扫再执行 | | 删除报「请先手动停止 Emby」 | 未勾选「我已手动停止 Emby 服务」;先在应用中心停止 Emby 再执行 | | 误删想恢复 | 用你自行备份的 library.db 还原,磁盘文件需从回收站/备份恢复(故务必先自行备份并预览删除) | ## ⚠ 风险提示 删除会修改 Emby 数据库并永久删除磁盘文件,不可撤销。生产环境务必: **先自行备份 library.db → 预览删除 → 再执行删除**;本应用不自动备份,务必在操作前对 Emby data 目录(含 library.db)做一份完整备份。