一个面向 NAS 的影视文件自动整理工具

技术专业 · 今天

最近整理 NAS 上的电影和剧集文件时,遇到一个很实际的问题:下载目录里的文件名经常非常混乱,包含资源站水印、清晰度、压制组、音轨、字幕信息,甚至还有拼音缩写、别名、错别字。如果全部手工整理,不仅耗时,也很容易出错。
伴随Ai编码的使用,我现在已经比较少的使用现有的代码了,主要还是为了避免一些安全上的问题,毕竟要部署在我的装着各种资料的nas上。

于是做了一个小工具:AutoReel

它的目标很简单:
监听一个输入目录,自动识别电影和剧集,然后按 Emby / Jellyfin / Plex 友好的目录结构移动并重命名文件。

项目地址:

https://github.com/walker22026/AutoReel

代码的编写过程远远的超出的我的开始预估,框架搭建起来容易,但是真正的运行,能用就有一些问题,当然更谈不上好用。
最主要的问题就是我的节目源主要来自于阿里云或者百度云,这个共享资源为了避免被“和谐”采取各种名称变型,导致猜测也猜不出来,这种情况下,我尝试使用llm来进行节目真实名称的理解,效果并不好。最终还是选择简化处理的模式,能够自动处理的自动处理,无法识别的放入特定目录中,等待使用人接入。避免llm自动处理导致的节目的整理混乱。

项目定位

AutoReel 不是一个复杂的媒体库管理系统,也不替代 Emby、Jellyfin、Plex。

它只做一件事:

把下载目录里的影视文件,整理到标准媒体库目录中。

例如:

输入目录:
/emby/source/毒液:最后·一舞.2160p.DV.HDR (2024)/xxx.mp4

整理后:
/emby/电影/毒液:最后一舞 (2024)/毒液:最后一舞 (2024).mp4

剧集则整理为:

/emby/剧集/低智商犯罪 (2026)/Season 01/低智商犯罪 - S01E01.mp4

当前执行策略

AutoReel 启动后会监听输入目录。

当前设计中有三个核心目录:

输入目录:/host/emby/source
电影目录:/host/emby/电影
剧集目录:/host/emby/剧集

输入目录下还会自动生成:

_unrecognized     未识别目录
_duplicates       重复文件目录
_pending_delete   无媒体文件待清理目录

整体流程是:

输入目录
  ├── 单个视频文件
  │     ├── 识别成功 -> 移动到电影/剧集目录
  │     └── 识别失败 -> 移入 _unrecognized
  │
  ├── 子目录
  │     ├── 作为一个批次处理
  │     ├── 全部视频识别成功 -> 整个目录整理
  │     ├── 任一视频识别失败 -> 整个目录移入 _unrecognized
  │     └── 目标文件已存在 -> 整个目录移入 _duplicates
  │
  └── _unrecognized / _pending_delete
        └── 扫描时跳过

为什么不用硬链接

最初考虑过硬链接,这样可以让下载目录和媒体库目录同时存在一份“看起来独立”的文件,但实际只占一份空间。

不过最终我选择了直接 move 模式,也就是:

直接移动并重命名原始文件。

原因是我的使用场景更偏向媒体库整理,不需要继续做种,也不希望一个文件在多个位置看起来同时存在。这样逻辑更简单,也更容易理解:整理完成后,源目录里就不再保留原文件。

电影处理规则

电影分两种情况。

单视频目录

如果一个目录中只有一个视频文件,优先使用目录名识别电影。

例如:

毒液:最后·一舞.2160p.DV.HDR (2024)/
  xxx.mp4

清洗后会识别为:

毒液:最后·一舞
2024

然后通过 TMDB 查询,整理为:

电影/毒液:最后一舞 (2024)/毒液:最后一舞 (2024).mp4

多视频目录

如果一个目录下有多个视频文件,则更像电影合集或系列电影。

例如:

夺宝奇兵/
  Raiders.of.the.Lost.Ark.1981.mkv
  Indiana.Jones.and.the.Last.Crusade.1989.mkv
  Indiana.Jones.and.the.Dial.of.Destiny.2023.mkv

这种情况下不会只用目录名“夺宝奇兵”去匹配一部电影,而是逐个视频文件识别。只有全部识别成功后,才会整体移动。

