Video API Proxy
🔒 Nội bộ · Dùng ngay · Không cần setup

Video API Proxy

Proxy nội bộ cho các app AI video. App chỉ gọi 1 URL kèm token — key vendor (DashScope, WaveSpeed) nằm an toàn trên Cloudflare Worker, không lộ trong binary.

BASE URL https://aivideo-proxy.manhpd-football.workers.dev

Tổng quan

Một endpoint duy nhất cho toàn bộ chức năng sinh video.

Hệ thống gồm 6 chức năng gen video qua 3 vendor. DashScope & WaveSpeed đi qua Worker proxy (prefix /ds, /ws); Ufoto app gọi thẳng (tự ký sign).

Chức năngVendorRoute
Text → VideoDashScope/ds
Image → Video (+lip-sync)DashScope/ds
Face Swap (video)WaveSpeed/ws
Template Face FusionUfototrực tiếp
AI Effect (beatImage2Video)Ufototrực tiếp
AI Live Photo / Multi FaceUfototrực tiếp

Luồng chung mọi chức năng: submit job → nhận task_id/URL → poll mỗi 3s tới khi có video hoặc lỗi (tối đa ~10 phút) → tải video về.

Dùng ngay

3 bước là chạy, không cần Firebase/SDK.

1) Health check (không cần token)

bash
curl https://aivideo-proxy.manhpd-football.workers.dev/health
# → ok

2) Submit 1 job Text→Video

bash
curl -X POST \
  https://aivideo-proxy.manhpd-football.workers.dev/ds/api/v1/services/aigc/video-generation/video-synthesis \
  -H "X-App-Token: " \
  -H "Content-Type: application/json" \
  -H "X-DashScope-Async: enable" \
  -d '{"model":"wan2.6-t2v","input":{"prompt":"a cat on the beach"},"parameters":{"size":"1280*720","duration":5}}'

3) Poll tới khi xong

bash
curl https://aivideo-proxy.manhpd-football.workers.dev/ds/api/v1/tasks/{task_id} \
  -H "X-App-Token: "
# output.task_status: PENDING → RUNNING → SUCCEEDED (kèm output.video_url)

Kiến trúc

App → Worker → Vendor. Key thật chỉ nằm ở Worker.

