文章

树莓派家庭影院(一):Docker 自动化电影下载管线(Radarr + Jackett + qBittorrent + Bazarr)

在树莓派上用 Docker 部署 Radarr、Jackett、qBittorrent、Bazarr 和 ChineseSubFinder,实现指定电影自动下载到 Samba 共享目录,并自动补充中文或英文字幕。

树莓派家庭影院(一):Docker 自动化电影下载管线(Radarr + Jackett + qBittorrent + Bazarr)

本系列共三篇:第一篇(本文)搭建“搜索 → 下载 → 字幕”的自动化电影下载管线;第二篇接入点播、播放、剧集、密码管理和统一入口;第三篇让电视上的 Kodi 使用 Jellyfin 媒体库与服务端字幕。

⚠️ 安全警告:本文会公开本实验环境的登录地址、账号、密码和 API Key。这些凭证在发布后即视为已泄露,请勿直接用于生产环境或长期暴露的服务。建议读者在复现时替换为自己的强密码,并在公网访问时加 VPN/反向代理 + HTTPS。(第二篇引入 Vaultwarden,正是为了终结这种“凭证写进文档”的管理方式。)

1. 目标

让树莓派变成一个可以通过网页操作的“电影下载站”:

  1. 打开网页搜索电影。
  2. 选择想下载的版本(优先 4K)。
  3. 自动通过 BT 下载到 Samba 共享目录 /share/Movies
  4. 自动下载字幕:优先使用字幕源提供的原版中英双语字幕,其次使用单中文或单英文字幕。

2. 最终架构

flowchart LR
    User[浏览器] -->|搜索/添加电影| Radarr[Radarr 7878]
    Radarr -->|查询种子| Jackett[Jackett 9117]
    Jackett -->|Torznab| TPB[The Pirate Bay]
    Jackett -->|Torznab| RARBG[TheRARBG]
    Jackett -->|Torznab| TP2[TorrentProject2]
    Radarr -->|下发任务| qBittorrent[qBittorrent 8085]
    qBittorrent -->|下载中| Downloads[(/share/Downloads)]
    Radarr -->|硬链接/整理| Movies[(/share/Movies)]
    Bazarr[Bazarr 6767] -->|同步片库| Radarr
    Bazarr -->|下载字幕| OpenSubtitles[OpenSubtitles.com]
    Bazarr -->|中文字幕| Zimuku[字幕库/射手网等]
    Bazarr -->|下载原版中文/英文字幕| Movies
    Bazarr -->|下载原版中文/英文字幕| Series[(/share/Video/Series)]
    CSF[ChineseSubFinder 19035] -->|定时扫描| Movies
    CSF -->|定时扫描| Series
    CSF -->|下载原版中文/双语字幕| Movies
    CSF -->|下载原版中文/双语字幕| Series
    subgraph 树莓派
        Radarr
        Jackett
        qBittorrent
        Bazarr
        CSF
        Downloads
        Movies
        Series
    end

2.1 各服务职责

Radarr:电影收藏管理器(*arr 家族中的一员)。它维护一份“想看的电影”列表,自动追踪影片上映信息,决定应该下载哪个版本,并把下载任务发送给下载客户端。下载完成后,它还会把文件整理/重命名到媒体库目录。

Jackett:索引器聚合器。BT 站点(如 The Pirate Bay)通常没有统一、机器可读的 API,Jackett 把它们封装成标准的 Torznab API,让 Radarr 可以用同一种方式查询多个站点的种子。

qBittorrent:BT 下载客户端,负责实际的 P2P 下载。Radarr 通过 qBittorrent 的 WebUI API 添加种子、监控进度;下载完成后把文件落到 /share/Downloads,再由 Radarr 硬链接/移动到 /share/Movies

Bazarr:字幕管理器。它定期向 Radarr 索取电影库清单,比对哪些影片缺少指定语言的字幕,然后到 OpenSubtitles 等字幕站下载原版字幕。语言 Profile 以中文为 cutoff,找到中文(其中可能本身就是中英双语)后停止;没有中文时保留英文兜底。

ChineseSubFinder(可选补充):专攻中文字幕的工具,会从中文来源匹配原版中文或中英双语字幕。它按计划任务直接扫描电影和剧集目录,和 Bazarr 并行工作;候选中优先选择双语字幕,其次选择中文字幕。当前 latest 镜像带一个轻量 WebUI,但只能查看媒体列表、控制系统状态和任务日志,不能像 Bazarr 那样逐个候选手动挑选字幕

2.2 工作流时序

sequenceDiagram
    actor U as 用户
    participant R as Radarr
    participant J as Jackett
    participant Q as qBittorrent
    participant B as Bazarr
    participant OS as OpenSubtitles
    participant Z as 字幕库/射手网
    participant CSF as ChineseSubFinder
    participant S as /share/Movies

    U->>R: 搜索并添加电影
    R->>J: 查询 Torznab: 有这部电影的种子吗?
    J-->>R: 返回种子列表
    R->>R: 按画质优先级挑选最佳种子
    R->>Q: 添加下载任务
    Q->>Q: P2P 下载
    Q-->>R: 下载完成
    R->>S: 硬链接/移动到媒体库
    R-->>B: Webhook 通知电影下载/移动完成
    B->>R: 同步电影库(兜底轮询)
    B->>B: 按语言 Profile 检查字幕(zh 优先、en 兜底)
    B->>Z: 优先搜索原版中文字幕
    Z-->>B: 返回 .zh(.hi).srt(可能本身是双语)
    B->>OS: 找不到中文时搜索英文字幕
    OS-->>B: 返回 .en(.hi).srt
    B->>S: 原样写入下载到的字幕
    CSF->>S: 扫描并补充原版中文/双语字幕

时序说明:

  1. 用户在 Radarr 搜索并添加电影。
  2. Radarr 通过 Jackett 的 Torznab 接口查询索引站点。
  3. Jackett 返回候选种子,Radarr 根据画质配置挑选最优版本。
  4. Radarr 把种子推给 qBittorrent,qBittorrent 开始 P2P 下载。
  5. 下载完成后,Radarr 把文件整理到 /share/Movies
  6. Radarr 通过 Webhook 主动推送事件给 Bazarr(电影下载/移动完成);同时 Bazarr 也会定期轮询 Radarr 的电影库做兜底同步。
  7. Bazarr 发现缺字幕后按 zh → en 搜索,zh 是 cutoff:优先采用字幕站给出的原版中文字幕,找不到时用英文兜底。原版 .zh.srt 可能是双语,也可能只有中文,Bazarr 不分析正文内容。
  8. Bazarr 原样保存下载结果,不执行自定义 Post-Processing,也不生成 .zh+en.srt
  9. (可选)ChineseSubFinder 每 6 小时扫描一次 /share/Movies/share/Video/Series,从中文来源补充原版中文或中英双语字幕。

2.3 为什么这些名字都这么奇怪?

这套工具的名字看着像黑话,其实大多是有意为之的双关或谐音梗。

Radarr 来自 Radar(雷达)+ r,寓意“扫描发现电影”。同属 *arr 家族的还有:

