侧边栏壁纸
博主头像
いちごみるく

行动起来,活在当下

  • 累计撰写 70 篇文章
  • 累计创建 48 个标签
  • 累计收到 55 条评论

目 录CONTENT

文章目录

两种方法解决linux下NFS等网络挂载文件变动不触发inotify,导致jellyfin等无法实时监控

本文分别说明 watchertoucher 的轮询刷新与 Autoscan 的触发转发,适用于需要将远程文件变化通知 Jellyfin 的场景。挂载路径、API 权限和轮询负载需在实际环境验证。

inotify 无法可靠感知由远端另一台机器造成的网络文件系统变化;本机对挂载目录的操作仍可能产生事件,因此“不支持任何本地 inotify”过于绝对。Linux 手册建议对远端变化使用轮询。inotify(7)

先尝试 Jellyfin 自身的计划扫描。如果希望缩短延迟,可让轮询程序触发 Jellyfin API;若下载器能发送 webhook,则可用 Autoscan 汇总事件和转换路径。两者都不能把远端文件系统变成本地实时通知,也没有固定一分钟必然更新的保证。

一、watchertoucher:轮询并请求刷新

上游 watchertoucher 已在 0.1.0 起改为 PollingObserver + Jellyfin API,不再靠创建虚拟 toucher 文件欺骗 inotify。folder、libraries、touchfile、touchdelay 等早期变量不适用于这里介绍的版本。

1. 安装到独立环境

在 Debian/Ubuntu 的宿主或容器中安装,不在系统 Python 内直接 pip 写包:

sudo apt update
sudo apt install python3-venv curl
sudo useradd --system --home-dir /opt/watchertoucher --shell /usr/sbin/nologin mediawatch
sudo install -d -m 0755 /opt/watchertoucher
sudo python3 -m venv /opt/watchertoucher/.venv
sudo /opt/watchertoucher/.venv/bin/python -m pip install watchdog requests
sudo curl -fL https://raw.githubusercontent.com/pulpul-s/watchertoucher/main/watchertoucher.py \
  -o /opt/watchertoucher/watchertoucher.py
sudo chown root:mediawatch /opt/watchertoucher/watchertoucher.py
sudo chmod 0640 /opt/watchertoucher/watchertoucher.py

用户已存在时不再执行 useradd。阅读下载的源码,记录版本或 commit,维护时不要自动拉 main 后直接替换。给 mediawatch 对媒体挂载目录的读取和遍历权限,通常不需要写媒体文件。项目 README 与变更记录

2. 修改当前脚本的实际参数

在 Jellyfin 后台创建 API Key,编辑 /opt/watchertoucher/watchertoucher.py:

jellyfin_url = "http://127.0.0.1:8096"
api_key = "替换为Jellyfin的API密钥"
mediafolder = "/mnt/media/"
delay_seconds = 60
po_timeout = 30
log_to_file = False
log_to_stdout = True

Jellyfin 在另一台机器或 Docker 网络内时,URL 换成脚本进程实际能访问的地址,不能照抄 localhost。mediafolder 是所有需要监视媒体库的父目录;上游递归轮询其内容,filetypes 决定匹配哪些扩展名。po_timeout 增大能减少目录遍历频率,也会增加延迟。API 密钥属于敏感凭据,不提交脚本副本或带密钥的日志。

当前源码响应新建、删除、移动事件,并向 /Library/Refresh 发送全库刷新请求;它不是只扫描一个子目录的工具,也没有为每种原地修改提供事件处理。全库很大、API 不稳定或希望精确刷新时,应选择计划任务或有路径处理能力的工具。当前脚本源码

3. 用 systemd 管理

保存为 /etc/systemd/system/watchertoucher.service,RequiresMountsFor 替换为实际远端挂载点:

[Unit]
Description=Poll media paths and refresh Jellyfin library
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/mnt/media

[Service]
Type=simple
User=mediawatch
Group=mediawatch
ExecStart=/opt/watchertoucher/.venv/bin/python -u /opt/watchertoucher/watchertoucher.py
Restart=on-failure
RestartSec=10
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now watchertoucher.service
sudo systemctl status watchertoucher.service
sudo journalctl -u watchertoucher.service -f

先观察实际目录规模、内存和 CPU,再设置资源上限。将资源限制固定为 CPUQuota=2%、MemoryMax=64M 并每日重启,可能使大库轮询被节流或进程反复退出,不适合作为通用优化。

4. 验证远端变化

从另一台客户端向共享目录放入一个合法的小型媒体样本,观察轮询日志、API 请求结果、Jellyfin 扫描任务和最终库条目,再测试删除与移动。空的 test.mp4 不是有效媒体,测试本机 touch 也不能证明远端变化已被发现。

远端离线、目录缓存尚未失效或服务没有权限时,应先恢复挂载再扫描,避免把临时空目录当作真实删除。

5. Python watchdog 与内核 watchdog 无关

Python Watchdog 与内核 NMI/soft-lockup 看门狗是不同机制。kernel.watchdog_thresh 改变的是内核锁死检测阈值,既不减少 Python 的轮询开销,也不修复锁死,提高它可能只是延后报警。高占用应通过增加轮询间隔、缩小目录范围、减少扫描次数和分析实际 I/O 处理。Linux 锁死检测文档

二、Autoscan:接收事件并转换路径

Cloudbox/autoscan 支持 Plex、Emby、Jellyfin 目标及 Sonarr/Radarr 等触发器。manual webhook 是外部发起的扫描请求,不是一个配置目录后就自动轮询的监视器;它也无法让 inotify 自动发现远端变化。

1. 部署

提前创建 /srv/autoscan 并写入下面的 config.yml。媒体目录只读挂载,管理端口先绑定回环地址;需要下载器访问时使用受控内网地址、认证和防火墙。

docker run -d \
  --name autoscan \
  -e PUID=1000 \
  -e PGID=1000 \
  -p 127.0.0.1:3030:3030 \
  -v /srv/autoscan:/config \
  -v /mnt/media:/mnt/media:ro \
  --restart=unless-stopped \
  cloudb0x/autoscan:latest

2. 正确的最小配置

port: 3030
minimum-age: 2m

authentication:
  username: mediahook
  password: 替换为长随机密码

triggers:
  manual:
    priority: 0

targets:
  jellyfin:
    - url: http://Jellyfin内网地址:8096
      token: 替换为Jellyfin的API密钥
      rewrite:
        - from: ^/mnt/media/
          to: /data/

manual 是一个映射对象,不是带 paths、recursive、include 的数组;这些配置项不能从其他触发器结构直接套用。minimum-age 在顶层配置处理器延迟,不能承诺两分钟后所有文件就下载完整。上例把 Autoscan 可见的 /mnt/media/ 转成 Jellyfin 实际库路径 /data/,每一层容器内路径都必须核对。

上游还提供 anchors 检查挂载可用性;如使用,应把锚文件放在真正的远端根目录,避免挂载离线后误扫空目录。Autoscan 配置说明

3. 手工触发

以下用户名应与配置一致,curl 会交互提示密码:

curl --fail --user mediahook --request POST \
  'http://127.0.0.1:3030/triggers/manual?dir=%2Fmnt%2Fmedia%2FMovies%2FExample'

核对日志中的队列、重写后路径和 Jellyfin 目标响应,再检查最终库条目。若设成定时任务,认证使用权限受限的凭据文件,避免把密码放进 crontab;全库每五分钟扫描并非普遍合理的默认值。

0

评论区