📱 App──(X-App-Token)──▶ ☁️ Cloudflare Worker──(Bearer key)──▶ 🎬 DashScope / WaveSpeed
PrefixUpstream thậtAuth Worker gắn
/ds/*dashscope-intl.aliyuncs.comBearer ALI_KEY
/ws/*api.wavespeed.aiBearer WSK
Worker tự strip header nhận diện (authorization, cf-*, x-forwarded-*) trước khi chuyển tiếp, và viết lại URL poll của WaveSpeed để nó vẫn đi qua proxy.

Ufoto không qua Worker — app gọi thẳng cpi.ufotosoft.com / face-api.ufotosoft.com và tự ký sign (xem mục Ký request).

Xác thực

Mọi request tới proxy phải mang header token.

HeaderGiá trị
X-App-Tokenbắt buộc

Thiếu hoặc sai token → 401 Unauthorized. Riêng /health không cần token.

Token này chỉ mở đúng Worker nội bộ này. Có thể xoay bằng wrangler secret put APP_PROXY_TOKEN rồi cập nhật lại app (xem mục Tự dựng Worker).

🧪 Test thật

Gõ prompt → tạo video thật qua proxy → xem kết quả ngay tại đây.

Ảnh/video của bạn được upload lên CDN vendor để xử lý. Mỗi lần chạy tiêu tốn quota thật; video có thể mất 1–5 phút. Trang tự poll mỗi 3s.

Text → Video

Sinh video từ prompt bằng model wan2.6-t2v.

POST/ds/api/v1/services/aigc/video-generation/video-synthesis

Header bắt buộc: X-App-Token, Content-Type: application/json, X-DashScope-Async: enable.

Body

FieldKiểuMô tả
modelstringwan2.6-t2v
input.promptstringMô tả video bắt buộc
parameters.sizestringvd 1280*720
parameters.durationintsố giây, vd 5
json · request
{
  "model": "wan2.6-t2v",
  "input": { "prompt": "a cat walking on the beach at sunset" },
  "parameters": { "size": "1280*720", "duration": 5 }
}
json · response
{
  "output": { "task_id": "06ad4c22-...", "task_status": "PENDING" }
}

Lấy output.task_id rồi chuyển sang Poll task.

Image → Video

Sinh video từ ảnh (kèm prompt & audio lip-sync tùy chọn) bằng wan2.6-i2v-flash.

POST/ds/api/v1/services/aigc/video-generation/video-synthesis

Ảnh/audio phải là URL công khai. Nếu là file local → upload trước qua WaveSpeed Upload để lấy CDN URL.

FieldKiểuMô tả
modelstringwan2.6-i2v-flash
input.img_urlstringURL ảnh bắt buộc
input.promptstringtùy chọn
input.audio_urlstringlip-sync tùy chọn
parameters.resolutionstringvd 720P
parameters.durationintvd 5
json · request
{
  "model": "wan2.6-i2v-flash",
  "input": {
    "img_url": "https://cdn.../face.jpg",
    "prompt": "gentle smile, cinematic",
    "audio_url": "https://cdn.../voice.mp3"
  },
  "parameters": { "resolution": "720P", "duration": 5 }
}

Poll task (DashScope)

Dùng chung cho Text→Video và Image→Video.

GET/ds/api/v1/tasks/{task_id}

Chỉ cần header X-App-Token. Gọi lại mỗi 3 giây.

task_statusÝ nghĩa
PENDING RUNNINGđang xử lý → poll tiếp
SUCCEEDEDxong → output.video_url
FAILEDlỗi → output.code / message
json · response (SUCCEEDED)
{
  "output": {
    "task_status": "SUCCEEDED",
    "video_url": "https://.../result.mp4"
  }
}

Upload file (WaveSpeed)

Đưa file local lên CDN để lấy URL dùng cho i2v / face swap.

POST/ws/api/v3/media/upload/binary?ext={ext}

Gửi raw bytes trong body. ?ext= giúp server nhận đúng định dạng (vd jpg, mp4, mp3). Header Content-Type theo loại file.

json · response
{ "data": { "download_url": "https://cdn.wavespeed.ai/..." } }

Face Swap (WaveSpeed)

Ghép mặt vào video.

POST/ws/api/v3/wavespeed-ai/video-face-swap

Body JSON gồm URL video + URL ảnh mặt (đã upload). Trả về URL để poll.

json · request
{ "video": "https://cdn.../src.mp4", "face_image": "https://cdn.../face.jpg" }
json · response
{ "data": { "urls": { "get": "https://aivideo-proxy.../ws/..." } } }

Poll

GET vào URL data.urls.get (kèm X-App-Token) mỗi 3s:

data.statusÝ nghĩa
created processingpoll tiếp
completedxong → data.outputs[0]
failedlỗi → data.error

Ufoto — Ký request

Các chức năng Template/Effect/Live gọi thẳng Ufoto, không qua proxy.

Mỗi request gắn bộ header sau:

HeaderGiá trị
cpins.story.unfold
version3.35.876 (fusion) · 876 (effect/live)
platform1
timeStampepoch giây, vd 1754900000
signmd5("IZCU5z" + timeStamp) — hex thường
userId16 ký tự hex ngẫu nhiên (giữ cố định/thiết bị)
Hosts: cpi.ufotosoft.com (upload/effect/live) · face-api.ufotosoft.com (fusion). Server thực tế không verify sign/userId nhưng vẫn nên gửi đúng.

Ufoto — Upload face

POSTface-api.ufotosoft.com/file/style/multipleUpload?cp&platform

Multipart field files (số nhiều). Filename phải unique (UUID) — nếu trùng, CDN dedup trả về ảnh cũ → lỗi "no face".

json · response
{ "c": 200, "d": [ "https://.../face.jpeg" ] }

Ufoto — Face Fusion (template & multi-face)

POSTface-api.ufotosoft.com/style/v1/videoFaceFusion

Query: cp, platform, projectId, modelId, templateId, imageUrls, level=0. Multi-face: lặp nhiều imageUrls theo thứ tự slot.

json · response
{ "c": 200, "d": { "jobId": "..." } }

Poll — POST /style/v1/queryVideoFaceFusion?cp&platform&jobId

json · response (xong)
{ "c": 200, "d": { "videoFaceFusionOutput": { "videoUrl": "https://.../out.mp4" } } }

c==200 mà chưa có videoUrl → còn render, poll tiếp. c==1022 → jobId chưa đăng ký, poll tiếp.

Ufoto — AI Effect (beatImage2Video)

POSTcpi.ufotosoft.com/algo/v1/faceFusion/aigc/beatImage2Video/createNormalTask?ifWise=true

Chạy trên version=876. Body JSON:

json · request
{ "imageUrls": ["https://.../face.jpg"], "level": 0, "effectType": 12 }

Trả d.taskId. Poll: POST .../beatImage2Video/queryTask?ifWise=true body {"taskId":"..."}d.taskStatus == "success" + d.videoUrl.

Ufoto — AI Live Photo (livePortrait)

POSTcpi.ufotosoft.com/algo/v1/faceFusion/aigc/livePortraitCreate?ifWise=true
json · request
{ "imageUrls": ["https://.../face.jpg"], "modelId": "<song id>", "ifFace": true, "level": 0 }

Trả d.taskId. Poll: POST .../livePortraitQuery?ifWise=true body {"taskId":"..."}d.taskStatus == "success" + d.videoUrl.

Code mẫu

Gọi Text→Video + poll.

Swift (async/await)

swift
let base = "https://aivideo-proxy.manhpd-football.workers.dev"
let token = ""

func submitT2V(prompt: String) async throws -> String {
  var req = URLRequest(url: URL(string: base + "/ds/api/v1/services/aigc/video-generation/video-synthesis")!)
  req.httpMethod = "POST"
  req.setValue(token, forHTTPHeaderField: "X-App-Token")
  req.setValue("application/json", forHTTPHeaderField: "Content-Type")
  req.setValue("enable", forHTTPHeaderField: "X-DashScope-Async")
  req.httpBody = try JSONSerialization.data(withJSONObject: [
    "model": "wan2.6-t2v",
    "input": ["prompt": prompt],
    "parameters": ["size": "1280*720", "duration": 5]
  ])
  let (data, _) = try await URLSession.shared.data(for: req)
  let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
  return ((json["output"] as! [String:Any])["task_id"] as! String)
}

cURL

bash
curl -X POST "$BASE/ds/api/v1/services/aigc/video-generation/video-synthesis" \
  -H "X-App-Token: $TOKEN" -H "Content-Type: application/json" \
  -H "X-DashScope-Async: enable" \
  -d '{"model":"wan2.6-t2v","input":{"prompt":"..."},"parameters":{"size":"1280*720","duration":5}}'

Polling

Chiến lược chờ kết quả.

Tham sốGiá trị
Khoảng cách3 giây
Số lần tối đa200 (~10 phút)
Lần poll đầuchờ 3s trước khi poll (để job kịp đăng ký)

Chỉ dừng khi đạt trạng thái terminal (thành công/thất bại) hoặc chạm giới hạn thời gian.

Mã lỗi

Proxy / DashScope

Ý nghĩa
401Sai/thiếu X-App-Token
404Prefix không phải /ds hay /ws
InvalidApiKey / ArrearageKey vendor hỏng/hết tiền → xoay key trên Worker

Ufoto (field c)

cÝ nghĩa
1022jobId chưa đăng ký — poll tiếp
1011Không phát hiện khuôn mặt
2002Không đủ credit
5000Ảnh bị từ chối

Tự dựng Worker

Khi cần proxy riêng (đổi tài khoản, xoay key).

bash
# trong thư mục proxy-worker/
npx wrangler login
npx wrangler secret put ALI_KEY           # key DashScope
npx wrangler secret put WSK               # key WaveSpeed
npx wrangler secret put APP_PROXY_TOKEN   # token tự đặt
npx wrangler deploy                       # in ra URL Worker

Sau đó cập nhật BASE URL + X-App-Token trong app. Xoay key: chạy lại secret put + deploy, không cần build lại app (trừ khi đổi token).

Key vendor hiện tại là của bên thứ ba lấy từ Remote Config — có thể bị thu hồi bất cứ lúc nào. Sản phẩm chính thức nên dùng tài khoản DashScope + WaveSpeed riêng.
🎬 Video API Proxy · Tài liệu nội bộ · Cập nhật giá trị token/URL khi xoay key.