工具用途名字梗
Sonarr电视剧Sonar(声纳),像雷达一样扫描剧集
Lidarr音乐Lid(盖子/眼睑),比较冷门的双关
Readarr电子书/有声书Read(阅读)直接加 r
Bazarr字幕Bazaar(集市),字幕来自五湖四海
Prowlarr索引器统一管理Prowl( prowling,四处搜寻)
Radarr电影Radar(雷达)

Jackett 则像是一件 jacket(夹克),给形形色色、接口各异的 BT 站点套上一层统一外套,让它们都能以 Torznab 协议对外服务。

qBittorrent 里的 q 指它基于 Qt 图形框架开发;Bittorrent 就是 P2P 下载协议本身。

TorznabTorrent + Newznab 的拼接。Newznab 是 Usenet 时代的 API 标准,Torznab 把它借用到 BT 世界,所以名字听起来像两种不同技术强行结婚生的孩子。

简单记法:

  • 管电影的带“雷达”——Radarr
  • 管字幕的像“集市”——Bazarr
  • Jackett 是 BT 站的统一“外套”
  • qBittorrent 就是实际干下载活的 BT 客户端

3. 环境信息

  • 设备:Raspberry Pi,ARM64,8 GB RAM
  • OS:Linux 6.12.93(6.12.93+rpt-rpi-2712)
  • Docker:29.5.3,Docker Compose v5.1.4
  • 服务版本(2026-08-05 升级至最新镜像):Radarr 6.3.0.10514、Jackett v0.24.2327、qBittorrent v5.2.3、Bazarr v1.6.0;ChineseSubFinder 镜像自 2023-12 后无更新
  • 已有服务:V2Ray(HTTP 代理 127.0.0.1:10809)、Portainer、AdGuard Home
  • Samba 共享:/share,局域网地址 192.168.1.7
  • Docker 默认网桥网关:172.18.0.1,容器内通过 172.18.0.1:10809 走宿主机 V2Ray 代理

4. 部署步骤

4.1 创建目录

1
2
3
4
5
6
mkdir -p /share/Movies /share/Downloads
mkdir -p /home/pi/docker/qbittorrent/config
mkdir -p /home/pi/docker/radarr/config
mkdir -p /home/pi/docker/jackett/config
mkdir -p /home/pi/docker/bazarr/config/scripts
mkdir -p /home/pi/docker/chinesesubfinder/config

4.2 Docker Compose

/home/pi/docker/compose.yml 里追加以下服务(与已有的 v2ray 等共存):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  qbittorrent:
    image: linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Asia/Shanghai
      - WEBUI_PORT=8085
    volumes:
      - /home/pi/docker/qbittorrent/config:/config
      - /share/Downloads:/downloads
    ports:
      - "8085:8085"
      - "6881:6881"
      - "6881:6881/udp"
    restart: unless-stopped

  radarr:
    image: linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Asia/Shanghai
      - HTTP_PROXY=http://172.18.0.1:10809
      - HTTPS_PROXY=http://172.18.0.1:10809
      - NO_PROXY=localhost,127.0.0.1,qbittorrent,radarr,jackett,bazarr
    volumes:
      - /home/pi/docker/radarr/config:/config
      - /share/Movies:/movies     # ⚠️ 错误示范:与下一行是两个独立 bind mount,会导致硬链接失败、空间翻倍,正确写法见第 10 节
      - /share/Downloads:/downloads  # ⚠️ 错误示范:应改为单一 /share 挂载,见第 10 节
    ports:
      - "7878:7878"
    restart: unless-stopped

  jackett:
    image: linuxserver/jackett:latest
    container_name: jackett
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Asia/Shanghai
      - HTTP_PROXY=http://172.18.0.1:10809
      - HTTPS_PROXY=http://172.18.0.1:10809
      - NO_PROXY=localhost,127.0.0.1,qbittorrent,radarr,jackett,bazarr,yts.gg,movies-api.accel.li,yts.mx
    volumes:
      - /home/pi/docker/jackett/config:/config
    ports:
      - "9117:9117"
    restart: unless-stopped

  bazarr:
    image: linuxserver/bazarr:latest
    container_name: bazarr
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Asia/Shanghai
      - HTTP_PROXY=http://172.18.0.1:10809
      - HTTPS_PROXY=http://172.18.0.1:10809
      # 国内字幕站必须直连,走代理会 SSL 超时,见第 11.1 节
      - NO_PROXY=localhost,127.0.0.1,qbittorrent,radarr,jackett,bazarr,yts.gg,movies-api.accel.li,yts.mx,zimuku.org,shooter.cn,subf2m.com,subhd.tv,a4k.net,assrt.net,subtitle.best
    volumes:
      - /home/pi/docker/bazarr/config:/config
      - /share/Movies:/movies
      - /share/Downloads:/downloads
    ports:
      - "6767:6767"
    restart: unless-stopped

  chinesesubfinder:
    image: allanpk716/chinesesubfinder:latest
    container_name: chinesesubfinder
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Asia/Shanghai
    volumes:
      - /home/pi/docker/chinesesubfinder/config:/config
      - /share/Movies:/media
      - /share/Video/Series:/series
    ports:
      - "19035:19035"
    restart: unless-stopped

注意:

  • qBittorrent 的 WebUI 端口改成 8085,因为宿主机 8080 已被其他服务占用。
  • Radarr/Jackett/Bazarr 不直接用内置代理,而是通过容器环境变量走 V2Ray,这样更稳定。
  • 容器下载目录统一挂到 /share/Downloads,电影最终目录是 /share/Movies但 radarr 不能像上面那样把两者挂成两个独立 volume——这会让导入时的硬链接悄悄退化为复制,空间翻倍(2026-07-18 踩坑实录,排查与正确写法见第 10 节)。
  • ChineseSubFinder 将电影和剧集分开挂载:容器内 /media 对应宿主机 /share/Movies/series 对应 /share/Video/Series

4.3 启动

1
2
cd /home/pi/docker
docker compose up -d qbittorrent radarr jackett bazarr chinesesubfinder

5. 各服务配置

5.1 qBittorrent

  • 地址:http://192.168.1.7:8085
  • 账号:admin
  • 密码:pi123456

Radarr 里添加下载客户端时,主机填 qbittorrent,端口填 8085(因为容器内 qBittorrent 的 WebUI 监听的是 8085)。

5.2 Jackett

  • 地址:http://192.168.1.7:9117
  • API Key:1gh53xjyx9l1djek69yas386y7wtcjoc

配置步骤:

  1. 进入 Jackett → Add indexer。
  2. 添加以下公开索引器(推荐至少加 TPB):
    • The Pirate Bay
    • TheRARBG
    • TorrentProject2
  3. 在 Jackett 设置里的 Proxy 先留空,靠容器环境变量 HTTP_PROXY/HTTPS_PROXY 走代理。
  4. 对每个 indexer 复制其 Torznab feed URL,例如:
    • TPB: http://192.168.1.7:9117/api/v2.0/indexers/thepiratebay/results/torznab/
    • TheRARBG: http://192.168.1.7:9117/api/v2.0/indexers/therarbg/results/torznab/
    • TorrentProject2: http://192.168.1.7:9117/api/v2.0/indexers/torrentproject2/results/torznab/

