跳至主要内容
五折优惠 全部方案,限时优惠。起价 $2.48/mo
11 min left
游戏与媒体

在 VPS 上以无界面方式运行 yt-dlp 做个人归档

S 作者 Sajjad 11 分钟阅读
yt-dlp running headless on a VPS, feeding scheduled downloads into an organized video library that a media server can scan

把 yt-dlp 放在 VPS 上,长时间的下载就有了一台在你合上笔记本之后仍然在线的机器。它还理顺了一条路径:从按计划抓取频道,一直到 Jellyfin 能够扫描的媒体库。

服务器会改变工作流中的三处:安装需要当前版本的 YouTube 相关依赖,需要账号才能看的视频需要安全地转移 cookie,自动化则需要有意识地控制请求节奏。下面的方案把这三点都照顾到了,同时并不假定每次下载都需要 cookie、网页界面或 PO Token 提供方。

TL;DR(太长不看版)

  • 在 Python 虚拟环境中安装 yt-dlp,并配上 ffmpeg、ffprobe、yt-dlp-ejs 和一个受支持的 JavaScript 运行时。项目目前推荐的运行时是 Deno。
  • 先不要用账号 cookie。只有在私享播放列表、年龄限制视频、会员专属内容或其他需要账号的情形下才加上。
  • 在拿到实测理由之前,保持 yt-dlp 默认的单分片行为。用 sleep 相关选项和错峰的计划任务来降低请求压力。
  • 纯命令行加 systemd,是最简单又可靠的一套。想要免打理的频道规则就选 Pinchflat,想要浏览器里的下载队列就选 MeTube,想要可搜索的观看界面就选 Tube Archivist。
  • 把存储当作规格计算里最主要的变量。在为完整归档购买磁盘容量之前,先用有代表性的样本测一遍。

负责任地使用 yt-dlp

只有在你获得许可、且使用方式符合平台条款和适用法律时,才去归档这些内容。

  • 合适的对象包括:你自己上传的内容、公有领域的作品,以及权利人已授权下载的素材。
  • 订阅 YouTube Premium 本身,并不等于获得了在 YouTube 提供的功能之外复制视频的许可。
  • YouTube 的授权与限制条款 限制下载和自动化访问,除非该服务或相关权利人另行授权。
  • 本教程不涉及绕过 DRM、商业性再分发,也不涉及规避平台执法手段的方法。

开始之前你需要准备什么

基础安装很小,媒体文件可不小。在下载整个频道之前,先把服务器和存储路径准备好。

  • 一台运行 Ubuntu 22.04 或更高版本、或当前 Debian 版本的 VPS,并可通过 SSH 访问
  • Python 3.10 或更高版本
  • 足够存放有代表性样本的空间,再加上留给未完成文件和后处理的余量
  • 一个单独的本地浏览器,仅在你需要账号 cookie 时才用到
  • 可选:Jellyfin、Emby 或其他能读取归档目录的媒体服务器

为什么要把 yt-dlp 放到 VPS 上?

当任务必须独立于你日常那台电脑继续运行时,VPS 就有用了。它给下载器一个常驻进程、一套可预期的文件系统,以及一个不会因笔记本休眠或换网络而中断的调度器。

取舍很重要。当你把归档再串流出去时,服务器的月流量额度会变成瓶颈;VPS 的 IP 也可能比家庭宽带更早撞上请求限制。更新、凭据管理、备份、清理存储和媒体服务器安全,也都归你自己负责。只下载一次的话,用笔记本更省事。要反复抓取或维护一个共享媒体库,VPS 运维起来更轻松。

围绕存储和播放来给 VPS 定规格

yt-dlp VPS sizing guide: starting allocations of 2 vCPU and 2 GB RAM for raw yt-dlp with systemd, 2 vCPU and 4 GB RAM for MeTube or Pinchflat, and 4 vCPU and 8 GB RAM for Tube Archivist, beside a method for projecting storage from a sample download

yt-dlp 负责下载和重新封装,通常并不会把每个文件都转码。因此下载器长期占用的 CPU 和内存都不高,而磁盘占用则随视频时长、分辨率、编解码器和所选格式而变化。

把下面这些配置当作偏保守的起点,而不是官方最低要求:

安装起步配置存储策略最适配
纯 yt-dlp 配 systemd2 vCPU, 2 GB RAM按样本推算容量无界面的定时下载
MeTube 或 Pinchflat2 vCPU, 4 GB RAM按样本推算容量浏览器下载队列或频道订阅
Tube Archivist4 vCPU, 8 GB RAM留有增长余量的本地磁盘可搜索的归档,并自带播放

