你往媒体库里加片子,习惯用改名或移动的方式——下载好一个临时名字的文件,改成正名拖进目录;或者把别处的文件直接 mv 进来。可发现媒体库根本不更新,目录里明明有新文件,库里还是显示旧条目。奇怪的是,如果你新建一个文件进目录,它又能立刻识别。同样是"文件出现在目录里",为什么移动可以、新建才行?
现象
- 把文件重命名(例如
abc.mkv→电影名.mkv)到受监控目录 → 媒体库不刷新,条目不变; - 把文件移动(
mv)进受监控目录 → 同样没反应; - 但如果是复制/新建一个新文件进目录 → 媒体库能自动识别,条目更新。
规律:只有"新建"能触发扫描,"改名"和"移动"不能。
根因
根因在扫描的触发机制:媒体库是靠文件系统事件通知来增量更新的,而它监听的是"新建"事件,改名/移动并不产生"新建"事件。
现代媒体库为了不做"每次都全盘扫描"这种昂贵操作,会挂上操作系统的文件系统监控(Linux 上常见的是 inotify)来监听目录变化。当它收到 IN_CREATE(文件被创建)事件时,就去扫描那个新文件。
问题在于,改名和移动在文件系统层面根本不是"创建":
mv a b在同一个目录里改名 → 内核发出的是IN_MOVED_FROM+IN_MOVED_TO,也就是"移动"事件,不是IN_CREATE;- 跨目录移动(把别处的文件移进来)→ 同样是移动事件;
- 只有当文件被写入一个新文件时,才会产生
IN_CREATE。
所以,一个只监听"新建"的实现,天然就覆盖不到"改名"和"移动"这两种最常见的加文件方式。它的逻辑没错,只是监听的范围漏了这两类事件。
这也是为什么很多人觉得"媒体库时灵时不灵"——通过下载工具新写入的文件(新建)能被发现,而手工 mv 进去的(移动)不会。两种操作在用户看来都是"把文件放进去",在内核看来是两类完全不同的事件。
解决
最直接的办法:改名或移动之后,手动触发一次重新扫描。
- 在媒体库界面里点"扫描媒体库 / 刷新库 / 重新扫描";
- 或者按软件提供的方式触发一次库重建(如果增量扫描没反应,就整体重建)。
想减少手工操作的话,可以顺手把"移动"也纳入触发范围(如果你在写这类监控脚本):
# 监听目录:把 create 和 moved_to 都作为触发条件
inotifywait -m -r -e create,moved_to --format '%w%f' /path/to/media \
| while read -r f; do
echo "检测到新条目: $f"
# 在这里调用媒体库的扫描/导入命令
done
关键是监听的事件集合要包含 moved_to(移动到),只监听 created 就会漏掉 mv 进来的文件。
另外,很多下载工具支持"下载完成后执行脚本",可以配置它下载完成后自动通知媒体库扫描,这样就不用手工点触发了——把"新建/移动"统一收敛成一条"完成即扫描"的路径。
延伸与预防
这条坑的通用教训是:基于"新建事件"的监听,天然覆盖不到改名和移动。 凡是"只在文件出现时才处理"的机制,都要专门想一遍:"如果用户是移动/改名进来的呢?"
可复用的做法:
- 明确你要监听的事件集合。 文件系统的变化不止"创建"一种——创建、修改、移动、删除、属性变更,都是不同的事件。用哪个,取决于你要覆盖哪些操作。
- 把"用户意图"和"内核事件"对齐。 用户觉得"放文件进去"是一个动作,内核眼里却是好几个。设计触发逻辑时,站在用户视角列出所有"看起来一样"的操作,逐个确认是否被覆盖。
- 给兜底留一条路。 无论监听多全,都提供一个"手动全量扫描/重建"入口,作为任何漏事件情况的最后保障。
一句话:别指望一种事件能代表所有变化——想清楚用户会用什么方式操作,再决定监听什么。