踩坑:Jackett 内置代理填 127.0.0.1:12345 会失败,因为容器内 127.0.0.1 不是宿主机。改成容器环境变量代理后正常。

5.3 Radarr

  • 地址:http://192.168.1.7:7878
  • API Key:be67ae6612cc4061a7a2335723893305

配置步骤:

  1. Settings → Media Management → Root Folders:添加 /movies
  2. Settings → Download Clients → Add → qBittorrent
    • Host:qbittorrent
    • Port:8085
    • Username:admin
    • Password:pi123456
  3. Settings → Indexers → Add → Torznab:把 Jackett 里每个 indexer 都单独加一次:
    • URL:对应 indexer 的 Torznab URL
    • API Key:Jackett 的 API Key 1gh53xjyx9l1djek69yas386y7wtcjoc
    • 当前已添加:TPB、TheRARBG、TorrentProject2
  4. Profiles → 编辑 Ultra-HD:把画质优先级调整为:
    1. Remux-2160p
    2. Bluray-2160p
    3. WEB-DL-2160p
      • 禁用 HDTV-2160p,避免下载到低质量 4K。

Radarr 是怎么决定下载哪个版本的

Radarr 做决策时主要考虑三个维度:可用性画质索引器来源

可用性:Minimum Availability

  • 路径:添加/编辑电影时的 Minimum Availability,或在 Settings → Profiles 里设置默认值。
  • 它决定电影到什么阶段才允许开始搜索:
    • Announced:刚加入 Radarr 就搜索,通常没资源;
    • In Cinemas:院线上映后搜索,可能下到枪版;
    • Released:蓝光/流媒体正式发行后搜索,资源最稳,推荐
  • 建议保持 Released,避免下到预告片或枪版。

画质:Quality Profile

  • 路径:Settings → Profiles
  • 每个 profile 是一组画质的排序列表,Radarr 会优先选择排在前面的画质。
  • 当前预置 profile 有:Any、SD、HD-720p、HD-1080p、Ultra-HD、HD - 720p/1080p。
  • 想优先 4K,就给电影指定 Ultra-HD profile。
  • 如果 UI 支持设置 Default Quality Profile,可以把它设成 Ultra-HD;否则每次添加电影时手动选。
  • 已经下载成 1080p 的电影,再改成 Ultra-HD 后,Radarr 会显示 "Cutoff Unmet",但不会自动重新搜索。你需要手动进入电影页面点 Search,或者等 RSS 监控刷到新的 4K 种子。

停止升级:Upgrade Until / Cutoff

  • 在 Quality Profile 里还有一个 Upgrade Until(也叫 Cutoff)设置。
  • 它表示“达到这个画质后就不再继续升级”。
  • 例如 Ultra-HD profile 里 Upgrade Until 设为 Remux-2160p,那么一旦下载到 Remux-2160p,Radarr 就会停止,不会再下载其他 4K 版本。
  • 如果看到 Release Rejected: Existing file meets cutoff: Remux-2160p,说明当前文件已经满足 cutoff,这是正常行为。

同一画质内继续升级:Custom Format Cutoff Score

  • 路径:Settings → Custom Formats 定义规则,Settings → Profiles 里设置 Upgrade Until Score
  • Custom Formats 让你给发布组、来源、音轨等属性打分。比如 SPHD +50、Atmos +20、HMAX -30。
  • Upgrade Until Score(即 Custom Format Cutoff Score)是“满意分数”。
  • Radarr 会升级,直到同时满足:画质达到 Upgrade Until 且 Custom Format Score 达到 Upgrade Until Score
  • 不折腾的话保持 Upgrade Until Score = 0,只按画质 cutoff 停止即可。

索引器来源:Indexer Priority

  • 路径:Settings → Indexers
  • 每个 indexer 有一个 Priority 数字,越小越优先
  • 当前配置:
IndexerPriority
TPB5
TheRARBG10
TorrentProject222
  • 同一部电影、同一画质下,Radarr 会优先尝试 TPB,没有结果再 fallback 到 TheRARBG,最后 TorrentProject2。

综合决策顺序

  1. 电影必须达到 Minimum Availability 才进入搜索池;
  2. Quality Profile 选最高可用画质,但不超过 Upgrade Until
  3. 如果启用了 Custom Formats,同一画质内继续升级直到 Upgrade Until Score 满足;
  4. 在该画质下按 Indexer Priority 选优先级最高的 indexer;
  5. 同一 indexer 多个结果中,优先选做种数多的种子。

资源来源类型详解:从枪版到原盘

Radarr 的画质体系是来源类型 × 分辨率的二维组合,比如 WEB-1080p 表示“流媒体源 + 1080p”。Quality 列表里的所有类别按来源的演进顺序可以分成几组,下面逐一说明。

影院流出类(枪版,不推荐)
缩写全称含义质量建议
CAMCamera摄像机在影院偷拍,画面抖、常有人头影子和观众笑声最差禁用
TELESYNC(TS)Telesync也是影院偷拍,但接了影院的外接音源(如无障碍音频孔),声音比 CAM 干净禁用
TELECINE(TC)Telecine把影院放映用的胶片拷贝通过胶转磁设备转成数字文件比 CAM/TS 好,但仍属枪版禁用
WORKPRINTWorkprint未完成剪辑的工作样片流出,可能缺特效、音轨未混音、有多余片段不稳定禁用

新片刚上映时往往只有这类源,比如《功夫女足》的 TC国语v2 就是 Telecine 源;v2 是发布组修正后的第二版(修音画不同步、补缺失片段等),不代表画质档次提升。这就是 Minimum Availability 推荐保持 Released 的原因——等正式发行再动手,自动避开枪版。

提前泄露 / 样片类(不推荐)
缩写全称含义质量建议
DVDSCR(DVD Screener)DVD Screener片方送审、评奖、送媒体用的 DVD 样片流出,画面可能带“仅供评审”水印和时间码接近 DVD 正版禁用
REGIONALRegional(常见为 R5)某些地区(如俄罗斯 R5 区)DVD 提前于全球发行,流出后被转录接近 DVD禁用

这两类是在正式零售版之前泄露的“正版样片”,画质尚可,但有水印、缺内容等风险。

DVD / 标清时代(收藏价值有限)
缩写全称含义建议
DVDDVDDVD 光盘的直接转录老片没有高清源时可接受
DVD-RDVD-R完整 DVD 光盘镜像(含菜单),体积大、播放麻烦不推荐
SDTVStandard Definition TV标清电视信号采集老片兜底
电视采集类(HDTV)
缩写全称含义质量建议
HDTVHigh Definition TV从电视广播信号采集,可能带台标、插广告、码率受限不如 WEB / 蓝光有 WEB / 蓝光源就不勾

HDTV 有 720p / 1080p / 2160p 各档。它的唯一价值是时效——电视剧和电视节目的 HDTV 源往往出得最快,对电影来说通常很快被 WEB / 蓝光取代,所以本文 Ultra-HD profile 里禁用了 HDTV-2160p。

