ComfyUI部署Wan2.2,开放API,文生视频与图生视频

通义万象2.2开源了,5B模型具有文生视频和图生视频的能力。这里介绍通过ComfyUI部署wan2.2并开放API,通过url返回生成的视频。环境配置,api的worlflow配置文件获取,ComfyUI启动,配置url路径参考这里

文生视频

这里直接给出API启动脚本:

python 复制代码
from fastapi import FastAPI, HTTPException, Header
from fastapi import FastAPI, HTTPException, Header
from pydantic import BaseModel
import requests
import copy
import json
import os
import urllib.parse
import time
import base64

API_KEY = "12345"

# ---------------- 配置 ----------------
COMFYUI_API_URL = "http://127.0.0.1:8188"  # ComfyUI API 地址
WORKFLOW_FILE = "text_to_video_wan22_5B_API.json"      # 固定 workflow 文件
OUTPUT_DIR = "output_videos"
POLL_INTERVAL = 2  # 秒

STATIC_DIR = "/var/www/static"  # 存储视频的目录
SERVER_BASE_URL = "http://server_ip:9001"  # 对外访问的服务器地址

os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(STATIC_DIR, exist_ok=True)

# ---------------- 读取 workflow ----------------
with open(WORKFLOW_FILE, "r", encoding="utf-8") as f:
    base_workflow = json.load(f)

# ---------------- 请求体定义 ----------------
class GenerateVideoRequest(BaseModel):
    positive: str
    negative: str = ""
    height: int = 704
    width: int = 1280
    length: int = 121  # 视频帧数
    fps: int = 24

# ---------------- FastAPI 实例 ----------------
app = FastAPI(title="ComfyUI Video API Server")

# ---------------- 生成接口 ----------------
@app.post("/generate_video")
def generate_video(req: GenerateVideoRequest, x_api_key: str = Header(None)):
    if x_api_key != API_KEY:
        raise HTTPException(status_code=401, detail="Invalid API Key")

    workflow = copy.deepcopy(base_workflow)

    # 修改 workflow 中的输入参数
    workflow["6"]["inputs"]["text"] = req.positive
    workflow["7"]["inputs"]["text"] = req.negative
    workflow["55"]["inputs"]["width"] = req.width
    workflow["55"]["inputs"]["height"] = req.height
    workflow["55"]["inputs"]["length"] = req.length
    workflow["47"]["inputs"]["fps"] = req.fps  # 保存 WEBM
    workflow["28"]["inputs"]["fps"] = float(req.fps)  # 保存 WEBP 动图

    # 提交任务
    resp = requests.post(f"{COMFYUI_API_URL}/prompt", json={"prompt": workflow})
    resp.raise_for_status()
    prompt_id = resp.json()["prompt_id"]

    public_urls = []
    while True:
        history = requests.get(f"{COMFYUI_API_URL}/history/{prompt_id}").json()
        if prompt_id in history:
            outputs = history[prompt_id].get("outputs", {})
            for node_id, output_data in outputs.items():
                if "images" in output_data:  # 视频也会走这里
                    for file in output_data["images"]:
                        filename = file["filename"]
                        subfolder = file.get("subfolder", "")
                        filetype = file.get("type", "output")  # webm / webp / png

                        file_url = (
                            f"{COMFYUI_API_URL}/view"
                            f"?filename={urllib.parse.quote(filename)}"
                            f"&subfolder={urllib.parse.quote(subfolder)}"
                            f"&type={urllib.parse.quote(filetype)}"
                        )

                        file_data = requests.get(file_url).content
                        save_path = os.path.join(STATIC_DIR, filename)
                        with open(save_path, "wb") as f:
                            f.write(file_data)

                        public_url = f"{SERVER_BASE_URL}/static/{filename}"
                        public_urls.append(public_url)

            if public_urls:
                break
        time.sleep(POLL_INTERVAL)

    return {"prompt_id": prompt_id, "video_urls": public_urls}

将上面的脚本保存为serve_fastapi.py,其中COMFYUI_API_URL是启动的ComfyUI服务,WORKFLOW_FILE是保存的workflow配置文件。使用下面的命令启动API:

bash 复制代码
uvicorn serve_fastapi:app --host 0.0.0.0 --port 9002

这里给出python访问api的脚本:

python 复制代码
import requests
import base64
from PIL import Image
from io import BytesIO

headers = {"x-api-key": "12345"}
resp = requests.post(
    "http://server_ip:9002/generate_video",
    headers=headers,
    json={
        "positive": "Two anthropomorphic cats in comfy boxing gear and bright gloves fight intensely on a spotlighted stage.",
        "negative": "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走",
        "height": 512,
        "width": 768,
        "length": 73, # 帧数,帧率固定为24 fps
    }
)
print(resp.json())

api会返回可访问的生成视频的url。

图生视频

直接给出api启动脚本,与上面不同的就是图生视频需要多传如一个图像,这里以url的形式传入:

python 复制代码
from fastapi import FastAPI, HTTPException, Header
from pydantic import BaseModel
import requests
import copy
import json
import os
import urllib.parse
import time
import base64
import tempfile

API_KEY = "12345"

