life

中年大叔的第一台DIY的NAS之系统配置

目录

在中年大叔的第一台DIY的NAS中主要介绍了我搭建NAS的相关硬件选择和一些基础的硬件知识,那本文就总结一下这么久下来要想让NAS增加可玩性,需要安装哪些软件和怎样配置。

这里首先再介绍一下TrueNAS的相关知识,因为后续所有的配置和软件都是基于TrueNAS来进行的。

TrueNAS的基础

TrueNAS是一款基于 Debian Linux 的开源网络存储(NAS)和软件定义存储平台,由 iXsystems 开发。它以 OpenZFS 为核心,在传统 NAS 功能之外,还集成了 Linux 容器、虚拟机和应用管理能力,因此既适合作为家庭实验室(Homelab)的存储服务器,也适用于企业级统一存储和超融合基础设施。

在中年大叔的第一台DIY的NAS的NAS操作系统选型的时候已经详细介绍过为什么选择TrueNAS,这里再简单总结一下选择TrueNAS的考虑:

  • 基于Linux的定制化系统,作为我的Homelab再适合不过了。

  • 数据安全性高,基于OpenZFS的TrueNAS,数据安全本身有保障。

  • TrueNAS本身集成了Docker Engine,且社区所有的apps都是通过Docker进行安装管理(当然有些人可能不喜欢,但是这恰恰是我最喜欢的,通过虚拟化保证了系统的稳定性的同时也可以通过Docker来统一管理所有的软件)。

  • TrueNAS能够支持自动扩容,RAIDZ Expansion的功能在这个PR里面合入,且在zfs-2.3.0进行了发布;所以TrueNAS的自动扩容就变得很简单了,直接插盘就可以了。

  • 开源,拥有活跃社区。

TrueNAS 官方的设计目标不是:

给你一个 Debian,然后顺便装了 NAS UI。

而更接近:

给你一个 NAS appliance,底层使用 Linux,同时提供容器化应用平台。

下面简单了解一下TrueNAS的发展时间线,参考官方https://www.truenas.com/freenas/,如下:

其中时间线里的主线是「一次分裂 + 一次合并」,最终的结果是我很喜欢的形态:在OpenZFS的基础上,形成了基于Linux+Docker的方案。

TrueNAS(2025 年统一之后)社区版和企业版共用同一个系统镜像,差别主要在解锁的企业功能、官方硬件和支持服务上。所以我们直接下载社区版的iso安装就好,官方文档中列出了当前的25.10版本的关键组件的版本信息,可以让我们了解相关版本信息,如下:

TrueNAS的安装

https://download.sys.truenas.net 可以进行下载官方社区版的镜像,官方当前可用的TrueNAS版本状态可以这个地址查看:TrueNAS版本状态,我安装的时候还是:TrueNAS-SCALE-25.10.1.iso,目前官方建议的社区稳定版本是:「25.10.6」

下载好ISO的镜像后,可以通过Ventoy进行引导安装,或者直接用烧录工具把ISO系统镜像刻录到U盘进行引导安装。这里如果不是装机爱好者,可以直接选择烧录工具:balenaEtcher,如下:

如下是安装的过程,很简单,就是选择目前磁盘和设置管理员的密码后,等待安装完成后就可以了:

安装成功后,就可以看到TrueNAS终端显示的控制台设置菜单,代表系统已经启动了。

如下,我们可以通过web UI进行登录TrueNAS的控制平面,我们所有的工作基本都可以通过进入webUI来进行管理,因为里面还可以直接登录shell进行系统的维护和管理.

TrueNAS的基础配置

创建存储池

通过web UI进行登录TrueNAS的控制平面后,第一件事就是创建NAS的存储池,创建存储池之前我们要先知道TrueNAS提供的基于OpenZFS的文件冗余系统,这里简单介绍一下什么是OpenZFS,

OpenZFS 是一个开源的"文件系统 + 卷管理器"合体,传统的方案:文件系统(ext4、NTFS)管文件、RAID 卡/LVM 管磁盘,两层分离;ZFS 把这两层合并成一个整体,由它直接管理裸盘。提供了校验和防静默损坏,CoW 防断电写坏,快照防误删,冗余防整盘报废,具体特性如下:

特性 说明
CoW 写时复制 数据从不原地覆写,永远写新块再改指针 → 断电不会写坏一半,不需要 fsck
端到端校验和 每个数据块带校验和,读出来立刻验证;发现静默损坏(bit rot)自动从冗余副本修复
快照 / 克隆 CoW 的副产品:快照即时创建、零空间成本,之后只记录差异
池化存储 没有传统分区概念,容量在池内按需分配给所有数据集
ARC 缓存 内存自适应读缓存(比 LRU 聪明),内存越大性能越好——这就是"TrueNAS 吃内存"的原因,经验值 1TB 存储配 1GB 内存起步
压缩 LZ4 压缩默认推荐开启,几乎无损 CPU,媒体/日志数据常省 20-50% 空间
原生复制 zfs send/receive 块级增量同步到远程,TrueNAS 复制任务和 CloudSync 的底层

下面介绍一下ZFS支持的文件冗余方案:

类型 最少盘数 允许坏盘 容量利用率 随机 IOPS 坏盘重建成本(resilver) 适用场景
Stripe(单盘条带) 1 0 100% ≈ 单盘 无重建可言:没有冗余,坏任意 1 盘 = 整池数据丢失 临时缓存 / 可弃数据
Mirror(镜像,可 N 路) 2 N-1 1/N(2 盘 = 50%) 高(读负载均衡) 低:只从存活盘顺序拷贝,不算校验;10TB 盘约 5~10 小时;重建期负载仅压 1 块盘 虚拟机 / 数据库 / 高 IOPS
RAID-Z1(单校验) 3 1 (N-1)/N 低(≈ 单盘) 高:读遍组内所有盘并重算校验,大池常超 24 小时,全组高负载;期间零冗余,再坏 1 块 = 全丢 小容量冷数据,不推荐大盘
RAID-Z2(双校验) 4 2 (N-2)/N 低(≈ 单盘) 高但可控:同样读遍全组、耗时同 Z1 量级,但期间仍容忍再坏 1 块;大容量盘的可接受底线 家用媒体库甜点选择
RAID-Z3(三校验) 5 3 (N-3)/N 低(≈ 单盘) 高:重建开销与 Z2 相同,期间仍容忍再坏 2 块,最安全;代价是容量利用率最低 超大盘(16TB+)/ 归档
dRAID2(分布式 RAID) 4+ 2(可配 1/2/3) (N-2-S)/N 中低 中:分布式热备并行重建,写入分摊到全组盘,数倍快于 Z2;代价是随机性能略降、布局复杂 大盘阵(20+ 盘)长期服役