流媒体类(WEB,推荐 WEB-DL)
缩写全称含义质量建议
WEB-DLWeb Download从流媒体平台(Netflix、Disney+、Apple TV 等)服务器直接下载的原始视频流,不重编码和平台用户看到的完全一致推荐
WEBRipWeb Rip对 WEB-DL 二次压缩,或用录屏 / 采集方式转录有一次编码损失能选 DL 就不要 Rip

注意 Radarr 里 WEB 2160p 是一个分组,里面同时包含 WEBRip-2160pWEBDL-2160p 两个子项,组内排上面的优先。只标 WEB、没写 DL 还是 Rip 的资源,一般按 WEB-DL 档处理。

同分辨率下优先级:WEB-DL > WEBRip > HDTV

蓝光家族(终极收藏)
缩写全称含义体积建议
BlurayBlu-ray Encode蓝光盘重编码压缩版,砍掉多余码率适中(1080p 约 2–10 GB)推荐,性价比最高
RemuxRemux(重新封装)蓝光原盘的视频流不重编码直接抽出,只去掉多余音轨 / 字幕 / 花絮后重新封装成 mkv大(1080p 约 20–40 GB,2160p 更大)画质党 / 收藏推荐
BR-DISKBlu-ray Disk完整蓝光原盘镜像(ISO 或 BDMV 文件夹),含菜单、花絮、全部音轨最大(25–100 GB)一般不用,播放需挂载
Raw-HDRaw HD未经压缩的原始高清采集流极大基本不用

同分辨率下:Remux > Bluray > WEB-DL

无法识别
缩写含义建议
UnknownRadarr 无法从文件名解析出来源类型禁用,避免下到乱命名的垃圾源
推荐勾选方案

以“4K 优先、1080p 兜底”为例:

  • ✅ 勾:Remux-2160pBluray-2160pWEB 2160pRemux-1080pBluray-1080pWEB 1080p
  • ❌ 不勾:CAM / TELESYNC / TELECINE / WORKPRINT(枪版)、DVDSCR / REGIONAL(样片泄露)、HDTV 全系(有更好源)、DVD / DVD-R / SDTV(标清)、BR-DISK / Raw-HD(原盘)、Unknown
资源名常见后缀补充
  • v2 / v3:发布组修正版,修 bug 但不提升画质档次;
  • PROPER:发布组认为别家的版本有问题,自己重发一个“正确版”;
  • REPACK:同组修正自己之前发错的版本;
  • x264 / x265(HEVC):视频编码格式,x265 同画质体积更小,但需要播放设备支持解码。

5.4 Bazarr

  • 地址:http://192.168.1.7:6767
  • API Key:db657bd7209430ebc1e25832b24c9d1b

配置步骤:

  1. Settings → Languages → Add New Profile
    • Name:原版字幕:中文优先,英文兜底
    • Language items 按 zh → en 排列,使用 alpha2 code(不要写“中文”“英文”,否则会报 ValueError: None is not a valid language)。
    • Cutoff 选择 zh。它是整条管线的“毕业线”:找到中文后才算满足,影片彻底退出 Wanted 清单;中文缺席期间会先下载英文兜底,但英文不满足 cutoff——条目会一直留在 Wanted 里,由每 6 小时的自动搜索持续重试中文,哪天中文字幕发布了就会自动补上。中文补上后英文文件保留,两条字幕共存,播放端随意切换。
  2. Settings → Providers:启用以下字幕源,覆盖英文和中文:
    • OpenSubtitles.com:英文/多语言字幕较全,需要到 OpenSubtitles.com 注册免费账号并填入用户名/密码。免费账号每天下载配额约 20 条,且默认匹配分门槛会挡掉部分中文字幕,见第 11.3 节。
    • subf2msubxshooter(射手网):中文字幕源,国内资源较多。注意这些国内站点必须直连,不能走代理,见第 11.1 节。
    • zimuku(字幕库):因反爬验证码问题已禁用,见第 11.2 节。
  3. Settings → Languages → Default Settings
    • Movies 和 Series 都启用默认 Profile,选择 原版字幕:中文优先,英文兜底
    • 对已有电影和剧集分别执行 Mass Edit,把同一 Profile 批量套用到全部条目。
  4. Settings → Radarr / Sonarr
    • IP:radarr
    • Port:7878
    • API Key:Radarr 的 API Key be67ae6612cc4061a7a2335723893305
    • Sonarr 同理使用 sonarr:8989 和 Sonarr 的 API Key;启用后 Bazarr 会同时管理电影与剧集字幕。
  5. Settings → Subtitles → Post-Processing:关闭 Use Custom Post-Processing,Post-Processing Command 留空。

Bazarr 只知道字幕的语言标签,无法判断一个 zh 文件的正文是“中英双语”还是“只有中文”。因此这里不做内容合并:中文站返回原版双语字幕时直接使用;否则接受单中文字幕,实在没有中文时再用英文。.hi 表示 Hearing Impaired(听障版),如果不需要对白之外的音效描述,可在 Profile 中取消 HI。

手动下载与多字幕共存

除了自动搜索,Bazarr 也支持手动干预:

  • 手动搜索:在单部电影或单集的页面里使用手动搜索,会列出所有字幕源的候选及各自得分,且不受最低匹配分限制,可以任意挑选下载(适合给某一部单独换更好的版本);也可以直接上传本地字幕文件,Bazarr 会按规范重命名并归档到对应影片目录。
  • 多条字幕按文件名共存:外挂字幕按语言标签命名,文件名不冲突就共存——不同语言(.en.srt + .zh.srt)、同语言不同格式(.zh.srt + .zh.ass)、是否听障版(.zh.srt + .zh.hi.srt)都可以同时存在;只有完全同名才会被新下载的覆盖。播放端(Jellyfin/Kodi)会把这些文件识别为独立字幕轨,观看时可随意切换。

Bazarr 与 Radarr 如何通信

两者同时存在拉取推送两种机制:

  • Bazarr 主动拉取:Bazarr 每隔一段时间(默认 60 分钟)调用 Radarr 的 /api/v3/movie 接口,获取完整电影库清单,判断哪些电影缺字幕。
  • Radarr 主动推送:在 Radarr 的 Settings → Connect → Bazarr 里配置 Webhook 后,Radarr 会在电影下载完成、重命名、添加/删除等事件发生时,立即通知 Bazarr 去检查该电影的字幕。

webhook 让字幕下载更及时,定期拉取则负责兜底同步。两个都配好是最佳状态。

原版字幕的落盘形态

以电影 Inception (2010).mkv 为例,Bazarr 只保存字幕源返回的原版文件:

1
2
3
Inception (2010).mkv
Inception (2010).zh.srt          # 优先;内容可能是原版双语,也可能只有中文
Inception (2010).en.srt          # 找不到中文时的英文兜底

媒体目录不再生成 .zh+en.srt。播放器优先选择 zh;如果这份原版中文字幕本身包含中英文,就显示双语,否则显示单中文。没有 zh 时再选择 en

5.5 ChineseSubFinder(可选,补充原版中文/双语字幕)

