agent-download 接口文档

← 返回控制台

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 cookiePOST /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 轮询结果。

参数

字段类型必填说明
urlstring与 urls 二选一单个视频 URL
urlsstring[]与 url 二选一批量 URL,上限 20 个,服务并行调度
resolutionstring否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" }
] }
幂等:同一 URL 存在未完成任务(排队/下载/上传中)时直接返回原 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 决定" },
  "stats": { "youtube": { "attempts24h": 12, "successes24h": 11, "lastAttemptAt": …, "lastSuccessAt": … } },
  "supportedPlatforms": ["youtube"],
  "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 之间)。stats 为 24h 尝试/成功(rollup 历史 + 存活任务双源合并)与全时段最后成功时点。

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}}}'
字段默认说明
maxInstances1实例预算(1–5)
jobsPerInstance2每实例并发预算(1–4);全局并发上限 = 两者乘积
channelLimits.平台.targetyoutube: 5频道池并发上限(1–10),先试高并发,失败自动降
设置修改仅对新派发的任务生效,进行中的任务不受影响。PUT 为部分更新,只传要改的字段。

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。已开始的下载不会被主动中断,但过期后无法发起新请求。

渠道健康与 AI 自愈(spec §7.2–§7.4)

GET /api/health — 渠道健康总览

curl -H "x-auth-token: $TOKEN" $BASE/api/health
{ "platforms": { "youtube": {
  "healthy": "unknown",          // healthy|unhealthy(连败中)|unknown(未探测)
  "lastProbeAt": 1759200000000, "lastResult": { "status": "failed", "errorCode": "youtube_blocked", "at": … },
  "failStreak": 3,               // channel 类连败(探针失败累加、成功清零)
  "infraFails": 0, "unknownCodes": 0,
  "sampleStale": false,          // true=样本失效(token 过期/视频删除),连败与修复触发冻结
  "urls": ["https://www.youtube.com/watch?v=…"],   // 样本 URL
  "expected": { "titleKeywords": […], "descriptionKeywords": […], "durationSRange": [60,300],
                "coverSha256": "…", "ordShas": […], "imageCount": 3 }   // sha 基准由首探成功自动写回(B5),只读
} } }

POST /api/health/:platform — 手动探针

用该渠道当前样本 URL 建一个 kind=probe 任务并立即补派。同渠道同时至多 1 个活跃探针(409 probe_active);未配样本 409 no_sample_url。每周 cron 自动全渠道巡检一轮。

PUT /api/health/samples — 样本与预期基准编辑

curl -X PUT $BASE/api/health/samples -H "x-auth-token: $TOKEN" -H "Content-Type: application/json" \
  -d '{"platform":"youtube","urls":["https://www.youtube.com/watch?v=…"],
       "expected":{"titleKeywords":["关键词"],"descriptionKeywords":["正文词"],"durationSRange":[60,300]}}'
URL 须在该平台域名白名单内(400 bad_sample_url);coverSha256/imageCount/ordShas 为自动基准不接受手填(400 expected_readonly)。换样本 URL 会清空既有 sha 基准并复位 sampleStale——新样本首次探针成功后自动重记。

GET /api/repairs · POST /api/repair/:platform · POST /api/repair/:id/rollback · GET /api/repair/:id/log

端点说明
GET /api/repairs最近 50 条修复(id/状态/trigger/retry_of 链/基线→候选 sha/错误)
POST /api/repair/:platform手动派发修复(限速:每渠道 1 次/小时、3 次/天,429 repair_rate_limited)
POST /api/repair/r_xxx/rollback回滚到该修复的 base bundle(仅当前指针仍是其候选时;否则 409 rollback_stale)
GET /api/repair/r_xxx/logcodex 执行日志(已脱敏:token/GLM key/签名 URL 打码)

修复生命周期:pending → running(排他领取)→ published|failed|expired|conflicted。自动触发条件:探针 channel 类连败 ≥3 且样本未失效(R1)。conflicted(发布时基线已被人移走)不立即重派——下次触发时自动以 retry_of 挂链新派一次。

POST /internal/repair/:id/:action — FC 侧回调(内部)

claim(排他领取,REPAIR_TOKEN)/ candidate(候选 zip,内容寻址封存)/ verify(验证结果与 CAS 发布,VERIFY_TOKEN 独立凭据,repair token 调它 401)/ patch(diff 留痕)/ failed / log。仅限 FC 容器回调,不走 API token。

探针错误分类(N9,决定连败/触发)

类错误码效果
channeldouyin_parse_failed douyin_risk_control xhs_risk_control xhs_parse_failed youtube_blocked po_token_missing permanent连败 +1;连败 ≥3 且样本未失效 → 自动派修复
sampleyoutube_video_unavailable xhs_token_expired不计连败,置 sampleStale(冻结触发),UI 提示换样本
infraproxy_unreachable timeout internal dispatch_failed probe_failed只计 infra 计数;未知新码保守按 infra 并告警

典型调用时序(提交 + 轮询 + 下载)

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 直接下载
批量并行:直接把多个 URL 放进 urls 数组一次提交,服务按频道池并发调度(池满时排队),逐个用返回的 jobId 轮询即可。

错误码

coderetryable含义
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 完全一致——后续开放只是服务端解锁,调用方无需改动。