这里我选择了RAID-Z1,因为考虑性价比,以及我的NAS目前只有4盘位,给了一个SATA口给了系统盘,只有3盘位了😂。

接下来就是TrueNAS的存储池的概念了,简单说:ZFS 不直接管理磁盘,而是先把若干磁盘捆成一个 VDEV,再把若干 VDEV 拼成一个存储池(Pool)。可以说VDEV是ZFS 存储池的基本组成单元,VDEV层其实就是ZFS系统存储池管理的逻辑单元。针对单VDEV池和多VDEV池的用途差异主要如下:

下面是我创建的存储池,把三块4T的盘组成一个RAIDZ1的存储池,支持7T的容量,冗余1块盘的故障:

镜像代理

TrueNAS的所有App都是通过Docker进行安装和管理的,但是由于国内针对Image Registry的限制,所以我们会发现安装软件的时候都会有如下错误,提示连接超时:

bash
[2026/03/09 23:28:03] (ERROR) app_lifecycle.compose_action():58 - Failed 'up' action for 'jellyfin' app:  jellyfin Pulling \n permissions Pulling \n permissions Error Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)\n jellyfin  Interrupted\nError response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)\n

这就需要NAS主机上安装代理客户端,我这里通过ghcr.io/shadowsocks/sslocal-rust的Image来快速的拉取安装,配置如下:

json
$ cat shadowsocks-rust-config.json 
{
    "servers": [
        {
            "disabled": true,
            "server": "127.0.0.1",
            "server_port": 8388,
            "password": "rwQc8qPXVsR3uW+Y3Lj4Y42yF9Bs0xg1pmx8/+bo=",
            "method": "aes-256-gcm",
            "timeout": 7200
        },
        {
            "server": "42.15.91.41",
            "server_port": 20202,
            "password": "8h67SpFVAa",
            "method": "chacha20-ietf-poly1305"
        },
        {
            "disabled": true,
            "server": "eg.disable.me",
            "server_port": 8390,
            "password": "mGvbWWay8ueP9nV5F1uWGN2BRToiVCAWJmWOTLU24=",
            "method": "chacha20-ietf-poly1305"
        }
    ],
    // ONLY FOR `sslocal`
    // Delete these lines if you are running `ssserver` or `ssmanager`
    "local_port": 1080,
    "local_address": "0.0.0.0"
}

然后启动本地的ss-local的Container:

sh
# cat ss-local.sh 
docker run --name sslocal-rust \
  --restart always \
  -p 1080:1080/tcp \
  -v /data/walkerdu/shadowsocks-rust-config.json:/etc/shadowsocks-rust/config.json \
  -dit ghcr.io/shadowsocks/sslocal-rust:v1.24.0

然后修改Docker daemon的配置如下:

bash
# /etc/docker/daemon.json     
{"data-root": "/mnt/.ix-apps/docker", "exec-opts": ["native.cgroupdriver=cgroupfs"], "iptables": true, "ipv6": true, "default-network-opts": {"bridge": {"com.docker.network.enable_ipv6": "true"}}, "storage-driver": "overlay2", "fixed-cidr-v6": "fdd0::/64", "default-address-pools": [{"base": "172.17.0.0/12", "size": 24}, {"base": "fdd0::/48", "size": 64}], 
"registry-mirrors": [
],"insecure-registries": [
],
"proxies": {
    "http-proxy": "socks5://127.0.0.1:1080",
    "https-proxy": "socks5://127.0.0.1:1080"
  }
}

# 重启docker daemon
$sudo systemctl restart docker

因为daemon的config属于系统的基础配置,是存放在TrueNAS的middlewared中(后面介绍),重启后会被重新渲染为初始化的配置,这里可以通过Init/Shutdown Script在系统启动后重新进行配置,详细看下一节关于middlewared的介绍。

TrueNAS的middlewared

我第一次接触TrueNAS的时候也会很奇怪,我修改了/etc/docker/daemon.json的配置后,为什么在重启后会被回滚呢?下面慢慢看TrueNAS的middlewared的设计思考。

前面在TrueNAS的基础,给了一个TrueNAS的简单概述:

一个 NAS appliance,底层使用 Linux,同时提供容器化应用平台。

它是一个NAS的专用设备,appliance的专用性,让它虽然基座是一个Linux,但是使用起来还是有很特殊的限制,下面这个概述我觉得更好:

以 Debian 为执行底座、以 ZFS 管理系统和数据、以 middlewared 作为控制平面的存储设备操作系统。

作为NAS appliance为了能够达到:一台 NAS 系统盘坏了/升级翻车了,不能丢数据也不能丢配置,而且要能秒级回退。TrueNAS引入了四层的系统设计:

层次 组件 职责
管理层 Web UI、API、midclt 接收用户配置意图
控制层 middlewared(freenas-v1.db) 保存配置、校验约束、编排操作
系统层 Debian、systemd、Docker、SMB、NFS 真正执行服务
存储层 ZFS boot-pool、用户自定义的pool 保存系统和业务数据

安装 TrueNAS 时,安装程序在系统盘上创建:boot-pool,它技术上也是ZFS pool,为什么系统盘也使用 ZFS,核心目的是支持:

  • 原子升级;
  • 快速回滚;
  • 多版本共存;
  • 数据完整性校验;
  • 利用 COW 避免完整复制系统。

所以整体TrueNAS最少有两个ZFS pool:

ZFS pool 保存内容
boot-pool TrueNAS 操作系统、配置数据库、日志、Boot Environment
用户自定义pool 电影、共享目录、Apps 数据等业务数据

这里介绍一下概念Boot Environment,简称 BE,是:

boot-pool 中一套完整、可独立引导的系统 dataset 树。

我安装的系统当前的 BE 是:boot-pool/ROOT/25.10.1,启动时,这些 ZFS dataset 被分别挂载为不同的目录,如下:

bash
$ df -h
Filesystem                                                  Size  Used Avail Use% Mounted on
boot-pool/ROOT/25.10.1                                      446G  105M  446G   1% /
boot-pool/ROOT/25.10.1/audit                                446G   30M  446G   1% /audit
boot-pool/ROOT/25.10.1/conf                                 446G  7.7M  446G   1% /conf
boot-pool/ROOT/25.10.1/data                                 446G  190M  446G   1% /data
boot-pool/ROOT/25.10.1/etc                                  446G  6.5M  446G   1% /etc
boot-pool/ROOT/25.10.1/home                                 447G  617M  446G   1% /home
boot-pool/ROOT/25.10.1/mnt                                  446G  128K  446G   1% /mnt
boot-pool/ROOT/25.10.1/opt                                  446G  4.8M  446G   1% /opt
boot-pool/ROOT/25.10.1/root                                 446G  256K  446G   1% /root
boot-pool/ROOT/25.10.1/usr                                  449G  2.9G  446G   1% /usr
boot-pool/ROOT/25.10.1/var                                  446G  5.4M  446G   1% /var
boot-pool/ROOT/25.10.1/var/ca-certificates                  446G  128K  446G   1% /var/local/ca-certificates
boot-pool/ROOT/25.10.1/var/lib                              446G   29M  446G   1% /var/lib
boot-pool/ROOT/25.10.1/var/lib/incus                        446G  128K  446G   1% /var/lib/incus
boot-pool/ROOT/25.10.1/var/log                              446G   32M  446G   1% /var/log
boot-pool/ROOT/25.10.1/var/log/journal                      446G   50M  446G   1% 
boot-pool/grub                                              446G  9.2M  446G   1% /boot/grub

在 Linux中看到的是普通目录,但底层实际上是多棵独立的 ZFS dataset。为什么不把整个根目录做成一个 dataset,因为不同目录需要不同策略:

dataset 特性
/usr 系统程序,正常设为只读
/opt、/conf 产品静态文件,正常设为只读
/data 动态配置,升级时需要克隆
/home、/root 管理员文件,升级时需要克隆
/audit、/var/log 审计和日志,独立快照、权限和执行策略
/etc 系统配置文件,部分由版本提供、部分由 middleware 管理

独立 dataset 还能分别设置:

text
readonly
exec/noexec
setuid/nosetuid
ACL
atime
mountpoint
snapshot/clone 策略

基于Boot Environment的多系统镜像独立的的引导设计,这就要求/目录中没有特殊的状态数据,所有状态都必须在数据库和数据池里。也是为了能够做到配置要能被"整机搬迁",TrueNAS在boot-pool的基础上设计了以middlewared 作为控制平面的配置管理结构。middlewared的配置渲染在启动阶段有多个checkpoint,当然运行期间如果通过UI/API等事件触发,也会进行渲染,如下:

Checkpoint 大致时机 用途
initial middlewared 加载插件和配置数据库后,系统初始化早期 大多数基础配置
pre_interface_sync 网络接口同步前 主机名、hosts 等网络前置配置
interface_sync 网络接口配置期间 依赖网络状态的配置
pool_import 数据池导入完成后 依赖数据池路径的配置
post_init 系统触发 system.ready 后 最后阶段配置

TrueNAS 25.10.1 的 middlewared 本质是一个用 Python + asyncio + aiohttp 实现的常驻控制平面进程。如下是我本机的 middlewared,丫的,我感觉它有内存泄漏😂。

机器在启动后,引导boot-pool的当前版本的镜像后,Linux内核开始加载启动,然后加载middlewared进行配置的渲染并启动相关服务,总体启动流程就是:

现在我们开始回答本节最开始提到的问题:

基于middlewared的设计,我们知道了本节最开始提到的问题:

修改了/etc/docker/daemon.json的配置后,为什么在重启后会被回滚呢?

那怎么解决这个问题呢?官方没有 UI 入口改 daemon.json 任意字段,TrueNAS给出的通用解法是 Init/Shutdown Script,UI 里配置,持久化在数据库里,不受回滚影响:具体做法如下:

  1. 把自定义配置 daemon.json 放到自定义的数据池,如 /mnt/<pool>/docker/daemon.json
  2. 同时在自定义的数据池中写个脚本 /mnt/<pool>/docker/init.sh:
bash
#!/usr/bin/env bash
systemctl stop docker
cp /mnt/<pool>/docker/daemon.json /etc/docker/daemon.json
systemctl start docker
  1. Web UI → System → Advanced Settings→ Init/Shutdown Scripts → Add:Type=Script,指向该脚本,When=Post Init(注意选 Post Init;Pre Init 太早,middleware 还没写完默认配置,会被后覆盖)

TrueNAS的ZFS命令

前面几节我们已经知道TrueNAS为了保证NAS存储的高可用性和容错性,采用基于OpenZFS开源文件系统作为基座进行磁盘和文件系统进行一体化管理的。

在 Linux中看到的是普通目录,但底层实际上是多个独立的 ZFS dataset。我们可以通过zfs命令来管理这些dataset的一些属性,总的来说,ZFS 管理分两层:

命令 管理对象 例子
zpool 物理磁盘组成的存储池 boot-pool、media-set
zfs 池里面的 dataset、zvol、snapshot media-set/media

zfs命令可以用来管理dataset,如下是dataset的几大属性:

zfs-dataset-properties

在 TrueNAS SCALE 上直接跑 frp 二进制会遇到下面问题:

bash
$ ./frpc 
zsh: permission denied: ./frpc

原因通常是:dataset 默认 noexec,下面通过zfs开启dataset的执行权限:

bash
zfs get exec
zfs set exec=on boot-pool/ROOT/25.10.1/data

但这种方式有问题:TrueNAS 升级可能破坏,所以 最推荐的方法是用 Docker / App 运行 frp,这也是 TrueNAS 官方推荐的模式。

开启挂载目录的exec权限后,就可以启动了:

bash
nohup ./frpc -c frpc.toml 2&> /dev/null &

crontab 本身不仅能按时间执行任务,还支持开机后执行一次的特殊时间表达式:@reboot

bash
@reboot /data/walkerdu/frp_0.68.0_linux_amd64/frpc -c /data/walkerdu/frp_0.68.0_linux_amd64/frpc.toml

