agent-download 是一个异步视频下载服务:提交视频 URL(v1 支持 YouTube,douyin/微信视频号/小红书已预留接口),服务在云端容器内完成下载,视频存入 R2,完成后 48 小时自动删除。
Base URL:https://agent-download.kukukala.com
认证
所有 /api/* 请求需要鉴权,两种凭证任一通过即可:
| 凭证 | 方式 | 适用 |
|---|---|---|
x-auth-token 头 | x-auth-token: <API_TOKEN> | 程序调用一律用这个,长期有效。Token 在控制台「API Token」按钮查看。 |
| UI session cookie | POST /ui/login(表单 user/pass)签发,12 小时有效 | 仅供浏览器控制台使用。 |
缺失/错误返回 401 {"error":"UNAUTHORIZED"}。登录连续失败 5 次锁 15 分钟。无鉴权路径:/doc、/login.html、POST /ui/login、/file/*(用独立文件 token,见下)。
端点总览
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /api/download | 提交下载(单个或批量,异步建任务) |
| GET | /api/job/:id | 查询任务状态/进度/下载地址 |
| GET | /api/status | 容量/频道池/实例/最近任务总览 |
| GET | /api/settings | 读取运行时设置 |
| PUT | /api/settings | 修改运行时设置(仅对新任务生效) |
| GET | /api/token | 查看 API Token(仅 cookie 会话) |
| GET | /file/:jobId?token=… | 下载成品视频(支持 Range) |
POST /api/download — 提交下载
立即返回任务列表(异步执行);用 /api/job/:id 轮询结果。
参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 与 urls 二选一 | 单个视频 URL |
urls | string[] | 与 url 二选一 | 批量 URL,上限 20 个,服务并行调度 |
resolution | string | 否 | 360/480/720/1080/2160/max 或纯数字,默认 1080。请求高度拿不到时下载最接近的可用高度 |
请求示例
curl -X POST https://agent-download.kukukala.com/api/download \
-H "x-auth-token: $TOKEN" -H "Content-Type: application/json" \
-d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","resolution":"1080p"}'
响应示例
{ "jobs": [ { "jobId": "j_a1b2c3d4", "status": "queued", "platform": "youtube" } ] }
批量且含无效 URL 时逐个处理,无效项原样带回:
{ "jobs": [
{ "jobId": "j_a1b2c3d4", "status": "queued", "platform": "youtube" },
{ "url": "https://v.douyin.com/xxx", "rejected": true, "errorCode": "platform_not_supported" }
] }
jobId,不重复建任务。只接受单视频 URL,播放列表/频道/直播地址返回 unsupported_url。成品上限 2GB(服务端可调)。GET /api/job/:id — 查询任务
curl -H "x-auth-token: $TOKEN" https://agent-download.kukukala.com/api/job/j_a1b2c3d4
{
"jobId": "j_a1b2c3d4", "platform": "youtube", "status": "done", "progress": 100,
"title": "…", "sizeBytes": 104857600, "durationS": 245.3,
"error": null,
"downloadUrl": "/file/j_a1b2c3d4?token=…",
"expiresAt": 1759286400000, "attempt": 1
}
状态机:queued → dispatched → downloading → uploading → done | failed,另有 expired(48h 到期)。progress 为 0–1 浮点。error 形如 {"code":"youtube_blocked","message":"…","retryable":true}(见错误码表)。downloadUrl 仅 done 时返回,随查询实时签发、48h 内有效。
GET /api/status — 服务状态
curl -H "x-auth-token: $TOKEN" https://agent-download.kukukala.com/api/status
{
"capacity": { "maxInstances": 1, "jobsPerInstance": 2, "globalInflightCap": 2,
"inflight": 2, "queued": 3,
"note": "预算值(软约束):实际并发由派发闸控制,实例布局由 FC 决定" },
"channels": { "youtube": { "target": 5, "current": 5, "inflight": 2, "queued": 3, "recentFailRate": 0.1 } },
"instances": [ { "instanceId": "i_x", "startedAt": 1759200000000, "lastHeartbeatAt": 1759200090000, "activeJobs": ["j_a1b2c3d4"] } ],
"jobs": [ /* 最近 50 条,元素同 /api/job 响应 */ ]
}
实例 90 秒内心跳视为存活;current 是频道并发池的当前上限(服务按失败自动降半、成功回升,介于 1 与 target 之间)。
GET / PUT /api/settings — 运行时设置
curl -X PUT https://agent-download.kukukala.com/api/settings \
-H "x-auth-token: $TOKEN" -H "Content-Type: application/json" \
-d '{"maxInstances": 2, "jobsPerInstance": 2, "channelLimits": {"youtube": {"target": 3}}}'
| 字段 | 默认 | 说明 |
|---|---|---|
maxInstances | 1 | 实例预算(1–5) |
jobsPerInstance | 2 | 每实例并发预算(1–4);全局并发上限 = 两者乘积 |
channelLimits.平台.target | youtube: 5 | 频道池并发上限(1–10),先试高并发,失败自动降 |
GET /file/:jobId?token=… — 下载成品
downloadUrl 自带签名 token,无需再带 x-auth-token。支持 HEAD、Range 断点(206/416)、Content-Length,可直接喂给播放器。
# 整段下载
curl -OJ "https://agent-download.kukukala.com/file/j_a1b2c3d4?token=…"
# 断点续传(Range)
curl -H "Range: bytes=1048576-" -o part.mp4 "https://agent-download.kukukala.com/file/j_a1b2c3d4?token=…"
生命周期
48 小时计时从任务完成时开始(不是提交时)。到期后:410 Gone;URL token 失效同样 410;任务不存在 404。已开始的下载不会被主动中断,但过期后无法发起新请求。
典型调用时序(提交 + 轮询 + 下载)
TOKEN="your-api-token"; BASE="https://agent-download.kukukala.com"
JOB=$(curl -s -X POST $BASE/api/download -H "x-auth-token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.youtube.com/watch?v=…","resolution":"1080p"}' \
| python3 -c 'import json,sys;print(json.load(sys.stdin)["jobs"][0]["jobId"])')
until [ "$(curl -s -H "x-auth-token: $TOKEN" $BASE/api/job/$JOB \
| python3 -c 'import json,sys;print(json.load(sys.stdin)["status"])')" = "done" ]; do
sleep 5
done
curl -s -H "x-auth-token: $TOKEN" $BASE/api/job/$JOB \
| python3 -c 'import json,sys;print(json.load(sys.stdin)["downloadUrl"])'
# 拿到 /file/... 相对 URL,拼上 $BASE 直接下载
urls 数组一次提交,服务按频道池并发调度(池满时排队),逐个用返回的 jobId 轮询即可。错误码
| code | retryable | 含义 |
|---|---|---|
proxy_unreachable | 是 | 下载容器代理不可用(订阅节点故障) |
youtube_blocked | 是 | 源站风控/需要登录(IP 或客户端被拒) |
po_token_missing | 是 | 缺少 PO Token(YouTube 新风控,需升级下载程序) |
too_large | 是 | 超出成品/临时空间上限(默认 2GB) |
timeout | 是 | 下载超时(15 分钟无进度,自动重试一次) |
unsupported_url | 否 | 非单视频 URL(播放列表/频道/直播) |
platform_not_supported | 否 | 平台尚未开放(douyin/视频号/小红书预留中) |
permanent | 否 | 视频不存在/私有/已删除等永久失败 |
internal | 视情况 | 服务内部错误 |
retryable=true 的失败会触发服务端自动重试(attempt+1,换执行目录重下);触发并发自适应降档。重试仍失败则终态 failed,重新提交即可。
平台支持
| 平台 | 状态 | 域名示例 |
|---|---|---|
| YouTube | ✅ 可用 | youtube.com/watch、youtu.be/… |
| 抖音 | ⏳ 预留 | douyin.com、v.douyin.com |
| 微信视频号 | ⏳ 预留 | — |
| 小红书 | ⏳ 预留 | xiaohongshu.com |
预留平台返回 platform_not_supported,接口形状(提交/轮询/下载)与 YouTube 完全一致——后续开放只是服务端解锁,调用方无需改动。