# ---------------- 配置 ----------------
COMFYUI_API_URL = "http://127.0.0.1:8188"  # ComfyUI API 地址
WORKFLOW_FILE = "image_to_video_wan22_5B_api.json"  # 你准备好的图生视频 workflow
OUTPUT_DIR = "output_videos"
POLL_INTERVAL = 2  # 秒

STATIC_DIR = "/var/www/static"  # 存储视频的目录
SERVER_BASE_URL = "http://server_ip:9001"  # 对外访问的服务器地址

os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(STATIC_DIR, exist_ok=True)

# ---------------- 读取 workflow ----------------
with open(WORKFLOW_FILE, "r", encoding="utf-8") as f:
    base_workflow = json.load(f)

# ---------------- 请求体定义 ----------------
class GenerateVideoRequest(BaseModel):
    image_url: str                     # 输入图像 URL
    positive: str = ""                 # 可选的正向 prompt
    negative: str = ""                 # 可选的负向 prompt
    height: int = 704
    width: int = 1280
    length: int = 121                  # 视频帧数
    fps: int = 24

# ---------------- FastAPI 实例 ----------------
app = FastAPI(title="ComfyUI Image-to-Video API Server")

# ---------------- 生成接口 ----------------
@app.post("/generate_video_i2v")
def generate_video(req: GenerateVideoRequest, x_api_key: str = Header(None)):
    if x_api_key != API_KEY:
        raise HTTPException(status_code=401, detail="Invalid API Key")

    workflow = copy.deepcopy(base_workflow)

    # ----------- 下载输入图像到临时目录,并传给 ComfyUI -----------
    resp = requests.get(req.image_url)
    if resp.status_code != 200:
        raise HTTPException(status_code=400, detail="Failed to download input image")
    
    tmp_path = os.path.join(STATIC_DIR, "input_image.png")
    with open(tmp_path, "wb") as f:
        f.write(resp.content)

    # 替换 workflow 的图像节点 (假设节点 id 是 "57")
    workflow["57"]["inputs"]["image"] = tmp_path

    # 修改其他输入
    if "6" in workflow:  # 正向 prompt 节点
        workflow["6"]["inputs"]["text"] = req.positive
    if "7" in workflow:  # 负向 prompt 节点
        workflow["7"]["inputs"]["text"] = req.negative
    if "55" in workflow:
        workflow["55"]["inputs"]["width"] = req.width
        workflow["55"]["inputs"]["height"] = req.height
        workflow["55"]["inputs"]["length"] = req.length
    if "47" in workflow:
        workflow["47"]["inputs"]["fps"] = req.fps  # 保存 WEBM
    if "28" in workflow:
        workflow["28"]["inputs"]["fps"] = float(req.fps)  # 保存 WEBP 动图

    # 提交任务
    resp = requests.post(f"{COMFYUI_API_URL}/prompt", json={"prompt": workflow})
    resp.raise_for_status()
    prompt_id = resp.json()["prompt_id"]

    public_urls = []
    while True:
        history = requests.get(f"{COMFYUI_API_URL}/history/{prompt_id}").json()
        if prompt_id in history:
            outputs = history[prompt_id].get("outputs", {})
            for node_id, output_data in outputs.items():
                if "images" in output_data:  # 视频也会走这里
                    for file in output_data["images"]:
                        filename = file["filename"]
                        subfolder = file.get("subfolder", "")
                        filetype = file.get("type", "output")

                        file_url = (
                            f"{COMFYUI_API_URL}/view"
                            f"?filename={urllib.parse.quote(filename)}"
                            f"&subfolder={urllib.parse.quote(subfolder)}"
                            f"&type={urllib.parse.quote(filetype)}"
                        )

                        file_data = requests.get(file_url).content
                        save_path = os.path.join(STATIC_DIR, filename)
                        with open(save_path, "wb") as f:
                            f.write(file_data)

                        public_url = f"{SERVER_BASE_URL}/static/{filename}"
                        public_urls.append(public_url)

            if public_urls:
                break
        time.sleep(POLL_INTERVAL)

    return {"prompt_id": prompt_id, "video_urls": public_urls}

将上面的脚本保存为serve_fastapi_i2v.py,其中COMFYUI_API_URL是启动的ComfyUI服务,WORKFLOW_FILE是保存的workflow配置文件。使用下面的命令启动API:

bash 复制代码
uvicorn serve_fastapi:app --host 0.0.0.0 --port 9003

这里给出python调用api的示例:

python 复制代码
import requests

headers = {"x-api-key": "12345"}
resp = requests.post(
    "http://127.0.0.1:9003/generate_video_i2v",
    headers=headers,
    json={
        "image_url": "https://comfyanonymous.github.io/ComfyUI_examples/chroma/fennec_girl_hug.png",
        "positive": "a cute anime girl with fennec ears and a fluffy tail walking in a beautiful field",
        "negative": "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走",
        "height": 704,
        "width": 1280,
        "length": 141, # 帧数,帧率固定为24 fps
    }
)
print(resp.json())

图生视频的图像用url进行传入,如果是图像就在服务器上,可以直接使用路径,如果图像在本地主机,可以传到某个可访问的云服务上,开放url。api会返回生成视频的url。