把下载的视频变成私人 Netflix——Jellyfin 家庭媒体库搭建全攻略
你用下载器攒了几百个视频:技术教程、纪录片、电影、自己喜欢的 up 主合集。但它们散落在各个文件夹里,想找个视频得翻半天,想在手机上看得先拷过去,想跟家人共享更是无从下手。其实你缺的不是视频,而是一个媒体服务器。Jellyfin 是开源免费的 Emby/Plex 替代品,装好之后你的所有视频会自动生成海报墙、自动按类型/年份/演员分类、支持任意设备播放(手机/平板/电视/浏览器)、还支持外网访问和硬件转码——完完全全的"私人 Netflix"体验。这篇文章从零带你搭建,包括 Docker 部署、元数据刮削、硬件加速转码、外网访问和自动入库。
TL;DR:Jellyfin = 开源媒体服务器。Docker 一行命令部署,视频放到指定目录后自动刮削海报和元数据(从 TMDB/豆瓣),支持硬件转码(Intel QSV/NVIDIA NVENC)实现实时转码播放,通过 DDNS + 反向代理实现外网访问。配合 yt-dlp 下载视频后自动放到 Jellyfin 媒体目录,整个流程全自动化。
目录
- 一、为什么选 Jellyfin 而不是 Plex/Emby
- 二、Docker 部署:一行命令跑起来
- 三、元数据刮削:让视频自动"有封面有简介"
- 四、硬件加速转码:为什么需要以及怎么配
- 五、外网访问:随时随地看你的视频库
- 六、自动入库:下载完自动归档到 Jellyfin
- 七、多用户与权限管理
- 八、常见问题排查
- 九、合规与温馨提示
一、为什么选 Jellyfin 而不是 Plex/Emby
市面上三个主流的家庭媒体服务器:
| 特性 | Jellyfin | Plex | Emby |
|---|---|---|---|
| 开源/免费 | ✅ 完全开源免费 | ❌ 高级功能收费 | ❌ 高级功能收费 |
| 硬件转码 | ✅ 免费 | ❌ 需 Plex Pass($120/永久) | ❌ 需 Emby Premiere($4.99/月) |
| 离线播放 | ✅ 免费 | ❌ 需 Plex Pass | ❌ 需 Emby Premiere |
| 数据隐私 | ✅ 完全本地 | ❌ 需通过 Plex 服务器认证 | ❌ 需通过 Emby 服务器认证 |
| 插件生态 | 中等 | 丰富 | 中等 |
| 客户端 | Web/Android/iOS/TV/KDl | Web/Android/iOS/TV/游戏机 | Web/Android/iOS/TV |
一句话总结:Jellyfin 是唯一一个"不花一分钱就能用全部功能"的选择。 Plex 和 Emby 的核心功能(硬件转码、离线下载)都需要付费订阅。而且 Jellyfin 完全本地运行,不需要把数据经过第三方服务器——这对隐私敏感的国内用户来说是刚需。
Jellyfin 是 Emby 的一个开源分支(2018 年 Emby 闭源后社区 fork 出来的),所以很多 Emby 的插件和教程对 Jellyfin 也适用。
二、Docker 部署:一行命令跑起来
2.1 准备工作
# 服务器要求:Linux(Ubuntu/Debian/CentOS),有 Docker
# 最低配置:1 核 CPU + 1GB 内存(纯直接播放)
# 推荐配置:4 核 CPU + 4GB 内存(带硬件转码)
# 创建目录结构
mkdir -p ~/jellyfin/{config,cache,media}
mkdir -p ~/jellyfin/media/{movies,tvshows,downloads}
2.2 Docker Compose 部署
# docker-compose.yml
version: '3.8'
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
ports:
- "8096:8096" # HTTP
- "8920:8920" # HTTPS(可选)
volumes:
- ./config:/config
- ./cache:/cache
- ./media:/media:ro # 只读挂载媒体目录
environment:
- TZ=Asia/Shanghai
- JELLYFIN_PublishedServerUrl=https://your-domain.com # 外网地址
devices:
# 硬件加速(Intel QSV)
- /dev/dri:/dev/dri
group_add:
- "44" # video 组 ID(ubuntu),用于访问 /dev/dri
- "105" # render 组 ID
# 启动
docker compose up -d
# 查看日志
docker compose logs -f jellyfin
启动后访问 http://你的服务器IP:8096,按照引导设置语言(中文)、创建管理员账户、添加媒体库。
2.3 媒体库类型选择
Jellyfin 支持多种媒体库类型,每种对应不同的元数据刮削策略:
| 类型 | 用途 | 目录结构要求 | 刮削源 |
|---|---|---|---|
| 电影 | 电影收藏 | 每个电影一个文件夹 | TMDB/豆瓣 |
| 电视剧 | 剧集/系列视频 | 剧名/Season 01/EP01.mp4 | TVDB/TMDB |
| 音乐 | 音乐收藏 | 歌手/专辑/曲目.flac | MusicBrainz |
| 混合内容 | 不按分类的视频 | 无要求 | 无(使用文件名) |
| 合集 | 系列电影打包 | 无要求 | TMDB |
| 家庭视频 | 自己拍的视频 | 无要求 | 无 |
对于下载的各种视频(教程、up 主合集、纪录片),推荐使用混合内容类型——Jellyfin 会用文件名作为标题,不会尝试刮削元数据(因为大部分网络视频在 TMDB 上没有条目)。
2.4 推荐的文件命名规范
Jellyfin 对文件名有要求,命名不规范会导致刮削失败:
电影:
/media/movies/盗梦空间 (2010)/盗梦空间 (2010).mp4
/media/movies/Inception (2010)/Inception (2010) [1080P][H.264].mp4
电视剧:
/media/tvshows/权力的游戏/Season 01/Game of Thrones S01E01.mp4
/media/tvshows/权力的游戏/Season 01/权力的游戏 S01E01 凛冬将至.mp4
混合内容(教程/纪录片/短视频):
/media/downloads/Fireship/Fireship - Docker in 100 Seconds [1080P].mp4
/media/downloads/阮一峰/阮一峰 - ES6 入门教程 01 [720P].mp4
三、元数据刮削:让视频自动"有封面有简介"
3.1 内置刮削器配置
在 Jellyfin 控制台 → 媒体库 → 管理媒体库 → 勾选需要的元数据下载器:
推荐配置(国内用户):
✅ TheMovieDb(电影/电视剧海报和简介)
✅ The Open Movie Database(补充信息)
✅ Screen Grabber(从视频中自动截图做封面)
不推荐(国内访问不稳定):
❌ TheTVDB(经常被墙)
❌ FanArt(很慢)
3.2 安装豆瓣刮削插件
TMDB 对中文内容覆盖不全,很多国产电影、综艺、动漫没有条目。安装豆瓣插件可以大幅提升中文内容的刮削成功率:
# 进入 Jellyfin 插件目录
cd ~/jellyfin/config/plugins
# 下载豆瓣插件(以 MetaTube 为例)
# 仓库地址:https://github.com/cxfksword/jellyfin-plugin-metashare
wget https://github.com/cxfksword/jellyfin-plugin-metashare/releases/download/v1.6.5/MetaTube.dll
# 放到插件目录后重启 Jellyfin
docker restart jellyfin
重启后在 Jellyfin 控制台 → 插件 → 目录 → 找到 MetaTube → 点击安装 → 重启。
配置 MetaTube:设置 → 插件 → MetaTube → 选择数据源(豆瓣/Douban)→ 保存。
3.3 手动修复元数据
刮削器不是万能的。对于刮削失败的视频:
方法 1:右键视频 → 识别 → 手动输入 IMDB ID 或 TMDB ID
例如:Inception 的 IMDB ID 是 tt1375666
方法 2:右键视频 → 编辑元数据 → 手动填写标题/简介/年份/海报
方法 3:在视频文件夹中放一张 poster.jpg 作为封面
/media/movies/某电影/poster.jpg
3.4 用 NFO 文件预设元数据
对于刮削器无法识别的视频(比如下载的 B 站教程),可以提前生成 NFO 文件:
<!-- 某视频目录/tvshow.nfo -->
<?xml version="1.0" encoding="utf-8"?>
<tvshow>
<title>ES6 入门教程</title>
<plot>阮一峰老师的 ES6 入门系列教程</plot>
<genre>教程</genre>
<premiered>2024-01-01</premiered>
<studio>阮一峰</studio>
</tvshow>
四、硬件加速转码:为什么需要以及怎么配
4.1 什么情况下需要转码
直接播放(Direct Play):客户端支持视频的编码格式 → 服务器直接发送原始文件 → 几乎不消耗 CPU。
转码(Transcoding):客户端不支持编码格式 / 带宽不足需要降码率 / 字幕需要烧录到画面 → 服务器实时转换格式 → 消耗 CPU 或 GPU。
常见需要转码的场景:
- 用手机浏览器看 H.265 编码的视频(Safari 以外大多数不支持 H.265)
- 外网带宽不够,需要把 4K 转成 1080P 传输
- 视频有 ASS 特效字幕,客户端不支持,需要烧录到画面
- 音频是 DTS/Dolby Atmos,客户端只支持 AAC
4.2 硬件加速方案对比
| 方案 | 适用硬件 | 转码能力 | 功耗 | 成本 |
|---|---|---|---|---|
| Intel QSV | Intel 核显(HD 610+) | 4-8 路 1080P 同时转码 | 极低 | 免费(CPU 自带) |
| NVIDIA NVENC | NVIDIA GTX 1050+ | 3-8 路(有 session 限制) | 中 | 需要独显 |
| VA-API | AMD/Intel Linux 驱动 | 3-5 路 | 低 | 免费 |
| Apple VideoToolbox | Mac | 3-5 路 | 低 | Mac 自带 |
推荐方案:Intel 核显 QSV。几乎所有 Intel CPU(第 7 代酷睿及以上)都自带,不需要额外硬件,转码效果也很好。
4.3 配置 Intel QSV 硬件加速
# docker-compose.yml 中已包含的配置
devices:
- /dev/dri:/dev/dri
group_add:
- "44" # video 组
- "105" # render 组
# 确认 /dev/dri 权限
ls -la /dev/dri
# 输出类似:crw-rw---- 1 root video 226, 0 Jan 1 00:00 renderD128
然后在 Jellyfin 控制台 → 播放 → 硬件加速:
硬件加速:Intel QuickSync (QSV)
启用硬件解码:✅ 全部勾选
✅ H.264
✅ H.265/HEVC
✅ VP9
✅ AV1(12 代酷睿及以上支持)
硬件编码选项:
✅ 启用硬件编码
✅ 允许 HEVC 编码
4.4 验证硬件加速是否生效
播放一个需要转码的视频,在 Jellyfin 控制台 → 仪表盘 → 查看当前播放信息:
播放方式:转码 ← 说明在转码
原因:视频编码不支持 ← 转码原因
如果是硬件加速转码,日志中会出现:
[hevc_qsv @ ...] ← QSV 硬件加速
也可以用命令行验证:
# 进入容器
docker exec -it jellyfin bash
# 检查 QSV 是否可用
/usr/lib/jellyfin-ffmpeg/ffmpeg -v debug -init_hw_device qsv=hw
# 输出包含 "qsv device initialized" 说明正常
五、外网访问:随时随地看你的视频库
5.1 方案一:DDNS + 端口转发
前提:有公网 IP(向运营商申请)
1. 路由器上设置端口转发:外网 8096 → 内网 192.168.1.x:8096
2. 配置 DDNS(动态域名):
- 阿里云 DDNS:在路由器上设置,或跑一个脚本定时更新
- 免费方案:duckdns.org
3. 访问:http://your-domain.duckdns.org:8096
5.2 方案二:Cloudflare Tunnel(不需要公网 IP)
没有公网 IP 时的最佳方案:
# 在 docker-compose.yml 中添加
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run --token YOUR_TUNNEL_TOKEN
在 Cloudflare Zero Trust 面板创建 Tunnel,绑定域名,指向 http://jellyfin:8096。
注意:Cloudflare Tunnel 免费版有 100MB 上传限制,看视频可能被限速。看高清视频建议用有公网 IP 的方案。
5.3 方案三:frp 内网穿透
# frpc.ini(客户端,跑在 Jellyfin 服务器上)
[jellyfin]
type = tcp
local_ip = 127.0.0.1
local_port = 8096
remote_port = 8096
需要一个有公网 IP 的 VPS 做 frp 服务端。相比 Cloudflare Tunnel 没有流量限制,但需要额外一台服务器。
5.4 Nginx 反向代理(配合 HTTPS)
# /etc/nginx/sites-available/jellyfin
server {
listen 443 ssl http2;
server_name media.your-domain.com;
ssl_certificate /etc/ssl/certs/your-domain.pem;
ssl_certificate_key /etc/ssl/private/your-domain.key;
# Jellyfin 要求较大的请求体(用于上传)
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:8096;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持(Jellyfin 需要)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
六、自动入库:下载完自动归档到 Jellyfin
6.1 下载完成后的自动处理脚本
#!/usr/bin/env python3
"""
下载完成后的自动处理:移动到 Jellyfin 媒体目录并通知刷新
"""
import os
import shutil
import requests
from pathlib import Path
JELLYFIN_API_KEY = 'your_api_key_here'
JELLYFIN_URL = 'http://localhost:8096'
MEDIA_ROOT = '/media'
def move_to_jellyfin(filepath, source='Unknown'):
"""将下载完成的视频移动到 Jellyfin 媒体目录"""
filepath = Path(filepath)
source_dir = MEDIA_ROOT / 'downloads' / source
# 按上传者分文件夹
uploader = extract_uploader(filepath)
if uploader:
target_dir = source_dir / uploader
else:
target_dir = source_dir / 'Uncategorized'
target_dir.mkdir(parents=True, exist_ok=True)
target = target_dir / filepath.name
# 移动(同盘快,跨盘慢)
shutil.move(str(filepath), str(target))
print(f"归档: {filepath.name} → {target}")
return target
def notify_jellyfin_refresh():
"""通知 Jellyfin 刷新媒体库"""
headers = {
'X-MediaBrowser-Token': JELLYFIN_API_KEY,
}
# 获取所有媒体库
resp = requests.get(
f'{JELLYFIN_URL}/Library/VirtualFolders',
headers=headers
)
libraries = resp.json()
# 触发每个库的扫描
for lib in libraries:
lib_id = lib['ItemId']
requests.post(
f'{JELLYFIN_URL}/Library/Refresh',
headers=headers,
json={'ItemIds': [lib_id]}
)
print(f"已触发媒体库刷新: {lib['Name']}")
return True
def extract_uploader(filepath):
"""从文件名提取上传者"""
name = filepath.stem
if ' - ' in name:
return name.split(' - ')[0]
return None
# yt-dlp 下载完成后自动调用
# yt-dlp --exec "python jellyfin_auto.py %(filepath)s" URL
if __name__ == '__main__':
import sys
if len(sys.argv) > 1:
downloaded_file = sys.argv[1]
source = sys.argv[2] if len(sys.argv) > 2 else 'YouTube'
try:
new_path = move_to_jellyfin(downloaded_file, source)
notify_jellyfin_refresh()
print("自动入库完成")
except Exception as e:
print(f"自动入库失败: {e}")
6.2 配合 yt-dlp 使用
# 下载完成后自动执行脚本
yt-dlp \
--exec "python /scripts/jellyfin_auto.py %(filepath)s YouTube" \
"https://www.youtube.com/watch?v=xxx"
6.3 定时扫描目录
如果不方便在下载时触发,可以写一个定时任务:
# 用 crontab 每 30 分钟扫描一次
# */30 * * * * python /scripts/jellyfin_scan.py
# jellyfin_scan.py
import os
import time
from pathlib import Path
WATCH_DIR = '/downloads/pending'
MEDIA_DIR = '/media'
while True:
for file in Path(WATCH_DIR).glob('*.mp4'):
# 等文件写入完成(大小不再变化)
size1 = file.stat().st_size
time.sleep(5)
size2 = file.stat().st_size
if size1 == size2:
move_to_jellyfin(str(file))
notify_jellyfin_refresh()
time.sleep(1800) # 30 分钟
七、多用户与权限管理
Jellyfin 支持多用户,每个用户有独立的观看记录、收藏和"继续观看"列表。
管理员设置:
控制台 → 用户 → 添加用户
推荐配置:
- 管理员账户(你):完全控制权
- 家人账户:可以播放,不能删除/管理
- 访客账户:只能播放,不记录观看历史
权限设置(每个用户可配置):
✅ 允许媒体播放
❌ 允许删除媒体(不要给家人这个权限!)
❌ 允许管理服务器
✅ 允许远程访问(外网)
还可以设置家长控制——根据内容分级限制某些用户能看什么。
八、常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 视频不显示 | 文件格式不支持或目录权限问题 | 检查文件扩展名、目录挂载 |
| 刮削失败 | TMDB 被墙或文件命名不规范 | 加豆瓣插件、检查命名格式 |
| 播放卡顿 | 服务器在软件转码 | 启用硬件加速 |
| 外网无法访问 | 端口未转发或 DDNS 失效 | 检查路由器设置和 DDNS 状态 |
| 字幕不显示 | 字幕文件编码不是 UTF-8 | 用 iconv 转码或 Jellyfin 设置中调整 |
| 硬件转码不工作 | /dev/dri 权限问题 | chmod 666 /dev/dri/renderD128 |
| 手机端无法播放 HEVC | 客户端不支持 | 在 Jellyfin 设置中启用"允许 HEVC 转码" |
九、合规与温馨提示
- Jellyfin 是一个合法的开源软件,搭建媒体服务器本身不涉及任何法律问题
- 但服务器上存储的内容必须是你合法拥有的——自己拍摄的视频、购买的 DRM-free 内容、或者已获得版权方授权的素材
- 与他人分享受版权保护的内容(即使是通过私人服务器)可能构成侵权
- 使用 DDNS/内网穿透将服务暴露到公网时,注意服务器安全——设置强密码、开启 HTTPS、定期更新系统
- 更多法律讨论见 下载视频算侵权吗?聊聊个人备份与版权的那条线
搭好 Jellyfin 之后,我第一次在手机上打开自己的视频库,看到那些精心整理的海报墙,心里只有一个感受:早就该弄了。 之前那些散落在各个硬盘里的视频,终于有了一个像样的"家"。而且 Jellyfin 的"继续观看"功能会自动记住你看到哪了——手机上看了一半,打开电视接着看,无缝衔接。这种体验,说真的,比很多视频平台的体验还好。
本文由 VidDown 技术博客原创发布。VidDown 下载器支持 30+ 平台的视频下载,配合 yt-dlp 的
--exec参数可以实现下载后自动归档到 Jellyfin 媒体库。访问 VidDown 了解更多。