在一张 RTX 5090 上运行的 MiniMax-H3 视频生成服务。文生视频、图生视频(1 到 6 张图),输出 24 fps 带立体声的 mp4。任务在单卡上顺序执行,新任务自动排队。
| 公网 | https://desktop-lp9ii17.tail97a7d2.ts.net |
|---|---|
| 本机 | http://127.0.0.1:8000 |
所有 /api/* 请求都需要口令,三种传法任选一种:
| 推荐 | Authorization: Bearer <TOKEN> 请求头 |
|---|---|
| 备选 | x-token: <TOKEN> 请求头 |
| 备选 | ?token=<TOKEN> 查询参数 |
口令错误或缺失返回 401 {"detail":"bad token"}。口令保存在服务器的 webapp/config.json,修改后重启服务生效。已开启 CORS,浏览器页面可以直接跨域调用。
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/generate | 用 JSON 创建任务。文生视频,或图生视频(图片给 URL 或 base64)。可选同步等待、完成回调。 |
| POST | /api/jobs | 同上,multipart 表单版本,图片作为文件字段 images(可多个)。网页就是用的这个。 |
| POST | /api/v1/tts | 声音克隆:Qwen3-TTS 学一段参考人声,念给定文字,返回 wav 地址。也有 multipart 版 /api/tts。 |
| POST | /api/v1/image | Qwen-Image-2.1:用 JSON 创建文生图或改图任务(0 到 10 张参考图,URL 或 base64)。可选同步等待、完成回调。 |
| POST | /api/images | 同上,multipart 表单版本,图片作为文件字段 images。网页就是用的这个。 |
| GET | /api/jobs | 任务列表(视频和图片任务混排),最新在前。 |
| GET | /api/jobs/{id} | 单个任务。加 ?wait=true&timeout=600 会阻塞到完成或超时。 |
| GET | /api/jobs/{id}/video | 下载成品 mp4(H.264 + AAC 立体声 32 kHz)。 |
| GET | /api/jobs/{id}/image | 下载成品 png(图片任务)。 |
| DELETE | /api/jobs/{id} | 排队中:取消;生成中:中断;已完成:删除任务和文件。 |
| GET | /api/status | ComfyUI 是否在线、当前任务 id、排队数。 |
| GET | /api/config | 可用的模型方案、尺寸表、时长选项。 |
| GET | /out/{id}.mp4 · /out/{id}.png | 成品的静态地址(可直接放进 <video src> / <img src>,无需口令)。 |
POST /api/v1/generate请求体 JSON,所有字段除 prompt 外都可省略:
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
prompt | string | 必填 | 画面、镜头运动和声音的描述,中英文均可。引号里的台词会做口型同步。 |
profile | string | fasth3 | fasth3 画质最好(8 步)· turbo_int8 均衡(8 步)· fasth3_4step_int8 最快(4 步) |
aspect | string | 16:9 | 16:9 · 9:16 · 1:1 |
res | string | 480p | 352p · 480p · 768p,实际像素见下方尺寸表 |
seconds | number | 5 | 1 到 15 秒。帧数会向上取整到模型的 17k+5 帧网格(24 fps),例如 5 秒 → 124 帧 = 5.17 秒 |
seed | int | -1 | -1 表示随机。相同参数和 seed 可复现 |
steps | int | 0 | 0 表示用方案默认(8 或 4),一般不用改 |
images | string[] | [] | 0 到 6 张。每项是 https://… 图片地址,或 data:image/png;base64,…。用法由 image_mode 决定。关键帧模式:1 张 = 首帧;2 张 = 首帧 + 末帧;3 到 6 张 = 首帧、末帧,其余按顺序均匀锚定到时间轴,图片会被缩放裁切到目标画幅。参考模式:全部作为参考图 |
image_mode | string | keyframe | keyframe 图片钉在对应帧上,视频首帧就是第 1 张图 · reference 图片只提供人物 / 物体 / 风格,视频不从图片开始。提示词里用 <Picture 1>、<Picture 2> 指代各张图,不写会自动加在开头 |
audios | string[] | [] | 0 到 3 段音频(每段 2 到 15 秒),https://… 或 data:audio/wav;base64,…。用法由 audio_mode 决定 |
audio_mode | string | voice | voice 音色参考:H3 用这段声音的音色,自己念提示词里的台词,提示词里用 <Audio 1> 指代(不写会自动加上;带图片时自动切到参考图模式)· track 音轨:第 1 段音频原样作为视频音轨从第 0 帧铺满,画面对口型 |
tts_text | string | 无 | 填了就走克隆朗读:Qwen3-TTS 学 audios[0] 的音色念这段话,合成结果作为音轨对口型(等价于 track 模式)。可配 tts_ref_text(参考音频原文,可选)和 tts_language(Auto / Chinese / English …) |
wait | bool | false | true 时请求阻塞到任务完成再返回最终任务对象 |
timeout | int | 600 | wait 的最长等待秒数,上限 1800;超时返回当时的状态(任务继续跑) |
callback_url | string | 无 | 任务结束(成功或失败)后把任务对象 POST 到这个地址,JSON 格式 |
返回值就是任务对象;不带 wait 时立即返回,状态为 queued。
POST /api/jobs字段名与上表相同(prompt, profile, aspect, res, seconds, seed, steps),图片放在一个或多个 images 文件字段里。不支持 wait 和 callback_url,拿到 id 后用 GET /api/jobs/{id}?wait=true 等待即可。
POST /api/v1/imageQwen-Image-2.1(7B,2026-09-20 发布):不传图就是文生图;传 1 到 10 张图就是改图 / 多图参考,提示词里用 Picture 1、Picture 2 指代各张图(按传入顺序)。支持中英文文字渲染。返回值同样是任务对象,成品字段是 image。实测(25 步,模型已加载):qwen21 1K 约 4.5 秒、2K 约 27 秒;qwen21_bf16 1K 约 8 秒、2K 约 46 秒;单图改图 1K 约 7 秒;krea2_turbo 1K 约 3.3 秒、2K 约 18 秒,带 1 张风格参考约 8 秒。切换模型后的第一次调用多约 5 到 20 秒加载。
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
prompt | string | 必填 | 要画的画面;有图时是修改指令 |
negative_prompt | string | "" | 负面提示词,只在 cfg 大于 1 时生效 |
profile | string | qwen21 | qwen21 Qwen-Image-2.1 INT8(默认,文生图 + 改图)· qwen21_bf16 全精度,约慢一倍 · krea2_turbo Krea 2 Turbo 12B NVFP4,8 步蒸馏,偏艺术审美,文生图 + 风格参考(不能改图)· krea2_turbo_int8 同上 INT8 版 |
aspect | string | 1:1 | 1:1 · 16:9 · 9:16 · 4:3 · 3:4 |
res | string | 1K | 1K 约 100 万像素 · 2K 约 400 万像素(模型原生 2048²)。改图时参考图也按这个档缩放 |
size | string | 无 | 直接指定 宽x高(32 的倍数,256 到 4096),给了就忽略 aspect/res |
size_from_ref | bool | false | 改图时输出尺寸跟随第 1 张图(缩放到 res 档),适合保持构图的局部修改 |
seed | int | -1 | -1 随机 |
steps | int | 0 | 0 = 方案默认 25 |
cfg | number | 0 | 0 = 方案默认 1.0(引导已蒸馏进模型,一般不用改) |
images | string[] | [] | https://… 或 data:image/png;base64,…。qwen21:0 到 10 张,作为改图 / 参考对象;krea2:0 到 3 张,只借用画风、色调、质感,内容不保留 |
wait / timeout / callback_url | 同视频任务,timeout 默认 300 |
| 画幅 | 1K | 2K |
|---|---|---|
| 1:1 | 1024×1024 | 2048×2048 |
| 16:9 | 1344×768 | 2752×1536 |
| 9:16 | 768×1344 | 1536×2752 |
| 4:3 | 1152×864 | 2304×1728 |
| 3:4 | 864×1152 | 1728×2304 |
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/v1/image \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"prompt":"一张咖啡馆海报,手写体中文标题「晨光咖啡」,暖色调,胶片质感","aspect":"3:4","res":"1K","wait":true}'
# → {"id":"…","status":"done","image":"/out/….png","image_download":"/api/jobs/…/image",…}
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/v1/image -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" -d '{"prompt":"a lighthouse on a rocky coast at dawn, mist, golden light",
"profile":"krea2_turbo","images":["https://example.com/watercolor_style.jpg"],
"aspect":"16:9","res":"1K","wait":true}'
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/v1/image \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"prompt":"让 Picture 1 里的人穿上 Picture 2 的外套,保持姿势、背景和光线不变",
"images":["https://example.com/person.png","https://example.com/coat.png"],
"size_from_ref":true,"res":"1K"}'
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/images \
-H "Authorization: Bearer <TOKEN>" \
-F "prompt=把背景换成雪山日出,其它保持不变" -F "size_from_ref=true" \
-F "images=@photo.png"
{
"id": "20260918_202901_6197",
"kind": "video | image", // image = Qwen-Image-2.1 任务
"status": "queued | running | done | failed",
"prompt": "…", "profile": "fasth3", "mode": "t2v | i2v | t2i | edit",
"aspect": "16:9", "res": "480p", "size": "864x480",
"seconds": 5.0, "frames": 124, "seed": 1234, "steps": 8, "image_count": 0,
"created": 1789731030.1,
"position": 1, // queued:队列位置(1 = 下一个)
"progress": {"value": 3, "max": 8}, // running:采样步进度
"stage": "采样", "elapsed": 12.3, // running:当前阶段、已用秒数
"exec_s": 24.7, // done:生成耗时(秒)
"video": "/out/20260918_202901_6197.mp4", // done(视频):可直接播放的静态地址
"video_download": "/api/jobs/20260918_202901_6197/video", // done(视频):带文件名的下载地址
"image": "/out/….png", "image_download": "/api/jobs/…/image", // done(图片):同上;另有 negative, cfg, size_from_ref
"finished": 1789731060.2,
"error": "…", // failed:原因
"callback_status": 200 // 设置了 callback_url 时:回调的 HTTP 状态
}
stage 依次经过:加载模型 → 加载文本编码器 → 编码提示词/图片 → 采样 → 视频解码 → 音频解码 → 封装 → 保存。模型只在切换方案时重新加载。
| 352p | 480p | 768p | |
|---|---|---|---|
| 16:9 | 608×352 | 864×480 | 1344×768 |
| 9:16 | 352×608 | 480×864 | 768×1344 |
| 1:1 | 480×480 | 672×672 | 1024×1024 |
| profile | 步数 | 特点 |
|---|---|---|
fasth3 | 8 | FastH3 8 步完整模型,画质最好,默认 |
turbo_int8 | 8 | Turbo LoRA,风格略不同,速度相近 |
fasth3_4step_int8 | 4 | 最快,细节稍软 |
时长:任意 1 到 15 秒,网页上给的是 5 / 8 / 10 / 15。
RTX 5090,fasth3 8 步,实测每条片子的生成耗时(不含排队):
| 分辨率 | 5 秒 | 10 秒 | 采样速度 | 备注 |
|---|---|---|---|---|
| 352p | ≈ 13 s | ≈ 26 s | 1.8 s/步 | 预览、快速试提示词 |
| 480p | ≈ 27 s | ≈ 54 s | 4.5 s/步 | 速度与可看度的折中 |
| 768p | ≈ 90 s | ≈ 178 s | 18.2 s/步 | 最高画质,10 秒片显存接近 32 GB 上限 |
fasth3_4step_int8 采样步数减半,整体约快 30% 到 40%。切换方案的第一条任务多 3 到 10 秒加载时间。同一时间只跑一个任务,排队时间 = 前面任务的耗时之和。
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/v1/generate \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"prompt":"一只戴墨镜的小熊猫在舞台上打鼓,暖色聚光灯,观众欢呼,电影感。声音:鼓点、欢呼声。","res":"480p","seconds":5,"wait":true}'
# 返回的 JSON 里 status 为 done 时:
curl -L -o clip.mp4 -H "Authorization: Bearer <TOKEN>" \
https://desktop-lp9ii17.tail97a7d2.ts.net/api/jobs/<id>/video
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/v1/generate \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"prompt":"镜头缓慢推进,画面从第一帧过渡到最后一帧。声音:轻柔的风声。",
"images":["https://example.com/first.png","https://example.com/last.png"],
"res":"480p","seconds":5}'
# → {"id":"2026…","status":"queued",…}
curl -s -H "Authorization: Bearer <TOKEN>" \
"https://desktop-lp9ii17.tail97a7d2.ts.net/api/jobs/<id>?wait=true&timeout=600"
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/v1/generate \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"prompt":"<Picture 1> 里的女孩走进咖啡馆,坐到窗边翻开一本书,镜头缓慢推近。声音:咖啡馆环境音。",
"images":["https://example.com/girl.png"], "image_mode":"reference",
"res":"480p","seconds":5}'
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/v1/generate \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"prompt":"<Picture 1> 里的老人站在灯塔窗前,面部特写,认真地对着镜头说话,口型清晰。声音:他的声音,雨声。",
"images":["https://example.com/keeper.png"],
"audios":["https://example.com/keeper_voice.wav"],
"tts_text":"海上的风很大,你要小心。今晚我会一直守在这里。",
"tts_ref_text":"别怕,灯还亮着。我在这儿等你回来。", "tts_language":"Chinese",
"res":"480p","seconds":5}'
只想要语音不要视频:POST /api/v1/tts,JSON 字段 text、ref_audio、ref_text(可选)、language,同步返回 {"audio":"/out/tts_….wav"}。
curl -s https://desktop-lp9ii17.tail97a7d2.ts.net/api/jobs \
-H "Authorization: Bearer <TOKEN>" \
-F "prompt=The scene comes alive, gentle camera drift. Audio: ambient." \
-F "res=480p" -F "seconds=5" \
-F "images=@first.png" -F "images=@middle.png" -F "images=@last.png"
import base64, time, requests
BASE = "https://desktop-lp9ii17.tail97a7d2.ts.net"
H = {"Authorization": "Bearer <TOKEN>"}
def data_uri(path):
return "data:image/png;base64," + base64.b64encode(open(path, "rb").read()).decode()
job = requests.post(f"{BASE}/api/v1/generate", headers=H, json={
"prompt": "A golden retriever puppy runs through autumn leaves, slow motion. Audio: leaves crunching.",
"images": [data_uri("first.png")], # 可省略 → 文生视频
"res": "480p", "seconds": 5, "seed": 42,
}).json()
while job["status"] in ("queued", "running"):
time.sleep(3)
job = requests.get(f"{BASE}/api/jobs/{job['id']}", headers=H).json()
print(job["status"], job.get("position"), job.get("stage"), job.get("progress"))
if job["status"] == "done":
mp4 = requests.get(BASE + job["video_download"], headers=H).content
open("clip.mp4", "wb").write(mp4)
print("saved, generation took", job["exec_s"], "s")
else:
print("failed:", job.get("error"))
const BASE = "https://desktop-lp9ii17.tail97a7d2.ts.net";
const r = await fetch(BASE + "/api/v1/generate", {
method: "POST",
headers: { "Authorization": "Bearer <TOKEN>", "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "城市夜景航拍,霓虹反射在湿润的街道上。声音:车流、雨声。", res: "352p", seconds: 5, wait: true })
});
const job = await r.json();
if (job.status === "done") document.querySelector("video").src = BASE + job.video; // /out/… 不需要口令
提交时带 "callback_url": "https://你的服务器/hook",任务结束后会向该地址 POST 一份任务对象(成功和失败都会发)。video 是相对路径,拼上 BASE 即可下载;回调的 HTTP 状态会记录在任务的 callback_status。
| 状态码 | 含义 |
|---|---|
400 | 参数错误:未知 profile / aspect / res,prompt 为空,seconds 不在 1 到 15,图片下载失败或格式不对,图片超过 25 MB。detail 里有原因 |
401 | 口令缺失或错误 |
404 | 任务不存在,或视频尚未生成完成 |
422 | JSON 字段类型不对(FastAPI 校验),返回里逐字段说明 |
position 是队列位置。error 带 ComfyUI 的异常信息;服务重启会把未完成的任务标记为失败。DELETE /api/jobs/{id} 删除。