TrueNAS的必备软件

在TrueNAS的应用市场,可以通过「Discover Apps」按照Popularity来进行排序,查看大家安装最多的应用,按照自己的需要进行安装,如下:

File Browser

File Brower安装完成后登录需要输出用户名和密码,这个信息只有在首次启动时随机生成,在容器日志里查看,如果重启过或者保留旧数据库重装,不会重新生成密码,也找不回密码,为了找回密码,我查看了容器日志:

bash
$ sudo docker logs 0eca11c35665
2026/04/02 17:09:00 Using config file: /config/settings.json
2026/04/02 17:09:00 Using database: /config/filebrowser.db
2026/04/02 17:09:00 Listening on [::]:30051
2026/04/02 17:09:01 http: TLS handshake error from 192.168.110.101:49513: remote error: tls: unknown certificate
2026/04/02 17:09:01 http: TLS handshake error from 192.168.110.101:49513: remote error: tls: unknown certificate

里面没有密码信息,但是看到配置文件了,发现配置settings.json里面没有,但是filebrowser.db有,但是密码是加密串,无法获取

bash
$ sudo docker exec -it 0eca11c35665 sh 
$ cat config/filebrowser.db
"username":"admin","password":"$2a$10$5dWK0eI5paDu7Bmri0CDOeFOKlDOK0T6vtbVtXrgQK.0qSC.zPsfC"

经过测试发现可以通过:

bash
$ ps aux
PID   USER     TIME  COMMAND
    1 568       0:45 tini -- /init.sh
    7 568       1:54 filebrowser --config=/config/settings.json
    
$rm config/filebrowser.db
$kill 7

然后主进程会自动重新拉起filebrowser,发现配置不存在了,自动重新初始化,打印出对应的账号密码:

bash
2026/04/18 15:28:28 Got signal: terminated
2026/04/18 15:28:28 Stopped serving new connections.
2026/04/18 15:28:28 Graceful shutdown complete.
2026/04/18 15:28:29 Using config file: /config/settings.json
2026/04/18 15:28:29 WARNING: filebrowser.db can't be found. Initialing in /config/
2026/04/18 15:28:29 Using database: /config/filebrowser.db
2026/04/18 15:28:29 Performing quick setup
2026/04/18 15:28:29 User 'admin' initialized with randomly generated password: BQNtO3keAKZzEABQ
2026/04/18 15:28:29 Listening on [::]:30051

qBittorrent

qBittorrent安装后遇到了无法上传的问题:

可以正常下载但是无法上传,整了好久,我一直在想家用网络没有对外IP,难道要我通过VPS的IP进行对外暴露端口吗。后来整理了一下NAT穿透及P2P相关技术入门,最终把qbittorrent的这个UPnP勾选,然后让光猫路由器的UPnP功能开启:

Plex Server

Plex 是一套个人媒体库管理与播放系统,和 Jellyfin 属于同类软件。 把服务端装在 TrueNAS 上,就能把已有的视频文件变成带海报、简介、分季列表的“家庭影视库”。

Plex是一款商业化的流媒体服务软件,有部分功能需要付费开通Plex Pass才能享用:

  • 免费:服务端、基础媒体库管理、同一局域网内播放个人视频。
  • 远程播放个人视频:需要服务器管理员有 Plex Pass,或者观看者有 Plex Pass / Remote Watch Pass。
  • Plex Pass 高级功能:硬件加速转码、离线下载、跳过片头片尾等。

如下,我们安装完Plex Server后登录控制端,会提示我们进行Plex账号的登录,这里是需要联网登录Plex账号,虽然可以选择不能录,但是很多限制,如下:

这里是让我很无语的,我本地的NAS服务为啥还有依赖你的Plex官方登录,后来我才发现Plex服务器的媒体服务默认是通过Plex官方服务器进行中转的,如果不登录,客户端是无法识别到LAN内部的Plex Server的,这里Plex Server的控制端不能关闭:「远程访问」和「网络设置->启用中转」,否者这Plex客户端也看不到NAS的媒体服务器里面创建的资料库。

Plex的奇葩之处就是TV客户端都不提供通过IP地址直连流媒体服务。

当然Plex Server支持DLAN和GDM的流媒体服务的发现,但是这就对家庭的网络有一定的要求,NAT模式下的二级路由网络里面的设备是发现不了一级路由设备后面的NAS流媒体服务器的。

折腾了Plex的网络和账号很久,最终还是放弃了。没办法商业化软件就是限制太多,自由性太差,为了把控Plex的流量入口,不提供直接IP直连Plex服务器的功能,基本上等同于强制用户登录Plex官方账号,虽然有上面介绍的GDM自动发现,但是对网络限制太多了。

所以后面就有了下一节的Jellyfin,如果你还是要使用Plex,那么也还是可以看一下下一节中的「流媒体文件管理」的相关介绍,能更好的帮你管理你的流媒体文件数据。

Jellyfin

在经历过Plex的折磨后,我最终选择了Jellyfin来搭建自己的流媒体服务,一句话是真香。

Jellyfin 是一个开源的媒体服务器系统——把电影、剧集、音乐放在自己的存储上,Jellyfin 负责整理、美化并向各种设备串流播放,相当于自建一个"私人 Netflix"。

前身:2018 年从 Emby 3.5.2 闭源前的代码 fork 而来,此后完全社区驱动开发,无任何付费功能、无遥测、数据全在自己手里(这也是它和 Plex/Emby 最本质的区别——后两者部分功能收费且有云端依赖)。

安装注意事项

Jellyfin服务器

TrueNAS市场可以直接很方便的安装Jellyfin服务器版本,安装前需要注意两点:

  1. 添加Additioanl Storage,挂载NAS上的媒体数据目录到容器中;
  2. 由于众所周知的原因,中国大陆地区已明确无法访问元数据提供者(metadata providers):The Movie Database (TMDb)和TheTVDB,如果需要使用的话需要配置代理,如下通过Additional Environment Variables添加HTTP代理,当然你还得配置一个翻墙的代理。

如果没有代理,可以先尝试使用国内源插件兜底,其中普通使用的就是:**MetaShark(豆瓣源,电影/剧集)**插件,数据源国内直连无障碍,缺点是豆瓣条目信息密度远低于 TMDB,演职员、海报质量都缩水,建议只作为补充源排在后面。