如果其中任何一个文件无法识别,整个目录会被移动到 _unrecognized,避免整理一半导致目录结构被破坏。

剧集处理规则

剧集采用“目录优先”的原则。

原因是很多剧集文件名非常简单,例如:

低智商犯罪(2026)/
  01 4K.mp4
  02 4K.mp4
  03 4K.mp4

如果直接把 01 4K.mp4 拿去 TMDB 搜索,很可能会误识别成电影,比如:

01 -> 某部电影
02 -> 另一部电影
03 -> 又一部电影

这是非常危险的。

所以现在的策略是:

目录名负责识别剧名
文件名只负责提取集数

也就是说:

低智商犯罪(2026)

用于识别剧集本身。

而:

01 4K.mp4
02 4K.mp4

只会被解释为第 1 集、第 2 集,不会再拿去搜索电影。

整理后:

剧集/低智商犯罪 (2026)/Season 01/低智商犯罪 - S01E01.mp4
剧集/低智商犯罪 (2026)/Season 01/低智商犯罪 - S01E02.mp4

如果无法从文件名中提取集数,整个目录会进入 _unrecognized,避免错误重命名。

文件名清洗

国内资源站的文件名经常包含大量无效信息,例如:

凶器.6v电影 地址发布页 www.6v123.net 收藏不迷路
双子杀手 4K原盘REMUX 杜比视界 国英双音 内封字幕
穿条纹睡衣的男孩.The Boy in the Striped Pajamas.2008.BluRay.1080P.H265.10bit.英语.中英特效字幕

AutoReel 会先做本地清洗,去掉常见水印和技术标签:

6v电影
地址发布页
收藏不迷路
www.xxx.com
4K
REMUX
杜比视界
国英双音
内封字幕
1080P
BluRay
H265

清洗后的结果更适合提交给 TMDB 查询。

例如:

双子杀手 4K原盘REMUX 杜比视界 国英双音 内封字幕

会清洗为:

双子杀手

年份匹配策略

年份是防止误识别的重要依据。

例如如果文件名里有:

某电影 (2019)

那么 TMDB 返回的结果必须是:

2019

或者允许很小的容差:

2018 / 2020

如果搜索结果只有 2000 年的同名电影,就不会匹配成功。

这样可以避免“同名不同年份”的电影被错误整理。

别名机制

有些电影或剧集的中文名、英文名、民间译名并不一致。

AutoReel 支持别名表:

/config/aliases.json

示例:

{
  "毒液:最后·一舞": "Venom: The Last Dance",
  "芭蕾杀姬": "Ballerina",
  "特 工 绍 特": "Salt"
}

别名表优先级高于普通搜索。
这对于 TMDB 搜不到、中文名不标准、或者文件名被严重缩写的情况很有用。

为什么取消 LLM

项目中曾经尝试加入 LLM 作为兜底识别方式,希望通过大模型推断文件名。

但实际测试后发现效果并不稳定。

原因是:

  1. 影视文件名高度依赖 TMDB 数据。
  2. 很多冷门片、译名、资源站缩写并不在 LLM 的可靠知识范围内。
  3. LLM 可能“猜得很像”,但并不一定准确。
  4. 对于自动重命名文件来说,错误识别的代价比识别失败更高。

所以最后取消了 LLM。

当前原则是:

宁可识别失败进入 _unrecognized,也不要自信地改错文件名。

这也是整个项目后期优化的核心原则。

重复文件处理

如果目标文件已经存在,AutoReel 不会覆盖。

例如目标目录中已经有:

电影/乱战 (2005)/乱战 (2005).mkv

新的输入目录又识别到了同一个目标路径,那么整个目录会被移动到:

source/_unrecognized/_duplicates/

并写入原因文件:

目录中xxx目标文件已存在.txt

这样可以人工判断:

  • 是重复下载
  • 是不同版本
  • 是需要保留
  • 还是可以删除

未识别目录的人工处理

识别失败的文件或目录会进入:

source/_unrecognized

目录中会生成原因文件,例如:

目录中xxx文件未识别到.txt
目录xxx无法识别.txt

