跳到正文

把 yt-dlp 做成 API:嵌入、自建 REST 封装,还是使用托管服务

作者 Tunelio 团队发布于

yt-dlp 没有官方 REST API —— 它是一个库和一个 CLI。要通过 HTTP 调用它,你要么把它嵌入自己的服务,要么部署社区封装之一,要么付费使用替你运行提取集群的托管 API。本文给出一个最小的自建封装、规模化运行的真实成本(CPU、IP 信誉、失效、带宽),以及自建与购买之间坦诚的决策规则。

选项 1:把 yt-dlp 嵌入自己的代码

如果你的应用是 Python,直接 import yt_dlp 并调用 extract_info —— 没有 HTTP 跳转,也没有第二个服务。Python 指南涵盖选项、钩子、错误处理和 async 陷阱。对脚本、内部工具和单租户应用来说,这就是正确答案。

选项 2:自建 REST 封装

封装让非 Python 代码、机器人和其他服务也能使用 yt-dlp。最小可用版本只有几十行:

# app.py —— 最小的 yt-dlp REST 封装(FastAPI)
import asyncio, yt_dlp
from fastapi import FastAPI, HTTPException, Query

app = FastAPI()
SEM = asyncio.Semaphore(4)                 # 每次提取约 1 秒 CPU;限制并发

def extract(url: str, fmt: str) -> dict:
    opts = {"quiet": True, "skip_download": True, "format": fmt, "noplaylist": True,
            "source_address": "0.0.0.0"}   # IPv4
    with yt_dlp.YoutubeDL(opts) as ydl:
        return ydl.extract_info(url, download=False)

@app.get("/info")
async def info(url: str = Query(...)):
    try:
        async with SEM:
            i = await asyncio.to_thread(extract, url, "b")
    except yt_dlp.utils.DownloadError as e:
        raise HTTPException(status_code=502, detail=str(e)[:200])
    return {"id": i["id"], "title": i["title"], "duration": i["duration"],
            "formats": [{"id": f["format_id"], "height": f.get("height"), "ext": f["ext"]}
                        for f in i["formats"] if f.get("vcodec") != "none" and f.get("acodec") != "none"]}

@app.get("/url")
async def direct_url(url: str, quality: int = 720):
    async with SEM:
        i = await asyncio.to_thread(extract, url, f"b[height<={quality}]")
    return {"url": i["url"], "expires_in_hours": 6}   # 仅对本服务器的 IP 签名

用 uvicorn 在 nginx 后运行,放进 Python 指南中的 Docker 镜像(yt-dlp + ffmpeg + Deno)。yt-dlp-web-ui、MeTube 和各种 “yt-dlp server” 镜像等社区项目做的是同样的事,外加界面和队列;它们继承下面的每一条限制。

封装解决不了的问题

返回的 URL 绑定在你的服务器上

googlevideo 链接针对发起请求的 IP 和客户端签名,约六小时后过期。把它们交给其他网络上的最终用户会得到 403;真正的设计是让媒体经过你的服务器流出(带宽 ×2),或先下载再重新提供(磁盘 + 带宽)。这正是托管 API 所说的 “tunnel”。

合并与音频每个请求都需要 ffmpeg

720p 以上都是分离的视频流和音频流;MP3 是转码。两者都是每次下载在你机器上消耗的 ffmpeg CPU(ffmpeg 指南)。

每次提取的 CPU

在我们的集群上,一次 YouTube 提取约消耗 1.2–1.3 秒 CPU,主要用于求值播放器 JavaScript,而且每个请求都要付。每秒十个请求就是十几个核心 —— 这还是在提供任何媒体字节之前。按视频缓存提取结果几小时,才能扛住突发流量。

IP 信誉才是真正的天花板

一台从单一数据中心地址每天提取数百个视频的服务器会遭遇 “Sign in to confirm you’re not a bot” 和媒体 403 —— 在我们的测量中,大约每四个新的数据中心出口就有一个在首次接触时被挑战,IPv6 更糟。要保持在线就意味着住宅代理、粘性会话、PO token 生成和 cookie 轮换(代理、cookies 和 403 指南)。这是人们最容易低估的运维成本。

失效

YouTube 每隔几周就改点什么;每次改动都是一次 yt-dlp 更新和重新部署,通常在故障发生后才被发现(更新指南)。要为值班预算。

选项 3:托管 YouTube 下载 API

curl "https://tunelio.dev/create?url=https://youtu.be/dQw4w9WgXcQ&quality=720p" \
  -H "Authorization: Bearer tnl_your_api_key"
# → { "url": "https://…/tunnel?id=…&sig=…", "file_size_str": "28.52 MB", "status": "ok" }

托管 API 就是上面的封装,加上集群,加上 tunnel:一次请求返回一个签名下载 URL,可以直接交给用户、机器人或 S3 上传,而提取、ffmpeg、IP 轮换和 YouTube 的变动都在服务端处理。Tunelio 是我们自己的服务,请带着这一点阅读对比;这些数字就是我们每天面对的数字。

自建还是购买 —— 坦诚的对照表

  • 单机每天不到约 50 个视频,个人或内部使用 → 嵌入或自建;免费工具足够,还能学到这套技术栈。
  • 面向你无法控制的用户的产品功能(机器人、SaaS、流水线)→ 托管 API 通常胜出:光是代理账单往往就超过 API 账单,而且值班不用你扛。
  • 严格的数据驻留或离线网络要求 → 自建,但要规划住宅出口和一位维护者。
  • 非 YouTube 网站 → yt-dlp 的覆盖面无可匹敌;托管 API 通常以 YouTube 为主。
  • 需要在一份契约下同时拿到转录、元数据和媒体 → 托管(Tunelio 用一个 key 提供 /info、/create 和 /transcript)。

价格的常识检验

自建看起来免费,直到你把各部分加起来:几核的 VPS、按 GB 计费的住宅代理套餐(视频的每个字节都经过它)、凌晨三点更新 yt-dlp 的人,以及故障期间流失的用户。托管定价按请求计:Tunelio 对 /info 或 /transcript 收 6 个额度、/create 收 10 个额度,注册送 100 个免费额度,付费套餐每月 9 美元起含 100,000 个额度。选择之前请按自己的量算一算。

来源

  • yt-dlp README —— “Embedding yt-dlp”、网络与格式选项。
  • 社区封装 —— yt-dlp-web-ui、MeTube(GitHub),作为参考实现。
  • 我们自己的测量(Tunelio 提取集群,2026):每次提取的 CPU、数据中心 IP 首次接触的挑战率、每次提取的代理字节数。

常见问题

yt-dlp 有官方 API 吗?

没有。yt-dlp 是 Python 库和 CLI。任何 REST API 要么是你自己的封装,要么是社区项目,要么是替你运行类 yt-dlp 提取的托管服务。

能把 yt-dlp 的 URL 给我的用户吗?

不可靠:googlevideo URL 针对你服务器的 IP 和客户端签名,约 6 小时后过期。让媒体经你的服务器流出,或使用提供对用户安全的签名链接的托管 API。

一台服务器每秒能处理多少请求?

大约每核每秒一次提取,外加 ffmpeg 和带宽 —— 在 IP 信誉限制你之前。缓存结果并限制并发。

什么时候自建更好?

低流量、内部使用、非 YouTube 网站,或严格的数据驻留规则。对规模化的面向用户产品,代理和维护成本通常让托管 API 更便宜。

相关指南