在线安装流程为:控制台 → 插件 → 存储库(Repositories)→ 新建存储库,填入下面的 manifest URL,

text
https://ghfast.top/https://github.com/cxfksword/jellyfin-plugin-metashark/releases/download/manifest/manifest_cn.json

然后回到插件目录里搜索:MetaShark,找到对应插件点安装,装完重启 Jellyfin 生效,装完后:

  1. 控制台 → 插件,确认 MetaShark 状态是 Active;
  2. 控制台 → 媒体库 → 点进每个库,元数据下载器勾选 MetaShark 并拖到第一位;
  3. 大量刮削前务必在插件设置里打开防封禁(豆瓣频控封 IP,一封 6 小时);

具体安装流程如下:

Jellyfin客户端

针对Jellyfin的客户端下载可以在官方地址进行下载:https://jellyfin.org/downloads/,目前我只需要TV客户端,电脑直接WebUI登录控制面板就可以了。

我的电视客户端安装的版本是:jellyfin.androidtv_0.19.10,也可以直接去APKMirror上直接搜索下载对应的版本。下载安装完成后需要做的就是:

  1. 添加服务器,直接输入局域网中你的NAS的Jellyfin服务器的地址;
  2. 通过Quick Connect快速的登录服务器创建的用户;

如下就是配置好后的Jellyfin电视端主界面的效果图:

流媒体文件管理

搭建流媒体服务,我们首先要了解媒体库组织规范,对于没有接触过NAS的人来说,这可能有点陌生,但是必须要了解一下流媒体服务是如何组织和管理媒体库的。

针对流媒体库文件没有跨厂商的「统一标准」,但有一套事实标准——源头是 Kodi(前身 XBMC)当年定下的命名约定,Kodi 的历史地位在于它当年定义了媒体库刮削的游戏规则,后来被 Plex、Emby、Jellyfin 全部沿用并互相兼容,自动化工具(Sonarr/Radarr、TinyMediaManager)的默认输出也对齐这套规则。所以你只要遵循一家(比如 Jellyfin)的官方规范,换软件基本无痛迁移。

Kodi定义的媒体库核心规则就三条:

类型 约定 示例
电影 片名 (年份)/片名 (年份).mkv,每部电影独立文件夹 Movies/Inception (2010)/Inception (2010).mkv
剧集 剧名 (年份)/Season NN/剧名 S01E01.mkv Shows/绝命毒师 (2008)/Season 01/绝命毒师 S01E01.mkv
元数据 同目录放 .nfo(XML 格式,源自 Kodi)+ poster.jpg、fanart.jpg 等 tvshow.nfo、movie.nfo

各家在细节上有自己的扩展,比如 Jellyfin 支持多版本电影(片名 - 1080p.mkv 同目录并存)、Extras 花絮目录类型;Plex 对 Season 01 要求更严格;anime 圈又是另一套(AniDB/BDMV 命名)。但这些都是在主干规则上的增量,互不冲突。

Jellyfin 支持大多数常见视频格式,如 mp4 和 mkv。文件名应尽可能与 metadata provider(元数据提供商)列出的名称一致。但是,某些字符不能使用,因为它们是 Jellyfin 保留的。包含这些字符将导致问题。已知会导致问题的字符如下:<、>、:、"、/、\、|、?、*

如下是Jellyfin的媒体库文件的规范格式:

TV Shows

可以使用 "Shows"(剧集)媒体库类型将电视剧添加到 Jellyfin。

剧集应组织为系列(series)文件夹,然后在每个系列下组织为 Season(季)文件夹。

txt
Shows
├── Series Name A (2010)
│   ├── Season 00
│   │   ├── Some Special.mkv
│   │   ├── Series Name A S00E01.mkv
│   │   └── Series Name A S00E02.mkv
│   ├── Season 01
│   │   ├── Series Name A S01E01-E02.mkv
│   │   ├── Series Name A S01E03.mkv
│   │   └── Series Name A S01E04.mkv
│   └── Season 02
│       ├── Series Name A S02E01.mkv
│       ├── Series Name A S02E02.mkv
│       ├── Series Name A S02E03 Part 1.mkv
│       └── Series Name A S02E03 Part 2.mkv
└── Series Name B (2018)
    ├── Season 01
    |   ├── Series Name B S01E01.mkv
    |   └── Series Name B S01E02.mkv
    └── Season 02
        ├── Series Name B S02E01-E02.mkv
        └── Series Name B S02E03.mkv

每个视频文件可以包含多集(multiple episodes)。但是,它们将显示为一个包含多集元数据(metadata)的单一条目。建议使用 MKVToolNix 之类的工具将视频文件拆分为单独的剧集。

系列文件夹应按以下格式命名:

txt
Series Name (year) [metadata provider id]

year(年份)和 metadata provider id(元数据提供商 ID)字段是可选的,但它们有助于更可靠地识别媒体。

  • 仅含名称的示例:Jellyfin Documentary.mkv
  • 含年份的示例:Jellyfin Documentary (2030)
  • 含元数据提供商 ID 的示例:Jellyfin Documentary [imdbid-tt00000000]
  • 同时含年份和元数据提供商 ID 的示例:Jellyfin Documentary (2030) [imdbid-tt00000000]

Season(季)文件夹应命名为 Season *,其中 * 为任意数字。不要将 Season 缩写为 S01 或 SE01(这个不是强制要求,实际很多都是缩写,也可以正常刮削😂)。为了获得最佳效果,请用 0 在季号前补零,确保每个条目具有相同的位数。例如:Season 5 -> Season 05。另外,不要在 Shows 文件夹中将 Season 文件夹与剧集文件混放。

Movies

可以使用 "Movies"(电影)媒体库(library)类型将电影添加到 Jellyfin 服务器。电影应组织为每部电影一个独立文件夹。该文件夹可以选择性地包含额外文件。

txt
├── Best_Movie_Ever (2019)
│   ├── Best_Movie_Ever (2019).mp4
│   ├── Best_Movie_Ever (2019).nfo
│   ├── Best_Movie_Ever (2019).en_us.srt
│   ├── cover.png
│   └── theme.mp3
└── Movie (2021) [imdbid-tt12801262]
    ├── backdrop.jpg
    └── VIDEO_TS
        ├── VIDEO_TS.BUP
        ├── VIDEO_TS.IFO
        ├── VIDEO_TS.VOB
        ├── VTS_01_0.BUP
        ├── VTS_01_0.IFO
        ├── VTS_01_0.VOB
        ├── VTS_01_1.VOB
        └── VTS_01_2.VOB