小规模测试大约需要 2 GB 可用内存,中到大型部署大约需要 4 GB,依据是 Tube Archivist 部署指南。从高于这个下限的配置起步,才留得出余量给操作系统、Docker、Elasticsearch 的活动,以及 Jellyfin 之类的另一个服务。

在为完整归档定容量之前,先按你选定的分辨率模拟或下载一组有代表性的视频。用 du 看一下生成的目录,除以已完成的视频数量,并把特别长的那些也考虑进去。

du -sh ~/archive
find ~/archive -type f \( -name '*.mp4' -o -name '*.mkv' \) | wc -l
df -h ~/archive

用样本比套用“每个视频多少 GB”的笼统估算更有用,因为它反映的是这个频道真实的时长和格式组合。

安装 yt-dlp 及其当前依赖

yt-dlp 项目支持 Python 3.10 及更高版本。 yt-dlp 的依赖清单 强烈建议配上 ffmpeg、ffprobe、yt-dlp-ejs 以及一个受支持的 JavaScript 运行时,才能完整支持 YouTube。

先从系统软件包和一个隔离的 Python 环境开始:

sudo apt update
sudo apt install -y python3 python3-venv ffmpeg curl nano
python3 -m venv ~/yt-dlp-venv
source ~/yt-dlp-venv/bin/activate
python -m pip install -U --pre "yt-dlp[default]"

Deno 在 yt-dlp 中默认启用,也是该项目的 EJS 指南 EJS 指南目前推荐的运行时。安装它,把它加入 shell 的 PATH,然后逐项验证各个组件:

curl -fsSL https://deno.land/install.sh | sh
export PATH="$HOME/.deno/bin:$PATH"
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile
yt-dlp --version
ffmpeg -version | head -1
deno --version
yt-dlp --simulate --verbose "https://www.youtube.com/watch?v=VIDEO_ID"

详细输出会列出 yt-dlp 能识别到的依赖。如果缺少 ffmpeg,yt-dlp 会给出警告,并且无法把分离的最佳画质视频流和音频流合并,也无法执行若干后处理步骤。

如果是用 pip 安装的,就在虚拟环境里重新跑一次 pip 来更新。内置的 yt-dlp -U 命令是给发行版二进制文件用的,不适用于 pip 包。

source ~/yt-dlp-venv/bin/activate
python -m pip install -U --pre "yt-dlp[default]"

关于更新通道,可参见 yt-dlp 的更新说明。stable、nightly 和 master 三条通道都可用,项目建议普通用户走 nightly,因为提取器的修复会先落到那里,然后才进入下一个稳定版。

只对需要账号的内容才加 cookie

先不带 cookie 试一下目标网址。yt-dlp 的 YouTube 指南写明,只有需要账号的内容才用得上 cookie,比如私享播放列表、年龄限制视频和会员专属内容。OAuth 登录在 yt-dlp 里已经不再可用。

确实需要 cookie 时,请在你本地的电脑上导出一个专用的 YouTube 会话。项目给出的 cookie 导出流程 做法是使用一个无痕窗口,这样 YouTube 就不会在你开着的普通标签页里轮换掉已导出的会话:

  1. 只开一个隐私窗口或无痕窗口,然后登录 YouTube。
  2. 在同一个标签页里,打开 YouTube 的 robots.txt 文件。
  3. 只导出 youtube.com 的 cookie,用 Netscape 格式,并使用 yt-dlp FAQ 里列出的某个扩展。
  4. 关掉这个隐私窗口,之后不要再打开那个会话。
  5. 把这个文件复制到 VPS 上,并收紧它的权限。
scp cookies.txt your-user@your-vps-ip:~/cookies.txt
ssh your-user@your-vps-ip 'chmod 600 ~/cookies.txt'

用一个需要账号的网址来测试这个文件:

~/yt-dlp-venv/bin/yt-dlp \
  --cookies ~/cookies.txt \
  --simulate \
  "https://www.youtube.com/watch?v=VIDEO_ID"

cookie 文件就是会话凭据,浏览器扩展也需要谨慎挑选,依据是 yt-dlp 的 cookie 常见问题。YouTube 提取器指南还警告说,把账号用在 yt-dlp 上可能导致临时或永久封禁。只在目标确实需要时才用 cookie,把文件保管好,并且用一个单独的账号,而不是你的主 Google 账号。

如果大多数目标都是公开内容,就别把 --cookies 写进全局配置。用第二个配置文件,或者只在需要它的任务上加这个参数。

