最近在工作上有大量的自媒体内容分析的任务,包括AI 爆款分析、AI 内容质检等。但是,所有分析的前期,是批量下载 B 站、YouTube、抖音、快手等平台的视频或音频。
可是,每个平台的架构不同,在进行不同平台音视频下载,都要找不同的脚本、插件,甚至还要手动切换环境、改参数。
基本上,每次换个平台,都是一场“折腾之旅”。
脚本东拼西凑,环境一乱就报错,想自动化更是难上加难。于是打算通过 Cursor 辅助,要做一个“万能视频下载工厂”——我只需要提供一个链接和平台名,剩下的交给 API 自动搞定。
API 化能让团队、AI 工具(比如 Dify)都能一键调用。
这不仅仅是“省事”,更是一次工程化思维的升级。经过 2 天时间的折腾,把整个过程完整的走通了,特地开一篇文章做记录。
和 AI 协作,做项目,最怕“想到哪做到哪”。第一步,先把需求梳理清楚。
- 支持多平台:B 站、YouTube、抖音、快手,甚至本地文件。
- API 化:不是写死在脚本里,而是做成标准接口,方便对接各种系统。
- 可扩展:以后想加新平台,不能推倒重来。
- 易部署:最好一条命令就能跑起来,别再为环境折腾。
方案选型时,选择了 Python(生态好,爬虫和音视频处理强),FastAPI(写 API 超快),Docker(环境一致,部署无忧),再加上自动化测试兜底。
目标很明确:让“下载”变成一项标准服务,像自来水一样随取随用。
技术选型代码片段:
from fastapi import FastAPIapp = FastAPI()
一开始,我的代码就是一堆“能用就行”的脚本。
每个平台一个 py 文件,参数全靠手动改,想测哪个就注释哪个。但很快我发现,这样下去根本没法维护,更别说扩展和自动化了。于是我决定重构:
- 抽象出一个 Downloader 基类,把通用逻辑提取出来。
Downloader 基类(base.py)
from abc import ABC, abstractmethodclass Downloader(ABC): @abstractmethod def download(self, url: str): pass
- 每个平台写一个子类,实现自己的下载细节。
Bilibili 下载器示例(bilibili_downloader.py)
from base import Downloaderclass BilibiliDownloader(Downloader): def download(self, url: str): # 伪代码:实际用 bilibili-api 或 you-get/youtube-dl video\_path = ... audio\_path = ... return {"video\_path": video\_path, "audio\_path": audio\_path}
- 用工厂模式统一管理,想加新平台只需加一个类。
工厂模式(downloader_factory.py)
def get\_downloader(platform: str): if platform == "bilibili": return BilibiliDownloader() # 其他平台...
重构后的代码像搭积木一样清晰,每个下载器都能独立测试、独立维护。
从“脚本乱麻”到“模块积木”,这一步让我体会到了工程化的力量。
有了模块化的代码,下一步就是让它们“靠谱”——写自动化测试。
我让 AI写了一个测试脚本,可以一键测试所有下载器,也能指定只测某个平台。
每次改完代码,跑一遍测试,心里就有底了。测试过程中也遇到不少坑,比如依赖库缺失、Cookie 配置、环境变量加载等。
但正是这些“红叉叉”,让我及时发现并修复了问题,避免了“上线即翻车”。测试不是负担,而是让你大胆重构的底气。现在每次加新功能,我都敢放心大胆地动手,因为有测试兜底。
测试脚本(test_downloaders.py)
import argparsefrom downloader\_factory import get\_downloaderdef test\_downloader(platform, url): downloader = get\_downloader(platform) result = downloader.download(url) assert result["video\_path"] and result["audio\_path"]if \_\_name\_\_ == "\_\_main\_\_": parser = argparse.ArgumentParser() parser.add\_argument("--downloader", type=str, help="指定下载器") args = parser.parse\_args() # 示例:python test\_downloaders.py --downloader bilibili test\_downloader(args.downloader, "https://www.bilibili.com/video/BV1xxxxxxx")
.env 文件配置(用于抖音/快手 Cookie)
DOUYIN\_COOKIE="your\_douyin\_cookie\_here"KUAISHOU\_COOKIE="your\_kuaishou\_cookie\_here"
脚本再好,也只能自己用。
要让“下载”变成一个服务,让任何人、任何系统都能一键调用。接下来,用 FastAPI 封装了 RESTful 接口,只需 POST 一个链接和平台名,就能返回音视频的下载地址。
更妙的是,AI 还用 FastAPI 的静态文件服务,把下载好的文件直接映射成可访问的 URL。
这样,外部系统(比如 Dify、前端页面)只需拿到链接就能直接下载,无需关心文件存储细节。
API 是能力的放大器,让工具变成别人的服务。这一步,让万能下载工厂”真正具备了“对外赋能”的能力。
FastAPI 主接口(api.py)
from fastapi import FastAPI, HTTPExceptionfrom fastapi.responses import FileResponsefrom downloader\_factory import get\_downloaderapp = FastAPI()@app.post("/download")def download\_video(data: dict): url = data["url"] platform = data["platform"] downloader = get\_downloader(platform) result = downloader.download(url) return { "video\_path": f"/files/{result['video\_path']}", "audio\_path": f"/files/{result['audio\_path']}" }# 静态文件服务from fastapi.staticfiles import StaticFilesapp.mount("/files", StaticFiles(directory="downloaded\_files"), name="files")
请求示例(curl)
curl -X POST http://127.0.0.1:8000/download \ -H "Content-Type: application/json" \ -d '{"url": "https://www.bilibili.com/video/BV1xxxxxxx", "platform": "bilibili"}'
开发环境能跑,不代表生产环境就没问题。依赖、版本、系统环境,分分钟让你“崩溃”。
为了解决“环境地狱”,我用 Docker 把整个项目打包成镜像。
只需一条命令,就能在任何地方一键启动服务。我还用 .dockerignore 排除了不必要的文件,让镜像更小更快。为了方便本地调试和文件管理,我用 -v 参数把容器里的下载目录挂载到本地,所有下载的音视频都能实时同步到电脑上。
Docker 让部署变得像复制粘贴一样简单。这一步,让我的服务从“能用”变成了“随时可用”。
Dockerfile
FROM python:3.10-slimWORKDIR /appRUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*COPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txtCOPY . .EXPOSE 8000CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]
.dockerignore
\_\_pycache\_\_/*.pyc.envtest\_downloaders.logdata/temp\_test\_data/downloaded\_files/
Docker 部署完成后,可以在 Docker 容器内看到 video-downloader 服务已经起来了。
点开 prots 看一下,看到网页响应的 message 是 "API is running!"代表这项服务已经通过 Docker 启用了!
当我把下载服务和 Dify 都用 Docker 跑起来后,发现 Dify 的 HTTP 节点死活访问不到 127.0.0.1:8000。
原来,Docker 容器里的 127.0.0.1 只代表自己,地址需要映射成:
http://host.docker.internal:8000/download
立刻就通了!网络互通,是微服务世界的“最后一公里”。这一步,让我的服务真正实现了“系统级联动”,为后续更多自动化场景打下了基础。
完成 Dify 工作流搭建后,直接请求 API 服务,完成了音频和视频的下载。这里的地址其实是我本地可访问的地址。
看一下效果:音频访问 ☑️
视频访问☑️
这次项目还有一个最大亮点:AI 助手Cursor全程陪跑。
从需求分析、代码重构、测试调试、Docker 打包,到文档撰写、网络排障,AI 都能给出专业建议和详细操作步骤。遇到报错时,AI 能帮我快速定位问题、给出修复建议;
有了 AI 的陪跑,开发过程无比丝滑。AI 不是替代者,而是最强的开发搭子。2 天时间完成一个稳定应用 API 服务发布,这在以前,是根本不敢想的!