包含电影的文件夹应按以下格式命名:

txt
Movie Name (year) [metadata provider id]

year(年份)和 metadata provider id(元数据提供商 ID)字段是可选的,但它们有助于更可靠地识别媒体。

文件夹内的视频文件应与文件夹同名。即如果文件夹名为 Super Fun Movie,其中的视频文件应命名为 Super Fun Movie.mp4(或其他扩展名),并可选择性地附加下文定义的标签。

  • 仅含名称的示例:Jellyfin Documentary.mkv
  • 含年份的示例:Jellyfin Documentary (2030).mkv
  • 含元数据提供商 ID 的示例:Jellyfin Documentary [imdbid-tt00000000].mkv
  • 同时含年份和元数据提供商 ID 的示例:Jellyfin Documentary (2030) [imdbid-tt00000000].mkv
多版本(Multiple Versions)

针对Movies的媒体库类型,Jellyfin支持多版本(Multiple Versions),通过文件名后缀在同一电影文件夹中存储同一视频的多个版本。每个文件在添加版本标签之前,必须以父文件夹名称开头——包括任何年份和/或元数据提供商 ID。该前缀必须逐字符匹配;否则,这些文件将被视为不同的电影。

txt
Movie (2021) [imdbid-tt12801262]
├── Movie (2021) [imdbid-tt12801262] - 2160p.mp4
├── Movie (2021) [imdbid-tt12801262] - 1080p.mp4
└── Movie (2021) [imdbid-tt12801262] - Directors Cut.mp4

为了区分版本,每个文件名需要依次包含一个,空格、连字符、空格,然后是标签。标签没有预定义,可以由用户自行拟定。,连字符是必需的。不支持点号、逗号等其他字符。格式为:

text
movie directory name - label.mp4

标签也可以选择性地放在方括号中,效果相同,如下所示。

txt
└── Best_Movie_Ever (2019)
    ├── Best_Movie_Ever (2019) - [1080P].mp4
    ├── Best_Movie_Ever (2019) - [720P].mp4
    └── Best_Movie_Ever (2019) - [Directors Cut].mp4

如果不像上面那样在文件名末尾添加标签,每个文件将被视为一部独立的电影,而不是同一电影的一个版本。

电影版本按字母顺序排序显示。分辨率名称例外,它们按分辨率从高到低降序排列。以 p 或 i 结尾的版本名被视为分辨率名称。列表中的第一个电影版本是默认选中的版本。排序示例如下:

  • 分辨率排序:1080p、2160p、360p、480p、720p → 2160p、1080p、720p、480p、360p
  • 命名版本排序:Extended Cut、Cinematic Cut、Director's Cut → Cinematic Cut、Director's Cut、Extended Cut

要手动合并媒体,长按或右键点击媒体以高亮显示,然后选择要合并的其他媒体。使用新出现的操作栏中的 "Group Versions"(合并版本)。

Music

专辑(Albums)以文件夹形式组织,一个文件夹包含且仅包含一张专辑。Jellyfin 不关心你如何组织专辑集合,只要每张专辑都包含在一个文件夹内即可。文件名通常无关紧要,因为信息会从音轨内嵌的元数据(metadata)中抓取。如果没有找到其他元数据,Jellyfin 会使用文件名作为音轨标题。

txt
├── Some Artist
│   ├── Album A
│   │   ├── Song 1.flac
│   │   ├── Song 2.flac
│   │   └── Song 3.flac
│   └── Album B
│       ├── Track 1.m4a
│       ├── Track 2.m4a
│       └── Track 3.m4a
└── Album X
    ├── Whatever You.mp3
    ├── Like To.mp3
    ├── Name Your.mp3
    └── Music Files.mp3

虽然 Jellyfin 一般不使用文件名进行识别,但包含特殊字符的文件名仍可能导致问题。已知会导致问题的字符如下:<、>、:、"、/、\、|、?、*

光盘(Discs)

包含多张光盘(disc)的专辑通过元数据标签中的 disc number(光盘号)和 total discs(光盘总数)字段来识别。请将所有光盘的音轨放在一个文件夹中。也可以选择将它们分隔到光盘文件夹中,但内嵌元数据优先。

txt
├── Album 1
│   ├── Disc 1 Track 1.ogg
│   ├── Disc 1 Track 2.ogg
│   ├── Disc 2 Track 1.ogg
│   ├── Disc 3 Track 1.ogg
│   ├── Disc 3 Track 2.ogg
│   └── Disc 3 Track 3.ogg
└── Album 2
    ├── Disc 1
    │   ├── Disc 1 Track 1.aiff
    │   └── Disc 1 Track 2.aiff
    ├── Disc 2
    │   ├── Disc 2 Track 1.aiff
    │   ├── Disc 2 Track 2.aiff
    │   └── Disc 2 Track 3.aiff
    └── Disc 3
        └── Disc 3 Track 1.aiff
歌词(Lyrics)

歌词文件必须与对应曲目位于同一文件夹中,并且文件名与对应曲目一致。例如:01 Death Eternal.mp3 的歌词文件必须是 01 Death Eternal.lrc、01 Death Eternal.elrc 或 01 Death Eternal.txt。

txt
└── Some Artist
    └── Album A
        ├── Song 1.flac
        ├── Song 1.lrc
        ├── Song 2.flac
        ├── Song 2.lrc
        ├── Song 3.flac
        └── Song 3.lrc

在 Jellyfin 的界面中可以对歌词进行跳转,即用户可以点击任意一行直接跳到该行在歌曲中出现的时间点。歌词文件可以是同步的(synchronised),也可以是不同步的(unsynchronised)。它可以包含一些额外的元数据,但这些元数据不会显示在 Jellyfin 客户端中。

  • 同步歌词是交互式的,用户可以点击任意一行直接跳转到歌曲中对应的时间点。你可以选择手动同步文本(这可能耗时且可能不够精确),或使用 MiniLyrics 等歌词同步软件。同步歌词文件大致如下:
