Appearance
抖音视频下载 API
基于 FastAPI + Playwright + hellotik.app 的抖音视频无水印下载服务,支持多画质自动缓存与 OneDrive 备份。
在线调试
输入你的 Token 并选择端点进行测试。
架构
用户 → api-dy.opccii.com → Cloudflare(CDN+SSL) → Nginx → uvicorn(127.0.0.1:8000)
│
┌──────────┴──────────┐
│ Worker │
│ ─ 缓存维护 │
│ ─ 健康检查 │
│ ─ OneDrive 备份 │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ MySQL │
│ videos / links │
│ backups / ip_records│
└─────────────────────┘端点一览
| 方法 | 路径 | 说明 | 鉴权 |
|---|---|---|---|
| GET | /api/random | 从缓存池随机返回一个视频 | Token |
| GET | /api/status | 缓存池状态 | Token |
| POST | /api/submit | 提交抖音链接加入队列 | Token |
| POST | /api/warmup | 立即爬取一个视频,阻塞返回 | Token |
| GET | /api/admin/videos | 视频列表(支持筛选/分页) | Admin Token |
| POST | /api/admin/videos | 手动新增视频 | Admin Token |
| DELETE | /api/admin/videos/{id} | 删除视频 | Admin Token |
| POST | /api/admin/videos/{id}/retry | 重试爬取 | Admin Token |
| POST | /api/admin/videos/batch | 批量导入链接 | Admin Token |
| GET | /api/admin/stats | 统计面板 | Admin Token |
公共接口
随机获取视频
GET /api/random
从缓存池随机返回一个已就绪的视频。
成功响应:
json
{
"ok": true,
"video": {
"id": 13,
"title": "和小狗一起! #抖音潮流舞蹈大赛# dancechallenge #潮流舞蹈挑战",
"url": "https://v11-default.365yg.com/064d44c548b7144d05630a771b79ca79/...",
"quality": "超高清",
"fallback": {
"720p": "https://v26-default.365yg.com/...",
"540p": "https://v26-default.365yg.com/..."
}
},
"cache_status": { "ready": 6, "target": 10 }
}错误响应:
json
{
"ok": false,
"error": "no_cache",
"hint": "缓存池为空,请先提交视频或等待爬取完成",
"cache_status": { "ready": 0, "target": 10 }
}查询缓存状态
GET /api/status
查询当前缓存池状态。
响应示例:
json
{ "ready": 6, "target": 10, "total": 15, "failed": 2, "deleted": 1, "expired": 0 }提交链接
POST /api/submit
提交抖音分享链接加入爬取队列。链接自动去重,重复提交返回已有 ID。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
share_url | string | 是 | 抖音分享链接 |
响应示例:
json
{ "ok": true, "message": "已加入队列", "id": 13 }立即爬取
POST /api/warmup
立即爬取指定视频,阻塞等待返回结果(最长 60s)。
可省略 share_url —— 此时从队列中取一个 pending 视频爬取;也可指定链接。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
share_url | string | 否 | 抖音分享链接(为空则从队列取) |
成功响应:
json
{
"ok": true,
"video": {
"id": 13,
"title": "和小狗一起! #抖音潮流舞蹈大赛# dancechallenge #潮流舞蹈挑战",
"url": "https://v11-default.365yg.com/...",
"quality": "超高清",
"fallback": {
"720p": "https://v26-default.365yg.com/...",
"540p": "https://v26-default.365yg.com/..."
}
},
"cache_status": { "ready": 6, "target": 10 }
}错误响应:
json
// 队列为空
{ "ok": false, "error": "no_pending", "hint": "暂无待爬取视频,请先提交链接" }
// 爬取超时
{ "ok": false, "error": "爬取超时,请稍后重试" }
// 解析失败(链接无效/视频已删除/hellotik 不支持)
{ "ok": false, "error": "解析返回空结果" }管理接口
需要 Admin Token 鉴权。
视频列表
GET /api/admin/videos
查看视频列表,支持状态筛选和分页。
查询参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
status | 全部 | 筛选:pending ready failed deleted expired |
page | 1 | 页码 |
size | 20 | 每页条数(最大 100) |
新增视频
POST /api/admin/videos
手动新增视频链接。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
share_url | string | 是 | 抖音分享链接 |
删除视频
DELETE /api/admin/videos/{id}
删除视频及关联数据。
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | integer | 视频 ID |
重试爬取
POST /api/admin/videos/{id}/retry
重置视频状态为 pending,触发重新爬取。
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | integer | 视频 ID |
批量导入
POST /api/admin/videos/batch
批量导入抖音链接。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
urls | string[] | 是 | 抖音分享链接数组 |
统计面板
GET /api/admin/stats
统计面板。
响应示例:
json
{
"total": 50,
"by_status": { "pending": 10, "ready": 30, "failed": 5, "deleted": 3, "expired": 2 },
"backup_rate": "30/30",
"total_size_bytes": 12884901888
}视频画质
爬虫自动采集所有可用画质并按优先级排序:
超高清 → 4K → 原画 → 1080p → 720p → 540p → 480p → 高清/api/random返回最高可用画质作为主链接,其余画质放入fallback- Worker 备份自动选择最高画质上传 OneDrive
缓存机制
| 项目 | 值 |
|---|---|
| 缓存目标 | 10 个就绪视频 |
| Worker 窗口 | 2 小时无请求自动停止 |
| 爬取间隔 | 30-60s(随机,防限流) |
| 链接健康检查 | 每 4 小时 |
Worker 行为
| 场景 | 行为 |
|---|---|
| 服务空闲 2h | Worker 自动停止,0 资源占用 |
| 新请求到达 | Worker 立即启动,刷新窗口计时 |
| ready < 10 | 从 pending 队列补充爬取 |
| 链接失效 | 标记 is_available=0,全失效则标记 expired |
| 新视频就绪 | 下载最高画质 → rclone 上传 OneDrive |
数据库
| 表 | 说明 |
|---|---|
videos | 视频主表(状态机:pending → ready/failed/deleted/expired) |
links | CDN 链接表(按画质拆分) |
backups | OneDrive 备份记录 |
ip_records | IP 鉴权记录(白名单/黑名单/失败计数) |
OneDrive 备份
Worker 自动执行:
视频 status=ready → 检测未备份 → get_best_link(最高画质)
→ 下载到 /tmp/douyin_backup/{id}_tmp.mp4
→ 重命名为 {aweme_id}.mp4
→ rclone move → onedrive:douyin_backup/{aweme_id}.mp4
→ 写入 backups 表错误码速查
| HTTP | 错误 | 原因 |
|---|---|---|
| 200 | no_cache | 缓存池无就绪视频 |
| 200 | no_pending | 队列无待爬取视频 |
| 200 | 解析返回空结果 | hellotik 无法解析该链接 |
| 200 | 爬取超时 | 单次爬取超 60s |
| 401 | 未授权 | 缺少 token 参数 |
| 401 | Token 无效 | token 错误(剩余尝试次数) |
| 403 | IP 已被封禁 | 连续 10 次鉴权失败 |
| 400 | 无效的抖音分享链接格式 | URL 格式不匹配 |