人工处理方式很简单:

  1. 进入 _unrecognized
  2. 修改文件名或目录名
  3. 把它移回输入目录
  4. AutoReel 会重新扫描并处理

这样保留了人工接入能力,也避免程序盲目处理。

群晖 NAS 的特殊处理

在群晖上,目录中可能会出现:

@eaDir

这是 Synology 的扩展属性目录。

AutoReel 会跳过这些目录,避免把它们作为附属文件移动到媒体库中。

同时也会跳过:

.DS_Store
__MACOSX

Docker 部署

项目使用 Docker 部署。

示例 docker-compose.yml

services:
  media-organizer:
    build: .
    container_name: AutoReel
    restart: unless-stopped
    environment:
      WATCH_DIR: /host/emby/source
      MOVIE_DIR: /host/emby/电影
      TV_DIR: /host/emby/剧集

      UNRECOGNIZED_DIR_NAME: _unrecognized
      DUPLICATE_DIR_NAME: _duplicates
      PENDING_DELETE_DIR_NAME: _pending_delete

      TMDB_API_KEY: "你的 TMDB v3 API Key"
      TMDB_LANG: zh-CN

      FILE_ACTION: move
      DRY_RUN: "false"
      SCAN_ON_START: "true"
      QUIET_SECONDS: "10"
      MIN_FILE_SIZE_MB: "100"

      LOG_LEVEL: INFO

    volumes:
      - /volumeUSB1/usbshare/emby:/host/emby
      - ./config:/config

启动:

docker compose up -d --build

查看日志:

docker compose logs -f

停止:

docker compose down

我的目录配置

我的 NAS 目录结构是:

输入目录:
/volumeUSB1/usbshare/emby/source

电影目录:
/volumeUSB1/usbshare/emby/电影

剧集目录:
/volumeUSB1/usbshare/emby/剧集

Docker 中挂载为:

volumes:
  - /volumeUSB1/usbshare/emby:/host/emby

容器内配置为:

WATCH_DIR: /host/emby/source
MOVIE_DIR: /host/emby/电影
TV_DIR: /host/emby/剧集

Dry Run 的作用

首次运行建议开启:

DRY_RUN: "true"

这时 AutoReel 只会打印计划,不会真正移动文件。

日志中会看到:

[DRY-RUN][move] source/xxx.mkv -> 电影/xxx/xxx.mkv

确认识别结果没问题后,再改为:

DRY_RUN: "false"

当前已知边界

AutoReel 当前仍然是一个轻量工具,不追求覆盖所有媒体管理场景。

目前已知边界包括:

  • 依赖 TMDB,NAS 必须能访问 api.themoviedb.org
  • 冷门片、译名混乱、缩写严重时仍可能需要别名表
  • 多版本电影、导演剪辑版、加长版暂时没有单独版本管理
  • 原盘目录、ISO、BDMV 结构还没有完整特殊处理
  • USB 移动硬盘作为输入目录时,监听机制可能影响硬盘休眠,需要结合 NAS 实测

项目原则

这个项目一路调整下来,最终形成了几个原则:

  1. 自动化不能以错误整理为代价。
  2. 文件名越混乱,越应该保守处理。
  3. 识别失败可以人工修正,识别错误反而更麻烦。
  4. 剧集目录不能用单集文件名去搜电影。
  5. 对 NAS 用户来说,Docker 部署和低配置门槛很重要。
  6. 配置尽量集中在 docker-compose.yml,不再依赖 Web 页面或额外配置文件。

总结

AutoReel 解决的是一个很小但很实际的问题:

下载目录很乱,媒体库需要干净。

它不会替代完整的媒体库系统,但可以作为下载目录到媒体库目录之间的一层自动整理工具。

目前它已经可以处理:

  • 电影自动识别
  • 剧集批量整理
  • 文件名水印清洗
  • TMDB 匹配
  • 年份校验
  • 别名映射
  • 重复文件隔离
  • 未识别文件人工接入
  • Docker 部署
  • 群晖特殊目录跳过

后续可以继续优化的方向:

  • 更完善的电影版本识别
  • 更强的原盘目录支持
  • 更友好的未识别清单
  • 更低唤醒的移动硬盘监听模式
  • 更完善的别名维护方式
Theme Jasmine by Kent Liao