txt
[ar: Some Artist]
[ti: Song 1]
[al: Album 1]
[by: Author]
[length: 2:57]
[00:10.89]Line 1
[00:14.58]Line 2
[00:16.78]Line 3
[00:21.03]Line 4
[00:24.86]Line 5
(...)
  • 不同步的歌词更容易实现,但用户跟唱会更困难。此类文件大致如下:
txt
Line 1
Line 2
Line 3
Line 4
Line 5
(...)

多部分(Multiple Parts)

针对Movies和Shows的流媒体文件,如果命名正确,被拆分为多个文件的内容可以堆叠(stacked)在一起。文件应按如下方式命名:

txt
Series Name A (2025)
└──Season 1
    ├── Series Name A (2025) S01E01-part-1.mkv
    └── Series Name A (2025) S01E01-part-2.mkv

<parttype>(部分类型)和 <partnumber>(部分编号)之间的分隔符是可选的。<partnumber> 可以是任意数字,或字母 a-d。

支持的部分类型:cd、dvd、part、pt、disc、disk。

支持的分隔符: (空格)、.(点)、-(连字符)、_(下划线)。

外部字幕和音轨(External Subtitles and Audio Tracks)

针对Movies和Shows的流媒体类型,可以通过文件后缀添加外部字幕和音轨。

txt
Movies
└── Film (1986)
    ├── Film.mkv
    ├── Film.default.srt
    ├── Film.default.en.forced.ass
    ├── Film.forced.en.dts
    ├── Film.en.sdh.srt
    └── Film.English Commentary.en.mp3
Shows
└── Series Name A (2021)
    └── Season 1
        ├── Series Name A (2021) S01E01 Title.avi
        ├── Series Name A (2021) S01E01 Title.ja.ass
        └── Series Name A (2021) S01E01 Title.commentary.ja.aac

每个 title/flag(标题/标志)字段可以是通用字符串,也可以是特殊标志。一个文件可以有多个标志,用 . 分隔。

类型 标志
Default(默认) default
Forced(强制字幕) forced、foreign
Hearing Impaired(听障辅助) sdh、cc、hi

hi 与印地语(Hindi)的语言缩写冲突。单独的 hi 会被解析为印地语音轨,而 hi 与另一个语言标识符一起使用时(如 title.en.hi.srt)会使用另一种语言并将其标记为听障辅助。

对于包含多个流(stream)的容器,标志会被忽略。

任何无法解析为语言或标志的任意文本都会被合并并作为流的标题使用(如果文件元数据中没有内嵌流标题的话)。上面示例中的最后一个文件将被解析为英语 mp3 音频流,标题为 English Commentary。

元数据提供商(Metadata providers)

对于大多数类型的内容,Jellyfin 会自动从外部元数据提供商获取媒体信息。为了提高媒体识别的准确性,你可以为每部电影或剧集手动指定一个 metadata provider identifier(元数据提供者标识符)。每个 metadata provider(元数据提供者)都为其内容使用唯一标识符,添加这些标识符可以极大地改善媒体识别效果。标识符可以写在你的电影/剧集的文件名或文件夹名中。可以指定多个标识符。例如:

txt
Movies
├── Best_Movie_Ever (1994) [tmdbid-680] [imdbid-1234]
│   ├── Best_Movie_Ever (1994) [tmdbid-680].mp4
└── Movie (2021) [imdbid-tt12801262]
    └── Movie (2021) [imdbid-tt12801262].mp4

Shows
└── Series Name (2018) [tvdbid-79168]
    ├── Season 01
    |   ├── Series Name S01E01.mkv
    |   └── Series Name S01E02.mkv
    └── Season 02
        ├── Series Name S02E01-E02.mkv
        └── Series Name S02E03.mkv

支持以下 metadata providers(元数据提供者):

正如前面安装注意事项给出了国内自有国情,国外的元数据提供商都无法提供服务,Jellyfin官网给出了一个Notice,如下:

Notice for Users in Mainland China 中国大陆地区用户请注意

Because of external factors, certain metadata providers may not be accessible in mainland China. 由于外部因素,部分元数据提供者在中国大陆地区可能无法访问。

Below is a list of known inaccessible providers: 下方为已知无法访问的提供者:

  • The Movie Database (TMDb)
  • TheTVDB

元数据图像(Metadata Images)

图像既可以作为外部文件放在媒体文件夹中,也可以内嵌在媒体文件本身中。提供外部图像时,应将它们与媒体文件并排放置。一旦提供,它们将优先于其他来源。

与媒体文件夹类似,艺术家(artist)图像可以放在艺术家文件夹的根目录中。它将在浏览艺术家时和艺术家详情页面上显示。

如下是在TV Shows中使用Metadata Images的示例,同理在Movies中也是一样:

txt
Series Name B (2018)
├── cover.jpg
├── thumbnail.jpg
└── Season 01
    ├── backdrop.webp
    ├── cover.jpg
    ├── logo.png
    ├── Series Name B S01E01.mkv
    ├── Series Name B S01E01-thumb.jpg
    ├── Series Name B S01E02.mkv
    └── Series Name B S01E02-thumb.jpg

Metadata Images支持的图像类型有多种,主要区别就是用于媒体介绍的不同位置,如下是Jellyfin支持的图像类型有以下几种:

类型 说明
Primary 主要封面/艺术家图像
Backdrop 媒体页面中的背景图像,可以使用多张 Backdrop 图像随时间轮播。只需在文件名末尾直接追加数字或在连字符后追加数字,例如 backdrop-1.jpg、backdrop2.jpg。
Banner 以横幅模式浏览媒体库时显示。仅限视频。
Logo 显示在媒体条目顶部的 Logo
Thumb 主页缩略图及以缩略图模式浏览媒体库时使用。仅限视频。

下面是各个图像类型对应的图像文件名称,以及支持的媒体类型的列表,除非特殊说明,所有的图像文件名称都可以是独立的名字 (e.g. logo.png),或者是以改名字为后缀的格式 (e.g. movie-logo.png)