把 PO Token 当作有条件才用的排障手段

yt-dlp access troubleshooting flow: simulate the URL first, then branch to no cookies needed for a working public video, a cookie export for account-gated content, or a dependency update and PO Token check when failures continue

Proof of Origin Token 并不是安装时人人都要的东西。YouTube 目前只对部分客户端与请求的组合强制要求这类令牌,而且具体的对应关系还在变。

当默认客户端失败时,建议为 mweb 客户端配一个 provider 插件,依据是 yt-dlp 的 PO Token 指南。文中把 bgutil-ytdlp-pot-provider 列为推荐选项之一,但这个插件同时需要一个令牌提供方和一个 yt-dlp 插件。只装那个 Python 包并不算配置完成。

当 YouTube 下载失败时,按这个顺序排查:

  1. 更新 yt-dlp、yt-dlp-ejs 和 JavaScript 运行时。
  2. 加上 --verbose、且不额外指定客户端,把故障复现一遍。
  3. 只有当视频确实需要账号时,才加上 cookie。
  4. 如果报错指向 PO Token 的强制要求,就按官方指南里链接的、当前版本的 provider 说明来操作。

这样就能把一个易变的临时方案,挡在原本稳定的基础安装之外。

按工作流来挑前端

关键的抉择不在于哪个界面功能列表更长。你要先想清楚:需要的是浏览器里的下载队列、按规则订阅,还是一个完整的、类似 YouTube 的本地库。

选项部署擅长之处主要取舍
纯命令行没有前端脚本、配置文件、systemd,以及对参数的精确控制没有浏览器界面
MeTube一个 Docker 容器在浏览器里下载,外加频道和播放列表订阅下载之后的媒体库管理能力有限
Pinchflat一个 Docker 容器针对频道和播放列表的规则、RSS、保留策略,以及适配媒体中心的输出为管理下载而生,而不是为在应用内观看
Tube Archivist应用、Redis 和 Elasticsearch 三个容器搜索、元数据、队列、频道页面和播放内存占用和运维负担最高

如果只想要浏览器里的轻量流程,MeTube 支持频道和播放列表订阅,会定期检查有没有新内容并自动排进队列。当你主要想要的就是一个网页表单加一条下载队列时,它依然是最简单的选择。

如果你要长期归档频道,之后通过 Jellyfin、Plex、Kodi 或 RSS 客户端来看,Pinchflat 最合适。它自成一体,会定期检查来源,支持保留规则,并且有意把播放这件事留给别的应用。

当你希望归档本身就像一个可搜索的视频站点时,Tube Archivist 多跑那几个服务才算值。如果播放界面已经由 Jellyfin 提供,那就先用纯命令行或 Pinchflat,等它的搜索和元数据模型确实解决了你的实际问题,再把 Tube Archivist 加进来。

搭一份可复用的归档配置

把长期不变的选项放进一个文件,频道地址则从命令行或调度器传进来。这个示例把输出限制在 1080p,记录已完成的视频 ID,稍微放缓请求节奏,并写入即便离开 yt-dlp 也依然有用的元数据。

创建目录和配置文件:

mkdir -p ~/.config/yt-dlp ~/archive
nano ~/.config/yt-dlp/archive.conf

加入这些选项:

-P "~/archive"
-o "%(channel)s/%(upload_date>%Y-%m-%d)s - %(title)s [%(id)s].%(ext)s"
-f "bv*[height<=1080]+ba/b[height<=1080]"
--merge-output-format mp4
--download-archive ~/archive/downloaded.txt
--sleep-requests 1
--sleep-interval 5
--max-sleep-interval 10
--write-info-json
--write-thumbnail
--write-subs
--write-auto-subs
--sub-langs en.*
--embed-subs
--embed-thumbnail
--embed-metadata
--sponsorblock-mark all

先拿单个视频跑一次测试,再把整个频道交给 yt-dlp:

~/yt-dlp-venv/bin/yt-dlp \
  --config-location ~/.config/yt-dlp/archive.conf \
  "https://www.youtube.com/watch?v=VIDEO_ID"

然后用同一份配置去跑频道或播放列表的地址:

~/yt-dlp-venv/bin/yt-dlp \
  --config-location ~/.config/yt-dlp/archive.conf \
  "https://www.youtube.com/@CHANNEL/videos"

--download-archive 文件会记下成功下载的视频 ID,之后再跑就会自动跳过。一开始请保持默认的 --concurrent-fragments 1。yt-dlp 的 YouTube 指南建议在会话触及请求限制时,在视频之间加上延迟;但它并没有给出适用于所有 VPS IP 的安全分片并发上限。