如果希望提高字幕源原本就是中英双语的概率,可以额外部署 ChineseSubFinder。它专门从迅雷字幕、射手网等中文来源匹配原版中文或双语字幕,比 Bazarr 更聚焦中文来源,但同样不负责把两份字幕重新合成。Zimuku(字幕库)则作为 Bazarr Provider 接入,不必在 ChineseSubFinder 中重复配置。

Docker Compose 追加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  chinesesubfinder:
    image: allanpk716/chinesesubfinder:latest
    container_name: chinesesubfinder
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Asia/Shanghai
    volumes:
      - /home/pi/docker/chinesesubfinder/config:/config
      - /share/Movies:/media
      - /share/Video/Series:/series
    ports:
      - "19035:19035"
    restart: unless-stopped

当前版本把主要设置保存在 /home/pi/docker/chinesesubfinder/config/ChineseSubFinderSettings.json。通常应在 WebUI 中修改;其中与扫描范围直接相关的结构如下:

1
2
3
4
5
6
7
8
9
10
11
{
  "common_settings": {
    "scan_interval": "@every 6h",
    "movie_paths": ["/media"],
    "series_paths": ["/series"]
  },
  "advanced_settings": {
    "sub_type_priority": 0,
    "save_multi_sub": false
  }
}

sub_type_priority: 0 表示字幕文件格式自动选择,不是语言优先级。ChineseSubFinder 的候选选择逻辑本身就会优先中英双语,再退到中文字幕;save_multi_sub: false 则只保留选出的结果,不把每个网站的候选都留在媒体目录。

启动后访问 http://192.168.1.7:19035

  • 账号:admin
  • 密码:admin

关于这个版本的 WebUI

当前 allanpk716/chinesesubfinder:latest 镜像(最后一次更新为 2023-12-01)带的是一个轻量 WebUI,功能有限:

  • ✅ 可以查看电影列表和封面;
  • ✅ 可以控制系统状态、启动/停止守护进程;
  • ✅ 可以查看任务日志;
  • 电影卡片点不开详情页
  • 不能在里面手动搜索或上传单部电影字幕

这意味着你不能像 Bazarr 那样在 ChineseSubFinder 的网页里点进电影选字幕。它只能作为一个后台自动扫描下载的兜底工具。

手动指定字幕的方法

既然 WebUI 不能操作,手动指定字幕只能走文件系统:

  1. 把字幕文件放到电影同目录,文件名和主视频文件同名:
    1
    2
    
    /share/Movies/Pressure (2026)/Pressure.2026.1080p...mp4
    /share/Movies/Pressure (2026)/Pressure.2026.1080p...chs.ass
    
  2. 回到 ChineseSubFinder WebUI 刷新媒体库,或等待每 6 小时一次的计划任务;当前配置不会因为重启容器就立刻执行字幕扫描。
  3. 看日志确认它识别到已有字幕:
    1
    
    docker logs -f chinesesubfinder
    

必须有的 .nfo 元数据

ChineseSubFinder 扫描电影时需要目录里有 .nfo 元数据文件(通常由 Radarr 生成)。如果缺少 .nfo,日志会报:

1
no metadata file, movie.xml or *.nfo

此时 WebUI 里可能连电影封面都显示不出来,或者显示为无法点击的空白卡片。

解决办法:在 Radarr 里开启 metadata 生成:

  1. Radarr → Settings → Metadata
  2. 启用 Kodi (XBMC)Emby,勾选 Movie Metadata / .nfo
  3. 对已有电影执行 Refresh & ScanOrganize,让 Radarr 生成 .nfo
  4. 回到 ChineseSubFinder WebUI 刷新媒体库,字幕下载则等待下一次计划任务。

版本状态

allanpk716/chinesesubfinder:latest 目前就已经是最新版,不要再指望更新它能获得完整 WebUI。作者已经明确转向 Lite 路线,全功能版本不再维护。如果你确实需要完整的电影详情管理界面,只能换用其他工具(例如主要依赖 Bazarr)。

5.6 字幕从下载到播放的完整链路

Bazarr 与 ChineseSubFinder 都能写字幕,但两者的触发方式不同。Radarr 和 Sonarr 在媒体新增、下载完成、升级或重命名时通过 Webhook 通知 Bazarr;Bazarr 还会定期同步片库并搜索 Wanted 队列。ChineseSubFinder 不接收这类通知,而是每 6 小时直接扫描电影与剧集目录。

sequenceDiagram
    participant A as Radarr / Sonarr
    participant B as Bazarr
    participant C as ChineseSubFinder
    participant M as 电影 / 剧集目录
    participant J as Jellyfin

    A->>M: 导入视频文件
    A-->>B: Webhook 通知媒体变化
    B->>B: 检查内置与外挂字幕
    B->>M: 从 Zimuku 等来源写入原版 zh / en 字幕
    C->>M: 每 6 小时扫描媒体目录
    C->>M: 优先写入原版中英双语,其次中文字幕
    J->>M: 实时扫描视频和同名外挂字幕
    J-->>J: 合并展示内置与外挂字幕轨

Bazarr 当前启用了 OpenSubtitles.com、射手网、Subf2m、SubX 和 Embedded Subtitles(Zimuku 因反爬问题已禁用,见第 11.2 节)。统一 Profile 按 zh → en 搜索,并把 zh 设为 cutoff:只要找到中文就视为满足,没有中文时才用英文兜底。由于字幕站通常把中英双语与单中文字幕都标成 zh,Bazarr 无法仅凭语言标签判断正文是不是双语;ChineseSubFinder 则在自己的候选中优先挑选原版双语字幕。

两套工具下载的网络字幕最终都是外挂字幕,与视频放在同一个目录、使用相同主文件名:

1
2
3
4
5
6
7
8
9
/share/Movies/Movie Name (2025)/
├── Movie Name (2025).mkv
├── Movie Name (2025).zh.srt
└── Movie Name (2025).en.srt

/share/Video/Series/Series Name/Season 01/
├── Series Name S01E01.mkv
├── Series Name S01E01.zh.ass
└── Series Name S01E01.en.srt

字幕在播放端分为三类:

类型存放位置能否选择或关闭
内置字幕MKV、MP4 等视频容器内部的字幕流可以
外挂字幕视频旁边独立的 .srt.ass 等文件可以
硬字幕已经压进视频画面不可以

视频自身携带的十几种语言仍然是内置字幕;Bazarr 或 ChineseSubFinder 下载到旁边的文件则是外挂字幕。Jellyfin 会读取视频容器中的字幕流,也会按照官方命名规则识别同名外挂字幕,然后把两者一起交给网页端、手机或 Kodi 选择。Jellyfin 对媒体目录使用只读挂载也不影响这个过程:字幕由前两套工具写入,Jellyfin 只负责扫描和播放。

Bazarr 与 ChineseSubFinder 彼此没有任务协调机制,理论上可能同时处理同一媒体。通常 Bazarr 发现已经存在满足 Profile 的 zh 后便不会继续把它列为缺失;若两者先后写入同一个 .zh.srt,最终保留的是最后一次写入的文件。当前系统不再运行任何自定义合并脚本,也不会生成 .zh+en.srt