Filename Type Movies Series Season Episode Music Artist
poster Primary ✅ 2 ✅ ✅ ✅
folder Primary ✅ 2 ✅ ✅ ✅ ✅
cover Primary ✅ 2 ✅ ✅ ✅
default Primary ✅ 2 ✅ ✅ ✅
movie Primary ✅ 2
show Primary ✅
jacket Primary ✅
thumb (suffix) Primary ✅
backdrop Backdrop ✅ 2 ✅ ✅ ✅
fanart Backdrop ✅ 2 ✅ ✅ ✅
background Backdrop ✅ 2 ✅ ✅ ✅
art Backdrop ✅ 2 ✅ ✅ ✅
extrafanart (folder) Backdrop ✅ 2 ✅ ✅ ✅
banner Banner ✅ ✅ ✅ ✅
logo Logo ✅ 2 ✅ ✅ ✅
clearlogo Logo ✅ 2 ✅ ✅ ✅
landscape Thumb ✅ ✅ ✅ ✅
thumb Thumb ✅ ✅ ✅ ✅

上面表格中标注「2」的表明:这些媒体文件名字可以被嵌入到支持多媒体Container的媒体格式中,例如mkv。

下面是一张展示 Jellyfin 中 3 种主要图像类型的截图:

Album Image View

针对各种图像该选用什么样的比例和尺寸,官方文档并没有,但是在GitHub Issue中有相关说明:

Type Ratio Dimensions
Poster 1:1.5 Usually 1000×1500
Thumb 16:9 Usually 1000×562
Background 16:9 Usually 1920×1080
Logo 80:31 Usually 800×310

本地 .nfo 元数据

Jellyfin 可以读取和写入本地 .nfo 元数据文件,以便从其他程序和工具导入元数据,或将元数据导出到其他程序和工具。

为了让 Jellyfin 能找到 .nfo 文件,你必须正确命名它们。下表将帮助你进行命名。

媒体类型(Media Type) 文件名(Filename)
电影(Movies) movie.nfo、VIDEO_TS.nfo,或对于 DVD 使用 <电影的文件名>.nfo
电视节目(TV Shows) tvshow.nfo
电视季(TV Season) season.nfo
剧集单集(Episode) <单集的文件名>.nfo
音乐艺术家(Music Artist) artist.nfo
音乐专辑(Music Album) album.nfo
音乐视频(Music Videos) 参见"电影"部分

注意:目前无法禁用 .nfo 元数据。本地元数据始终会被读取,并且其优先级高于 TMDb 等远程元数据提供者(remote metadata providers)

关于 .nfo更详细的结构定义可以参考: Kodi wiki.

配置流媒体服务

有了前面的流媒体文件相关的知识储备后,我们就可以更好的配置我们自己的流媒体服务。在安装完Jellyfin后,登录Jellyfin的控制端后,会有提示向导创建自己的媒体服务器设置管理员的用户名和密码,如下,创建完成后就可以创建媒体库啦:

在「控制台」->「媒体库」->「添加媒体库」就可以添加我们的媒体库了:

如下会弹出添加媒体库的指引,我们按需添加我们的媒体库内容的类型就可以了,如下:

前面我们已经介绍了Jellyfin的流媒体数据的类型分类,这里有些第一次使用的人可能还是区分不清楚自己该如何选择,这里我针对我们可能最常用的三种媒体数据类型,再简单的总结一下:

  • 电影:针对电影的媒体数据,每个电影要放到同名(电影名字title相同,不考虑flag的标记)的文件夹中,要添加的电影媒体库的文件夹要是这些电影目录的父目录,结构如下:我们添加媒体文件夹的时候要选择MyMovies1这个文件夹才可以。
bash
MyMovies1
└── FilmA (1986)
│   ├── FilmA.mkv
│   ├── FilmA.default.srt
└── FilmV (1987)
    ├── FilmV.mkv
    ├── FilmV.default.srt
  • 节目:针对系列剧集的媒体数据,也就是常规的一个节目有很多Season,每一个季有很多集。同样添加节目媒体库文件夹要是这些系列目录的父目录才可以,如下,我们添加媒体文件夹的时候要选择MyShows2这个文件夹。
bash
MyShows2
├── Series Name A (2010)
│   ├── Season 00
│   │   ├── Series Name A S00E01.mkv
│   │   └── Series Name A S00E02.mkv
│   ├── Season 01
│   │   ├── Series Name A S01E01-E02.mkv
│   └── Season 02
│       ├── Series Name A S02E01 Part 1.mkv
│       └── Series Name A S02E01 Part 2.mkv
└── Series Name B (2018)
    ├── Season 01
    |   ├── Series Name B S01E01.mkv
  • 家庭视频和照片:这主要是针对自己的视频和照片进行管理的媒体类型,主要的特点就是:没有媒体元数据。这种媒体库类型是不会进行元数据刮削的,因为这些都是自己的视频,在Metadata providers也不可能存在相关的元数据介绍,所以这里都只是简单的以目录进行媒体数据的管理,当然你可以自己配置的元数据图像,以便更好的展示。
  • 混合电影和电视节目:这一类目前Jellyfin官方已经不推荐再使用了。

Jellyfin效果展示

如下是我的Jellyfin配置好后主页展示:

打开我们的english-cartoons的节目效果如下:

打开Numberblocks的系列后的效果如下:

再来一个有毒的😂,如下,我给娃搭建了英语课本视频的系列节目,封面什么的通过前面的知识你就可以知道该如何配置了,这里要配置媒体类型成「节目」或者「混合电影和电视节目」,不能配置成「家庭视频和照片」,因为这种媒体类型只是简单的视频管理,不能很好的支持视频的下一集播放,它们里面的视频是没有关联性的。

另外这里需要注意:这种自定义的「节目」不要配置元数据刮削,在配置里面取消所有的元数据提供商就可以了。

Jellyfin进阶配置

我们可以通过给不同的客户端创建不同的用户,以更加细粒度的控制流媒体服务,例如如下是我通过创建新用户+节目系列打标签来控制家里电视客户端只能看节目中的某些系列,如下:

  • 创建home-tv用户,这里可以直接控制该用户可以查看哪些媒体库;
  • 设置用户的「家长控制」->「通过标签锁定内容」,增加level-2标签,过滤掉展示不要对该用户展示的系列;
  • 找到对应的媒体节目或者电影,通过「编辑元数据」->「标签」,添加level-2标签,然后home-tv的用户就无法看到对应的节目或者电影啦。

如下图所示:

评论