用 systemd 给下载排期

用户级的 systemd timer 能给任务提供持久的排期和日志,而不必往 cron 里塞一条很长的命令。随机延迟还能避免所有排定的来源在同一秒一起启动。为了不依赖用户级管理器的 PATH,这个 service 让 yt-dlp 直接指向 Deno 的默认安装位置。

创建 service:

mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/yt-dlp-archive.service
[Unit]
Description=Archive a YouTube channel with yt-dlp

[Service]
Type=oneshot
ExecStart=%h/yt-dlp-venv/bin/yt-dlp --js-runtimes deno:%h/.deno/bin/deno --config-location %h/.config/yt-dlp/archive.conf https://www.youtube.com/@CHANNEL/videos

创建 timer:

nano ~/.config/systemd/user/yt-dlp-archive.timer
[Unit]
Description=Run the yt-dlp archive daily

[Timer]
OnCalendar=*-*-* 04:00:00
RandomizedDelaySec=30m
Persistent=true

[Install]
WantedBy=timers.target

启用 timer,并允许该用户级 service 在你没有登录时也能运行:

systemctl --user daemon-reload
systemctl --user enable --now yt-dlp-archive.timer
sudo loginctl enable-linger "$USER"
systemctl --user list-timers
journalctl --user -u yt-dlp-archive.service -n 100 --no-pager

如果有多个频道,就为每个频道建一个 service 实例,或者用各自独立的 timer。把它们的时间错开,别让几个大任务挤在一起启动。

把归档接入 Jellyfin

把同一个 ~/archive 目录挂载或开放给 Jellyfin,然后把它添加为一个媒体库。对于按频道/日期排列的原始结构,适合用 Jellyfin 的 Music Videos 媒体库类型 ,它接受嵌套文件夹和任意文件名,也不去联网匹配元数据。对非音乐的归档来说,这个标签并不贴切,但在文件结构上比 "Shows" 类型更合适,后者期望的是剧集和季度文件夹加 SxxEyy 式的剧集命名。除非你能接受 Jellyfin 关于元数据结果可能不可靠的提醒,否则请避开 Mixed Content。

yt-dlp 配置里关于元数据、缩略图和字幕的那几个选项,会把有用的信息留在每个文件旁边或写进文件里。即便如此,Jellyfin 可能仍需要你手动调整元数据,因为一个 YouTube 频道并不能干净地对应到电视剧数据库。

如果你想要贴合 Jellyfin 的文件名和元数据、又不想手动折腾太多,Pinchflat 为这种流程提供了媒体中心预设。至于媒体服务器之间更大范围的取舍,可以看我们的 Jellyfin 与 Plex 对比 ,其中讲了播放、远程访问和转码方面的取舍。

把这套东西放到一台常开的服务器上

等这套流程在一小批测试内容上跑通之后,就把它挪到 Cloudzy 的 Linux VPS 上,这样定时下载和你的媒体库就能一直在线,而不用占着你每天用的那台电脑。你还可以部署 一键安装版的 Jellyfin ,并把它的媒体库指向 yt-dlp 的输出目录。

查看 Linux 套餐

在拥有 root 权限、NVMe 与 AMD EPYC 强大性能的 Linux VPS 上构建应用。

查看 Linux 套餐

常见问题

在 VPS 上运行 yt-dlp 需要 GPU 吗?

不需要。yt-dlp 没有 GPU 也能下载和重新封装。GPU 之所以变得相关,是当 Jellyfin 或其他媒体服务器必须为不兼容的客户端、或带宽较低的连接转码视频时;直连播放并不需要这种转换。

yt-dlp 能续传中断的下载吗?

可以。yt-dlp 默认就启用了分片文件和续传,所以之后再跑通常会接着已下载的分片继续,而不是重头再来。如果你想要这种行为,就别加 --no-continue、--no-part 或 --force-overwrites。

yt-dlp 和 Jellyfin 该共用一台 VPS 吗?

如果存储、CPU 和出站流量对两件事都够用,就可以共用一台 VPS。但当播放或转码要和大批量下载抢资源、当你需要各自独立的维护窗口,或者当归档更适合放在比流媒体服务器更便宜的存储上时,就该把它们分开。

分享

博客更多内容

继续阅读。

准备好部署了吗? 起价 $2.48/月。

独立云厂商,自 2008 年起。AMD EPYC、NVMe、40 Gbps。14 天退款保证。