6. 下载测试:《速度与激情 1》4K

  1. 在 Radarr 搜索 The Fast and the Furious(2001)。
  2. 选择 Ultra-HD 质量配置。
  3. Radarr 通过 Jackett/TPB 找到 4K UHD BluRay 版本,约 18.67 GB。
  4. 发送给 qBittorrent 开始下载。
  5. 由于种子数只有 1-2,速度较慢,预计需要几天。
  6. 下载完成后 Radarr 自动硬链接/移动到 /share/Movies

如果速度太慢,可以随时在 Radarr 里把画质改成 1080p 重新搜索。

7. 访问地址与凭证汇总

服务URL账号密码 / API Key
Radarrhttp://192.168.1.7:7878be67ae6612cc4061a7a2335723893305
Jacketthttp://192.168.1.7:91171gh53xjyx9l1djek69yas386y7wtcjoc
qBittorrenthttp://192.168.1.7:8085adminpi123456
Bazarrhttp://192.168.1.7:6767db657bd7209430ebc1e25832b24c9d1b
ChineseSubFinderhttp://192.168.1.7:19035adminadmin

8. 踩坑与备注

  1. 代理问题:Jackett 内置代理在容器里对 127.0.0.1 解析错误,改用容器环境变量 HTTP_PROXY=http://172.18.0.1:10809 解决。
  2. qBittorrent 端口:默认 8080 被占用,改为 8085,Radarr 里也要对应填 8085
  3. Radarr 4K 画质:需手动调整 Ultra-HD profile 优先级并禁用 HDTV-2160p
  4. Bazarr 语言 code:语言 profile 里必须用 en/zh,不能用中文名。
  5. 字幕源:OpenSubtitles.com 需要注册并填入账号密码,否则字幕下载会全部失败;ChineseSubFinder 账号密码为 admin/admin
  6. ChineseSubFinder 需要 .nfo 元数据:如果日志报 no metadata file, movie.xml or *.nfo,WebUI 的电影卡片会点不开或显示异常。需要在 Radarr 的 Settings → Metadata 里启用 .nfo 生成,然后刷新/整理已有电影。
  7. .hi 后缀字幕:Bazarr 下载的字幕可能是 .en.hi.srt / .zh.hi.srt(Hearing Impaired,听障版)。如果不需要对白之外的音效描述,可在语言 Profile 中取消勾选 Hearing Impaired
  8. ChineseSubFinder 版本现状allanpk716/chinesesubfinder:latest 目前(2023-12-01 后未再更新)已经是最新版,且作者已转向 Lite 路线,更新也不会带来能点进电影详情页的完整 WebUI。需要手动管理字幕时,建议主要使用 Bazarr。
  9. 安全:以上凭证仅用于本实验,发布本文后应视为已泄露,建议尽快修改。
  10. 硬链接从未生效(重点)/movies/downloads 两个独立 bind mount 导致跨挂载 link(2) 返回 EXDEV,Radarr 静默退化为复制,所有电影占双份空间。排查与修复详见第 10 节。

9. 后续可优化(多数已在第二篇落地)

本文发布时列的优化方向,两条主线已经在第二篇落地:

  • HTTPS + 反向代理:第二篇第 4 节用 Caddy + Homepage 实现了统一入口,家人只需记住 https://raspberrypi.local/,并为必须 HTTPS 的 Vaultwarden 单独保留了 8443 端口;
  • Sonarr 扩展电视剧:第二篇 2.5 节接入了 Sonarr,剧集走与电影完全相同的自动化管线,只是终点目录换成 /share/Video/Series

还有一个方向上的修正值得交代:本文按“优先 4K”配置的 Ultra-HD profile,在实际跑了一个月后收敛了——树莓派不适合实时转码,27 GB 的单文件对存储和播放都不友好,而下载管线本身(索引器、硬链接、字幕)与分辨率无关。第二篇把点播默认画质改为 1080p 起步,4K 只在点播时单部覆盖,具体代价和教训见第二篇 5.3 节。

仍然待办的:

  • 给 qBittorrent 设置完成后自动做种限制或分类标签。
  • 把 OpenSubtitles / ChineseSubFinder 账号密码改为环境变量注入,避免手动在 UI 填写。
  • 4K 下载慢时,可尝试加入更多公共索引器或 PT 站点。

10. 最大的坑:两个 bind mount 让"硬链接"悄悄变成复制(2026-07-18 补记)

本文前面的架构图和流程都写的是"Radarr 硬链接/整理到媒体库"。实际上线一个月后才发现:硬链接从未生效过,所有电影都是完整复制了两份,235G 磁盘被吃到 91%。这是整套方案里最值得单独成章的坑。

10.1 先理清:Downloads 和 Movies 两个目录的分工

理解这个坑之前,得先搞清楚为什么会有两个目录、为什么还要分别挂载。

目录谁在用角色里面的文件长什么样
/share/DownloadsqBittorrent下载中转站 + 做种田保持种子原始命名(如 The.Shawshank.Redemption.1994.RESTORED.1080p.BluRay.REMUX-DDB.mkv),还混着广告 txt、样图等发布组附带文件
/share/MoviesRadarr正式片库每片一个目录、规范重命名(如 The Shawshank Redemption (1994)/),附带 movie.nfoposter.jpgfanart.jpg 等元数据,供播放器、Bazarr、ChineseSubFinder 消费

关键在于:同一部电影需要同时存在于这两个角色里。做种要求原始文件留在 Downloads 里纹丝不动(改名或移走都会破坏种子完整性,没法继续做种);而片库要求规范命名、目录干净。鱼和熊掌要兼得,答案就是硬链接——同一份数据挂在两个路径下,互不干扰:

  • Radarr 导入时不是"移动"也不是"复制",而是在 Movies 里建一个指向同一份数据的硬链接,文件名随便改,Downloads 那份原封不动继续做种;
  • 以后任何一边删除都不影响另一边:qBittorrent 删种只删一个链接,片库文件还在;删片库文件也不伤害做种;
  • 磁盘空间只在所有硬链接都被删除后才真正释放。

所以"两个目录分别挂载"这个设计本身没有错——错的是挂成了两个相互独立的 bind mount,把硬链接这条路悄悄堵死了。接下来就看到这个堵法有多隐蔽。

10.2 现象:磁盘空间翻倍

事发是磁盘告警:235G 的盘用到 91%。du 逐层排查后发现 /share/Downloads 占了 49G,而其中 6 部电影(肖申克 21G REMUX、Toy Story 5 18G 2160p 等)在 /share/Movies 片库里也各有一份。

第一反应是:也许 Radarr 用的是硬链接,两份路径共享同一文件,du 只是重复计数了?用 stat 验证 inode 和链接数:

1
2
3
4
5
stat -c '%i %h %n' \
  "/share/Movies/The Shawshank Redemption (1994)/The.Shawshank.Redemption....mkv" \
  "/share/Downloads/The.Shawshank.Redemption.../The.Shawshank.Redemption....mkv"
# 5943816  1  /share/Movies/...
# 5438054  1  /share/Downloads/...

