GitHub 上每隔一段时间就会出现一批自托管视频/音频下载器项目,它们的共同卖点是:一个支持 1000+ 网站、可以部署在自己服务器或 NAS 上的下载服务。这类项目解决的实际问题很明确——当你需要定期保存公开课视频、会议回放、播客节目或设计素材时,浏览器右键、在线解析站和命令行工具各有短板:在线解析站隐私不透明,命令行工具不便于家人或同事使用,浏览器下载插件又受平台更新影响。自托管下载器把“解析链接、提取真实媒体地址、下载文件、后处理合并”的完整流程放在自己的服务里,任何人通过浏览器打开一个网页就能提交下载任务,文件直接落到本地磁盘或 NAS。
这篇文章面向的读者是个人站长、NAS 用户、运维开发,以及经常需要批量保存媒体素材的内容创作者。目标不只是让你会敲启动命令,而是让你理解这类项目为什么能支持那么多网站、文件最终保存在哪里、失败时日志该去哪个文件里查,以及生产环境使用需要补哪些环节。先讲机制,再给最小可运行部署,最后给排查和加固方案。
1. 自托管下载器解决什么问题,1000+ 网站支持从哪来
1.1 自托管不是本地安装,而是把下载服务部署在自己的设备上
自托管英文是 self-hosted,意思是软件运行在你自己控制的设备上,而不是使用第三方平台提供的在线服务。一个自托管视频/音频下载器,本质上是在自己的服务器、NAS 或长期开机的家用电脑上运行一个常驻服务,用户通过浏览器访问服务页面,粘贴链接后由服务端完成下载。
这里要区分三种常见使用方式:
| 使用方式 | 数据流向 | 主要优点 | 主要限制 |
|---|---|---|---|
| 在线解析站 | 链接发送到第三方服务器 | 无需安装,简单直接 | 隐私不可控,服务可能关闭,速度和稳定性取决于对方 |
| 本地命令行 | 本机执行下载脚本 | 功能强,配置灵活,数据不出本机 | 有使用门槛,换设备要重新配置,不方便多人使用 |
| 自托管 Web UI | 下载发生在自己服务器/NAS,数据落自己磁盘 | 多设备共用,带界面和任务管理,可长期运行 | 需要一台常驻设备,初始部署工作更多 |
自托管并不比本地工具更“高级”,它带来的是运维模式的变化:服务集中在一台设备上,所有客户端都通过网页访问。这样家里其他设备、公司内网、甚至远程协作者不需要各自安装下载工具,只要打开同一个地址即可提交任务。
1.2 为什么把命令行工具包装成 Web UI 更有价值
下载视频的底层工具大多是命令行程序,比如 yt-dlp、ffmpeg。命令行对熟悉技术的人很友好,但对日常使用者来说,参数过多、输出不直观、报错信息难懂。Web UI 的核心价值不是重新实现下载能力,而是在命令行工具外面包了一层任务管理界面,解决三类问题:
第一,可访问性。任何人打开浏览器就能用,不需要理解-f、--merge-output-format这些参数。第二,任务管理。下载变成后台任务,可以排队、限速、查看进度、保存历史记录,即使关掉浏览器,服务端仍在继续。第三,接口化。很多自托管下载器除了网页表单,还提供 REST API,后续接脚本、接聊天机器人、接自动化流程都很方便。
这也是这类项目在 GitHub 上受欢迎的原因:它不是做一个“比命令行更快的下载器”,而是做一个“普通人也能用的下载服务”。
1.3 1000+ 网站支持能力来自 yt-dlp 的 extractor 体系
当你看到“支持 1000+ 网站”这个数字时,不要误以为项目作者手写了上千个网站的解析逻辑。实际情况是,自托管下载器项目普遍内置了 yt-dlp 作为下载引擎,而 yt-dlp 维护了一套庞大的 extractor 结构。
在 yt-dlp 源码中,每个支持站点对应一个提取器文件:
yt_dlp/extractor/ ├── youtube.py ├── bilibili.py ├── vimeo.py ├── generic.py └── ...每个 extractor 负责一件事:根据某个网站的页面结构、接口返回值或嵌入数据,找出媒体文件的真实地址、标题、封面、字幕信息。同一个站点改版后,往往是某个 extractor 需要更新,而不是整个下载器失效。
自托管项目选择依赖 yt-dlp,而不是自己从零解析,是因为维护网站提取器成本极高。yt-dlp 社区持续跟进站点变化,项目更新时会把最新版 yt-dlp 打进镜像或依赖中。因此,选择这类项目时最该关注的点是:它是否持续同步 yt-dlp 新版本,以及容器内是否可以单独更新 yt-dlp。如果项目长期不更新,即使页面再漂亮,站点一旦改版,下载任务也会频繁失败。
2. 部署前要确定环境和目录,避免启动之后反复迁移
2.1 建议运行环境
自托管下载器在轻量环境下就能运行,但仍然有几个资源指标需要先评估。下面是一个参考范围,具体数值会因不同项目实现而有差异:
| 资源 | 最低要求 | 建议配置 | 说明 |
|---|---|---|---|
| CPU | 1 核 | 2 核及以上 | 下载本身不耗 CPU,但 ffmpeg 合并视频片段时会占用计算资源 |
| 内存 | 1 GB | 2 GB 及以上 | 内存太小,容器可能被系统 OOM Killer 杀掉 |
| 磁盘 | 视下载量而定 | 独立数据盘或 NAS 挂载 | 视频文件体积大,要预留扩展空间 |
| Docker | 20.10 以上 | 最新稳定版 | Compose 建议使用 V2 版本 |
下载任务属于网络 I/O 密集型操作,对 CPU 要求不高。真正可能在运行时吃掉较多内存和 CPU 的操作是后处理阶段,比如 HLS 分片合并、视频流与音频流混流、字幕烧录等。如果计划在低功耗设备上运行,优先选择官方镜像体积较小、后处理功能按需启用的项目。
2.2 安装完成先检查 Docker 环境
无论使用哪种自托管项目,都强烈建议用 Docker 部署,原因有三个:Docker 镜像已经打包好下载引擎、ffmpeg 和运行环境;容器和宿主机隔离,删除容器不会污染系统;升级时只需替换镜像并重建容器,回滚也容易。
启动前先确认 Docker 已经安装并能正常工作:
docker -v docker compose version如果docker compose命令不存在,而系统里只有docker-compose,需要确认这是旧版 Python 工具还是 Docker Compose V1。新项目大多按 Compose V2 编写配置,命令格式会有些差别。这里以 V2 的docker compose写法为例。
下载目录如果使用 NAS 挂载,需要额外确认文件系统类型和挂载选项。NFS 或 SMB 挂载通常支持大文件,但要检查是否有写入权限、是否支持硬链接、是否被某些安全软件限制。避免把下载目录直接放在容器可写层,否则容器重建后数据会丢失。
2.3 设计目录结构
推荐在部署前就规划好文件目录。一个典型的自托管下载器目录结构如下:
~/media-downloader/ ├── docker-compose.yml ├── downloads/ │ └── (下载完成的音视频文件) └── config/ └── (项目配置、cookie 文件等)downloads是下载文件的落盘目录,config用于存放项目自身的配置和可能需要传给下载引擎的 cookie 文件。这样做的好处是:备份时只备份docker-compose.yml和config即可,下载文件可以单独迁移或清理;容器的可写层保持精简,故障排查范围更小。
2.4 学习环境与生产环境部署差异
很多人第一次部署时只在本机映射端口,验证完就放在那里。如果只是个人学习,这样没问题。如果要长期使用,需要区分两个阶段:
| 部署维度 | 学习演示 | 生产使用 |
|---|---|---|
| 访问方式 | 直接访问http://localhost:8081 | 通过反向代理绑定域名并启用 HTTPS |
| 身份认证 | 不开启或使用默认账号 | 开启 Basic Auth、令牌或接入统一认证 |
| 镜像版本 | 使用latest快速尝鲜 | 固定到具体 release 标签,升级前测试 |
| 数据安全 | 已下载文件可重新获取 | 下载目录、配置目录要有备份策略 |
| 日志管理 | 用docker logs查看 | 必要时采集到集中日志系统 |
生产环境的核心思路是:不要把公网端口直接暴露给所有访问者;不要让容器升级或崩溃导致已下载文件丢失;不要让“能下载内容”这一能力被未授权使用。
3. 用 Docker Compose 跑起一个最小可用实例
3.1 准备 compose 文件
这里以一个常见的 yt-dlp Web UI 下载器 MeTube 为例。不同项目名称、端口、环境变量可能不同,但部署逻辑一致:启动一个容器,挂载下载目录,声明下载参数,通过浏览器操作。下面的docker-compose.yml只是一个起点,落地前请确认你选定的项目文档是否支持这些变量。
version: "3.8" services: metube: image: ghcr.io/alexta69/metube:latest container_name: metube restart: unless-stopped ports: - "8081:8081" environment: - TZ=Asia/Shanghai - DOWNLOAD_DIR=/downloads - YTDL_OPTIONS={"limit_rate":"2M"} volumes: - ./downloads:/downloads把这段内容保存为docker-compose.yml,放在~/media-downloader目录下。其中ghcr.io/alexta69/metube是镜像地址,8081:8081把容器内端口映射到宿主机。如果你的局域网端口 8081 已经被占用,可以改成8082:8081,左边是宿主机端口,右边是容器内端口。
YTDL_OPTIONS是传给 yt-dlp 的 JSON 参数,这里配置了单任务限速 2 MiB/s。如果你的网络环境或下载对象不同,可以根据需要调整,甚至可以先不加这一行。
注意:
latest标签只适合快速体验。长期使用建议到项目 releases 页面查看具体版本标签,例如ghcr.io/alexta69/metube:2024-xx-xx,并把 compose 文件中的镜像固定到该版本。这样升级前后行为可预期,也方便回滚。
3.2 关键参数解释
上面这段配置虽然短,但每个字段都有实际意义。如果不理解,遇到问题时会不知道从哪改。
| 配置项 | 作用 | 常见调整 |
|---|---|---|
restart: unless-stopped | 容器异常退出或宿主机重启后自动拉起 | 不需要自动重启时可改为no |
ports | 宿主机端口映射到容器端口 | 宿主 8081 被占用时改左边端口 |
TZ | 设置时区,影响日志时间和文件名自带时间 | 根据服务器所在地调整 |
DOWNLOAD_DIR | 容器内部下载目录路径 | 必须和卷挂载目标一致 |
volumes | 把宿主机目录挂载到容器目录 | 换成 NAS 路径后需确认权限 |
YTDL_OPTIONS | 透传给 yt-dlp 的额外参数,JSON 格式 | 可设置limit_rate、format等 |
最容易出错的是DOWNLOAD_DIR与挂载目标不一致。如果容器内部期望下载目录是/downloads,但你只挂载到了/data,服务会把文件写到容器可写层,容器删除后文件也就没了。判断方法是查看项目文档中声明的默认下载路径,让环境变量、挂载目标、项目默认路径三者对齐。
3.3 启动服务并验证页面
在~/media-downloader目录下执行:
docker compose pull docker compose up -d docker compose pspull会从镜像仓库拉取镜像,up -d在后台启动容器,ps输出当前容器运行状态。正常情况下,容器状态应该是Up,不会显示Restarting或Exited。
然后打开浏览器访问:
http://localhost:8081如果部署在服务器上,将localhost替换为服务器 IP。页面会出现一个输入框,用于粘贴视频或音频链接。看到这个页面,说明服务已经启动成功。如果页面打不开,先检查防火墙是否放行端口,再执行docker compose logs metube查看启动日志。
3.4 用公开链接完成第一次下载验证
部署成功后不要急着接真实需求,先用一条公开测试链接验证整条链路。适合测试的资源包括:公开的课程视频、发布者明确允许下载的样例文件、合规的公开音视频页面。避开版权存疑的内容。
在页面的输入框中粘贴链接,选择一个目标格式,点击下载。页面会进入任务列表,出现queued、downloading、done等状态。完成后检查宿主机的下载目录:
ls -lh ~/media-downloader/downloads file ~/media-downloader/downloads/*file命令会输出文件真实类型。如果显示类似Media或Audio的字样,说明文件已经被正确写入宿主机目录,而不是留在容器内部。如果服务支持 REST API,也可以用 curl 提交链接,便于后续自动化:
curl -X POST http://127.0.0.1:8081/add \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "url=https://example.com/sample-video"不同项目提供的路由不同,/add不一定通用。使用前查看该项目文档中的 API 说明。
3.5 启动阶段最常见的失败原因
第一次启动时遇到问题很常见,下面几张情况覆盖了绝大多数早期故障:
| 现象 | 可能原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 端口无法启动 | 宿主机端口被占用 | ss -lntp | grep 8081 | 更换宿主机端口 |
| 镜像拉取超时 | 网络访问镜像仓库不稳定 | 观察 pull 阶段报错 | 配置 Docker Registry Mirror 后重试 |
| 容器不断重启 | 环境变量或启动命令错误 | docker compose logs查看 | 按日志修正配置 |
| 下载文件不在宿主机 | 挂载目录配置不一致 | docker inspect metube查看 Mounts | 对齐DOWNLOAD_DIR与卷挂载路径 |
| 文件保存提示权限错误 | 宿主机目录不可写或 UID 不匹配 | ls -ld ~/media-downloader/downloads | 调整目录所有者或容器运行 UID |
排查启动问题有一条通用顺序:先看容器是否成功运行,再看端口是否监听,再访问页面,最后提交测试下载。不要在服务还没起来时就急着调整下载参数。
4. 一条下载任务背后发生了什么
4.1 从粘贴链接到文件落盘的完整链路
理解了部署,还需要理解服务内部发生了什么,否则以后看到日志会一头雾水。一次下载任务通常会经历以下步骤:
- 用户在 Web UI 输入 URL,选择格式和清晰度。
- 服务端将任务加入队列,返回任务 ID。
- 工作进程启动一次 yt-dlp 子进程。
- yt-dlp 根据 URL 选择合适的 extractor。
- extractor 请求目标页面或平台接口,解析真实媒体地址。
- yt-dlp 下载视频流和音频流,需要时调用 ffmpeg 进行合并、转封装或提取音频。
- 任务状态变为完成,文件写入挂载的下载目录。
这条链路的每一步都可能失败。页面打不开、链接解析失败、下载超时、ffmpeg 不存在、磁盘空间不足,都会让任务停在某个阶段。这也是为什么日志如此重要:它能告诉你是还没开始下载,还是下载到一半失败,还是后处理阶段失败。
4.2 为什么需要服务端队列和限速
多人同时提交任务时,如果没有队列,所有下载会同时启动。此时可能出现两种问题:家庭带宽被打满,其他设备无法正常上网;目标网站检测到异常并发,直接断开连接或返回异常页面。
自托管服务引入队列的价值就在这里:同一时间只允许一定数量的下载任务运行,其余任务排队等待。你可以通过limit_rate之类的参数给每个任务限速,避免单次下载抢占全部带宽。比如前面配置的:
{ "limit_rate": "2M" }表示每个任务最多使用 2 MiB/s 的下载带宽。如果三四个任务同时限速运行,总带宽消耗仍在可控范围内。这个参数调小会降低单个任务速度,但能保障网络稳定;调大可以加快下载,但可能影响家庭或办公网络体验。
4.3 文件格式、画质和字幕参数怎么透传
自托管下载器在界面上往往只暴露部分参数,更精细的控制要看它是否支持透传 yt-dlp 选项。以 yt-dlp 为例,常见的参数含义如下:
| 参数 | 作用 | 说明 |
|---|---|---|
-f "bv*+ba/b" | 选择最佳视频流和最佳音频流,无则用完整视频 | bv是 best video,ba是 best audio |
--merge-output-format mp4 | 合并后封装为 MP4 | 需要容器内置 ffmpeg |
--write-thumbnail | 下载封面图 | 适合媒体库整理 |
--write-subs | 写入字幕 | 需要站点提供字幕文件 |
--embed-metadata | 写入元数据 | 便于播放器展示标题 |
如果 Web UI 支持把 JSON 透传给 yt-dlp,可以这样写:
{ "format": "bv*+ba/b", "merge_output_format": "mp4", "writethumbnail": true, "writesubs": true }需要注意,不同项目对 JSON 配置项的命名可能不同,有的用write_thumbnail,有的用writethumbnail。使用时以 yt-dlp 官方文档和项目 README 为准。如果某个参数没有生效,先检查是“项目不支持透传该参数”还是“键名写错”。
4.4 Cookie、登录态与访问限制
有些网站需要登录后才能看到视频,或需要登录后才能下载高清版本。此时下载器拿到的只是公开页面,可能返回 403 或未登录提示。解决办法是把浏览器中已有的 Cookie 传给 yt-dlp。
常见做法是使用 cookies 文件:浏览器插件导出 cookies.txt,挂载到容器内,然后在下载参数中加入:
--cookies /config/cookies.txt容器内路径必须与挂载对应,比如 compose 中把宿主机./config挂载到/config,那么 cookies 文件路径就是/config/cookies.txt。
需要注意的是:只能使用自己账号的 Cookie,且只能下载自己有权访问的内容。不要通过该方式绕过付费墙、DRM 或访问控制,这既违反平台条款,也可能涉及法律风险。若目标网站对登录抓取有明显限制,优先确认自己是否有权限下载,而不是强行绕过。
5. 怎么确认它真的能用,以及失败时按什么顺序排查
5.1 判断下载成功的指标
很多人看到页面显示完成就认为成功,但页面显示完成并不代表文件可用。完整的成功判断标准应该包括:
- 任务状态变为
done或相等状态。 - 下载目录中确实出现了文件,且文件大小不为 0。
- 用播放器能正常打开,视频时长与源内容一致。
- 如果任务包含多个分片,合并后的文件没有花屏或音画不同步。
下降后的第一件事是检查文件的真实类型。比如一个下载回来的文件扩展名是.mp4,但file命令显示其实是HTML,这通常是下载到了错误页面。另一个常见情况是任务显示完成,但文件只有几百字节,说明源站返回的是空壳或错误提示。此时优先看日志,而不是反复重试。
5.2 日志去哪看
容器化部署的日志统一由 Docker 管理。查看 MeTube 容器的日志:
docker compose logs -f metube-f表示持续跟踪日志输出,适合观察新任务。如果日志量很大,只想看最后一部分:
docker compose logs --tail=200 metube日志里的关键字可以帮助判断阶段:出现Extracting URL表示正在解析链接;出现Destination表示开始写文件;出现Completed表示下载完成;出现ERROR、Traceback、403、403 Forbidden、ffmpeg not found则需要进一步排查。
5.3 高频故障对照表
实际使用中,下面这些故障出现频率最高:
| 问题现象 | 可能原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 任务一直排队不下载 | 并发数达到上限 | 查看并发配置或等待其他任务完成 | 调大并发数或减少同时提交任务 |
| 返回 403 Forbidden | 目标站要求登录或触发反爬 | 请先用浏览器手动打开链接确认是否正常 | 传入 Cookie,或降低请求频率 |
| 下载到中途失败 | 网络中断、源站限速、链接过期 | 查日志中最后一个请求 URL | 减少并发,配置限速后重试 |
| 文件无法播放 | 缺少 ffmpeg 后处理或格式选择不当 | 检查镜像是否包含 ffmpeg,执行ffmpeg -version | 换一个内置 ffmpeg 的镜像,或调整格式为mp4 |
| 容器重启后没有文件 | 未挂载下载目录 | docker inspect metube查看挂载信息 | 在 compose 中补充 volume 配置 |
| 磁盘占用迅速增长 | 视频文件过大或临时文件未清理 | df -h检查磁盘 | 清理临时分片,规划自动清理任务 |
5.4 排错顺序
遇到下载失败时,不要直接重复点击下载,按下面顺序排查能更快定位问题:
第一步,看任务状态。任务停在queued还是downloading,还是已经报错。第二步,看容器日志。复制日志中与任务相关的 ERROR 行。第三步,把日志中的 URL 复制到普通浏览器手动访问,确认链接本身还有效,确认是否能正常播放。第四步,检查目标站是否需要登录、是否有地区限制、是否有下载权限。第五步,检查磁盘空间和下载目录权限。
df -h ls -ld ~/media-downloader/downloads第六步,如果前面都没问题,可能是下载引擎版本过旧,目标网站结构变化后原 extractor 失效。去容器内确认当前 yt-dlp 版本:
docker compose exec metube yt-dlp --version如果版本落后较多,更新镜像或直接在容器内升级 yt-dlp。如果确认是单站点问题,可以到对应 yt-dlp issue 区搜索相同报错。排错的核心原则是:先看日志,再复现链接,再查环境和版本,最后才是换参数或换工具。
6. 生产化建议与可以继续扩展的方向
6.1 存储与文件清理策略
部署完成后,最需要规划的是文件生命周期。下载器本身只会把文件写进目录,不会替你管理硬盘空间。如果一个下载任务默认下载最高画质,一部影片可能超过几十 GB,几周就能占满磁盘。
建议从一开始就确定三个策略:
- 下载目录独立挂载到 NAS、数据盘或远程存储,避免容器重建导致文件丢失。
- 文件名中加入日期或视频 ID,便于追踪来源。例如在 yt-dlp 参数中设置输出模板。
- 定期清理临时文件和已完成文件。可以使用宿主机 crontab 定期执行 find 删除 N 天前的文件:
find ~/media-downloader/downloads -type f -mtime +30 -delete这条命令会删除 30 天前修改过的文件。使用前先明确自己的清理周期,不要自动删除还需要保留的素材。更稳妥的做法是先移动到一个待审核目录,确认后再物理删除。
6.2 安全加固
自托管下载器一旦暴露到公网,就成为一台可被外人使用的下载服务,可能被滥用。最直接的安全措施是不把容器端口直接暴露到公网,而是放到内网,或通过反向代理加认证访问。
下面是一个最小 Nginx 反向代理加 Basic Auth 的示例,仅说明思路,生产环境还需要配置 HTTPS 证书:
server { listen 80; server_name dl.example.com; auth_basic "download"; auth_basic_user_file /etc/nginx/.htpasswd; location / { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }反向代理解决的是访问入口问题。除此之外,生产环境还要考虑:
- 下载目录不要放在 Web 服务的静态根目录下,避免文件被直接下载。
- 不要在公网使用默认端口和弱密码。
- 如果 Web UI 支持令牌或用户账号,务必启用。
- 容器尽量以非 root 用户运行,必要时映射到固定 UID。
注意:无论用 Basic Auth 还是其他认证,认证保护的是服务入口,不等于保护目标网站的账号和 Cookie。不要将任何账号凭据直接写在下载链接或公开配置中。
6.3 扩展:接入自动化流程
如果使用频繁,完全靠浏览器提交任务会变得繁琐。可以围绕自托管服务的 API 做三类扩展:
第一,聊天机器人。把下载服务接到 Telegram Bot 或企业微信机器人,用户把链接发给机器人,机器人在服务端调用下载 API,完成后返回状态或文件地址。这样手机端就能批量提交任务,适合内容创作者和社群维护者。
第二,脚本批量下载。维护一个包含 URL 列表的文本文件,用脚本逐行读取并调用下载服务的 REST API。这种方式适合周期性保存公开课程或合规素材。
第三,订阅通知。部分项目支持 Webhook 或通知机制。下载完成时向指定地址发送请求,可以接进自动化工作流,实现“下载完成 -> 通知 -> 移动到媒体库 -> 生成索引”的链路。
扩展前先确认项目是否提供 API、是否存在事件回调、并发控制怎么做。不要在自己机器上再写一套重复解析逻辑,自托管项目的核心价值已经体现在这些接口中。
6.4 版权合规与合理使用
下载工具本身是中性的,但下载行为需要遵守规则。适合用这类服务覆盖的场景是:公开课和开放培训视频、Podcast 订阅备份、获得授权的内容、公有领域或 CC 协议资源、平台明确允许下载的自有媒体。
不应该用这类服务做的事情包括:下载付费墙后的内容、绕过 DRM 保护、批量下载平台明确指出禁止下载的视频、将下载内容用于商业分发。平台是否允许下载,一般会在服务条款中说明。自托管服务如果开放给多人使用,更应该先约定使用范围,避免个体行为给整个服务带来合规风险。
6.5 更新策略
自托管下载器最怕的不是不用,而是不更新。网站结构每个月都在变,extractor 需要同步更新才能继续识别新页面。如果镜像长期停留在旧版本,某个站点可能某天就解析失败。
更新前建议先备份 compose 文件和配置目录:
cp ~/media-downloader/docker-compose.yml ~/media-downloader/docker-compose.yml.bak然后拉取新镜像并重建容器:
docker compose pull docker compose up -d更新后用一条测试链接验证核心流程,确认没问题后再恢复日常使用。如果更新后某个配置不兼容,可以快速回滚到之前的镜像标签。固定版本号、小步更新、每次更新后验证,是生产环境维护这类项目的最稳路径。
到这里,一台自托管下载器已经从镜像拉取、任务提交、失败排查走到了生产加固。对个人用户来说,最有价值的不是页面多漂亮,而是“链接提交到文件落盘”这条链路足够透明:文件在自己磁盘,任务日志可查,下载引擎随 yt-dlp 持续更新。接下来更值得花时间的地方,是把你自己的使用场景固化成一套规则:哪些链接可以下载,文件按什么规则落盘,下载完成后怎么归档,什么时候清理。把这些规则补全,下载器才算真正融入工作流。