两个 inode 不同、链接数都是 1——如果是硬链接,两个路径应该指向同一个 inode 且链接数 ≥ 2。结论是:这是两份真实的拷贝,49G 是实打实多占的空间,"硬链接入库"从未发生过。

10.3 根因:跨 bind mount 的 link(2) 返回 EXDEV

分层验证的结果很有意思:

  • 宿主机上/share/Movies/share/Downloads 在同一块 ext4 上(stat -c %d 设备号相同),手动 ln 硬链接成功
  • radarr 容器内ln /downloads/xxx /movies/xxx 直接报 Cross-device link(EXDEV)。

原因在于:Linux 内核的 link(2) 要求源和目标落在同一个挂载(vfsmount)下。本文第 4.2 节的 compose 把 /share/Movies/share/Downloads 作为两个独立的 bind mount 挂进容器——哪怕底层是同一文件系统,跨 bind mount 做硬链接也会被内核拒绝。而 Radarr 虽然 copyUsingHardlinks: true,硬链接失败后会静默回退为复制,界面上没有任何告警,不用 stat 查 inode 根本发现不了。

这正是 TRaSH Guides 的经典文章 Hardlinks and Instant Moves (Atomic-Moves) 要解决的问题:它建议让下载目录和媒体库落在同一个挂载下(即所谓单一 /data 布局),硬链接和瞬时移动才能正常工作。linuxserver 官方示例 compose 里 /movies/downloads 两个 volume 的写法历史悠久,照抄就会踩中这个坑。

flowchart TD
    subgraph G1["修复前:两个 bind mount"]
        EXT4A["/share(同一 ext4)"] --> BA["bind: /share/Movies → /movies"]
        EXT4A --> BB["bind: /share/Downloads → /downloads"]
        BA -.->|"link() = EXDEV,退化为复制"| BB
    end
    subgraph G2["修复后:单一挂载 + 软链"]
        EXT4B["/share(同一 ext4)"] --> B1["bind: /share → /share"]
        B1 --> S1["/movies → /share/Movies(软链)"]
        B1 --> S2["/downloads → /share/Downloads(软链)"]
        S1 ==>|"link() 成功,零拷贝"| S2
    end
    style BA fill:#ffe3e3
    style BB fill:#ffe3e3
    style S1 fill:#e8f5e9
    style S2 fill:#e8f5e9

10.4 修复:单一挂载 + 软链,原有路径不变

顺着 10.3 的根因往下推,最直观的修法几乎是自己长出来的:既然病根是"两个独立 bind mount",那就只挂一个 /share:/share,然后把 Radarr 里的路径直接配成挂载下的真实路径 /share/Movies/share/Downloads——这正是 TRaSH Guides 推荐的布局,干净利落,没有任何 trick。

但真要动手时发现这条路被一开始的配置卡死了:这套系统最初就是按 /movies/downloads 两个路径配的,它们早已固化在至少三处:

  1. Root folder:Radarr 里登记的媒体库根目录就是 /movies
  2. 数据库记录:每部电影在 Radarr 数据库里存的完整路径都是 /movies/xxx (年份)/...,几十上百条;
  3. 下游联动:Bazarr 通过 Radarr API 拿到这些 /movies/... 路径再去文件系统找字幕——路径一变,Bazarr 的挂载也得跟着改,否则字幕功能整个坏掉。

也就是说,"直接改配置"意味着一整套有风险的迁移手术:批量迁移 root folder、补配 Remote Path Mapping(qBittorrent 上报的路径是 /downloads/...)、同步改 Bazarr 挂载——任何一处漏改都是静默故障。就为了绕开这个,才用软链 trick 一下:

  • 挂载改成 /share:/share 后,真实路径变成 /share/Movies/...,与旧配置对不上;
  • 那就建个软链 /movies → /share/Movies,让旧地址自动改道到新位置。

一行 ln -s 换掉了整套手术,Radarr 和 Bazarr 的配置一个字都不用动,它们甚至不知道软链的存在。

这里要分清两种"链接",它们完全不是一回事:

  • 软链接(symlink):上面这种,只是容器内目录的别名。radarr 访问 /movies/xxx 时内核先把它翻译成 /share/Movies/xxx 再操作。它不连接任何电影数据,唯一作用是把两个路径引到同一个挂载下,为硬链接铺路;
  • 硬链接(hardlink):真正省空间的那个,由 radarr 之后每部电影导入时自动创建——同一份电影数据同时挂在 Downloads 的种子原名和 Movies 的规范名两个路径下。

一句话:软链是搭桥的,硬链是过桥的。下面脚本里的软链只是把路修通,真正连接电影文件的硬链接不需要手动建。

如果这套系统是从零搭建,根本用不着软链:直接挂 /share:/share,并在 Radarr 里把 root folder 配成 /share/Movies、下载目录配成 /share/Downloads,就什么桥都不需要。软链纯粹是给"已经存在的配置"打的兼容补丁。

做法:

  1. compose 改为单一挂载,radarr 服务的 volumes 从两个 bind 改成:

    1
    2
    3
    4
    
    volumes:
      - /home/pi/docker/radarr/config:/config
      - /home/pi/docker/radarr/config/custom-cont-init.d:/custom-cont-init.d:ro
      - /share:/share
    
  2. 用 linuxserver 镜像的 custom-init 机制建软链/home/pi/docker/radarr/config/custom-cont-init.d/10-hardlink-symlinks.sh(记得 chmod +x):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    #!/usr/bin/with-contenv bash
    # 让 radarr 沿用已配置路径 /movies、/downloads,
    # 但两者软链到单一 /share 挂载下,使导入时可硬链接而非复制。
    make_link() {
      link="$1"; target="$2"
      if [ -L "$link" ]; then ln -sfn "$target" "$link"; return; fi
      if [ -d "$link" ]; then
        if mountpoint -q "$link"; then
          echo "[hardlink-symlinks] $link 是挂载点,保持不变"; return
        fi
        if ! rmdir "$link" 2>/dev/null; then
          echo "[hardlink-symlinks] $link 非空目录,保持不变"; return
        fi
      fi
      ln -s "$target" "$link"
    }
    make_link /movies /share/Movies
    make_link /downloads /share/Downloads
    

    软链解析后,/movies/downloads 实际都落在 /share 这一个挂载点下,link(2) 自然成功。Radarr 的 root folder、下载客户端映射、数据库记录全部不用改。

    这个脚本不是一次性的,必须长期保留:软链建在容器可写层里,容器每次重建(升级镜像、配置变更)都会被丢弃,脚本是每次容器启动时运行、负责把软链重建出来的。它本身幂等,留着没有任何副作用;删掉的话下次重建容器后 /movies/downloads 会变成无效路径,片库直接"消失"。

    小坑中坑:新版 linuxserver 基础镜像(s6 v3)只在容器根/custom-cont-init.d 找自定义脚本,不再读旧文档里的 /config/custom-cont-init.d。所以 compose 里要显式把脚本目录挂到 /custom-cont-init.d,否则日志只会打印 [custom-init] No custom files found, skipping...

  3. 只重建 radarr 容器,qBittorrent 完全不用动:

    1
    
    docker compose up -d radarr
    

    进行中的下载不受任何影响——qBittorrent 没重启,已下载的分块有哈希校验,不存在损坏或重下的问题;radarr 停的一两分钟里,下完的任务在 qBittorrent 里排队等它回来再导入。

验证:容器内 ln /downloads/x /movies/x 成功,且 Radarr API GET /api/v3/config/mediamanagement 返回 copyUsingHardlinks: true

10.5 存量重复文件:字节校验后改硬链接,立省 49G

对于已经重复占空间的存量文件,不用删也不用重下:把 Movies 里的拷贝替换成指向 Downloads 的硬链接即可(Downloads 侧保留为源,qBittorrent 做种完全不受影响)。

但替换前必须先证明"两份文件内容真的相同",否则就是把好数据换成坏数据。证据分两层:

  1. 尺寸粗筛stat -c %s 对比字节数。例如肖申克两边都是 21970935446 字节(21G REMUX),Toy Story 5 两边都是 18907976291 字节——字节数完全相等是同一份文件强有力的信号,但理论上仍可能巧合;
  2. 全文比对实锤cmp -s 逐字节比对两份文件的全部内容(21G 的文件就读 21G × 2),全部一致才动手。

替换本身是原子的,核心逻辑:

1
2
3
cmp -s "$downloads_file" "$movies_file" \
  && ln "$downloads_file" "$movies_file.tmp" \
  && mv "$movies_file.tmp" "$movies_file"

6 部电影全部通过字节级校验并完成替换后,stat 链接数变为 2,磁盘从 91% 降到 69%(空闲 21G → 70G)。之后新下载的电影导入时会自动硬链接,Downloads 做种和 Movies 入库共享同一份数据,不再占双份空间。

后续更新:本节当初判断“风险大于收益”而绕开的迁移手术,后来在第二篇 5.6 节完成了——全链路统一到 /share 物理路径后,软链脚本和远程路径映射已全部拆除。本节作为硬链接问题的排障记录保留;新搭建的系统请直接采用统一路径方案,不必再走软链这条兼容路线。

11. 字幕管线的四个坑:中文站代理、反爬、配额门槛与重复下载(2026-08-05 补记)

上线一个半月后暴露出一组字幕问题:剧集《光环》全 8 集一直没有任何中文字幕,Bazarr 的 Wanted 列表里明明挂着"缺中文"却始终下不到;与此同时,电影《Volver》的同一条中文字幕被反复下载了 28 次。逐个排查后定位到四个互相独立的坑。

11.1 坑一:国内字幕站走了出国代理

症状:zimuku 等中文字幕源全部报错,日志里全是 _ssl.c: The handshake operation timed out

根因:bazarr 容器按第 4.2 节配置了 HTTP_PROXY 走 V2Ray,所有出站流量都被拐到国外。国内字幕站(zimuku、射手、subf2m)本来直连很快,经代理绕一圈后 SSL 握手直接超时。

修法:把国内字幕站域名加进 bazarr 的 NO_PROXY,中文站直连、OpenSubtitles 继续走代理(第 4.2 节的 compose 片段已更新)。

同一个道理的另一个版本:dockerd 拉镜像也不走 shell 里配的代理。树莓派直连 Docker Hub 会超时,需要给守护进程单独配置 /etc/systemd/system/docker.service.d/http-proxy.conf(写入 HTTP_PROXY/HTTPS_PROXY),再 systemctl daemon-reload && systemctl restart docker,否则 docker compose pull 慢到不可用。

11.2 坑二:zimuku 的反爬验证码

zimuku 有 yunsuo 反爬保护,搜索时要求过图形验证码,Bazarr 只能借助付费的 anti-captcha 打码服务来过。没配 key 时每次搜索必然失败:先白等约 30 秒超时,再被 Bazarr 节流 10 分钟。由于字幕源是串行查询的,每一集都要先给 zimuku 陪葬半分钟——这是"搜字幕很慢"的主要原因。

不配打码服务的话,直接在 Settings → Providers 里禁用 zimuku,中文字幕靠射手、subf2m 和 ChineseSubFinder 兜底。

11.3 坑三:OpenSubtitles 的配额与匹配分门槛

OpenSubtitles.com 免费账号每天只能下载约 20 条字幕,配额耗尽后当天剩余搜索全部落空。更隐蔽的是默认的最低匹配分:Bazarr 给每条候选字幕按文件名匹配度打分,剧集默认要求达到满分 360 的 90%(324 分)才下载。《光环》的中文字幕其实一直能在 OpenSubtitles 搜到,但得分只有 312(86.7%),永远被挡在门外——"看得到,下不到"。

修法Settings → Subtitles 把剧集的 minimum score 降到 85。降门槛后《光环》8 集立刻全部命中,而且下到的就是中英双语字幕。

顺带一个 API 坑:Bazarr 1.6 的设置接口(POST /api/system/settings)用 JSON body 提交会返回 204 但什么都不保存,必须用表单字段格式(如 settings-general-minimum_score=85)。在 WebUI 上操作则没有这个问题。

11.4 坑四:重复下载循环——缺失清单与磁盘脱节

《Volver》的中文字幕从 7 月 18 日到 8 月 4 日被重复下载了 28 次:每次都是同一条 OpenSubtitles 字幕、同样的 85% 得分,覆盖写入同一个 .zh.hi.srt 文件。

关键在于:Bazarr 判断"缺不缺字幕"不是实时看磁盘,而是查数据库里的 missing_subtitles 缺失清单,这张清单由索引任务负责刷新(每天 4:00 全量索引,下载后单部增量更新)。Volver 的清单记录卡在了"缺中文"的状态:字幕文件明明早已落盘、命名也正确,清单却没有被纠正。于是每 6 小时的"搜索缺失字幕"任务都把它当成无字幕处理:搜索 → 命中同一条字幕 → 下载覆盖 → 记一条历史,循环往复。这个循环还顺手把 OpenSubtitles 每天的 20 条配额吃得干干净净,让真正缺字幕的《光环》一直排不上队——四个坑就是这样互相放大的。

修法System → Tasks 里手动执行一次 Index All Existing Movies SubtitlesIndex All Existing Episodes Subtitles,全量重算缺失清单。重算后 12 部电影中 11 部状态转为"已满足",重复下载立即停止。

遗留疑问:清单为什么没有被自动刷新(疑似这个版本对 .hi 字幕的索引更新存在缺陷)没有彻底定位,需要观察一段时间。如果日后再出现同一字幕被反复下载,先手动重建索引,再考虑升级 Bazarr 或上报 issue。

11.5 修完后的状态

  • 《光环》S02 全 8 集补上了中英双语字幕(OpenSubtitles.com 来源);
  • 仍缺字幕的只剩真正没有资源的:Rick and Morty 最新几集(中文字幕尚未发布)和老片《Picnic》,由每 6 小时的自动搜索持续盯着;
  • 中文站直连后射手、subf2m 恢复可用,zimuku 保持禁用;
  • 历史下载记录显示,这套系统里所有字幕实际都由 Bazarr(OpenSubtitles)下载,ChineseSubFinder 暂无贡献记录,其去留观察后再定。
本文由作者按照 CC BY 4.0 进行授权