跳转到主要内容
本页聚焦代码链路:把一张本地图片变成 asset:// 素材 ID,再引用它生成人物一致性视频,一个脚本跑通。素材库各接口的逐个说明、网页零代码操作见 素材库,生成接口的完整参数表见 视频生成 API

什么时候需要素材引用

Seedance 2.0 生成「人物一致性」视频时,不能直接上传含写实人脸的参考图(防深伪拦截)。需要先把人像入库成可信素材,拿到 asset://xxx 素材 ID,再在生成请求里引用——同一角色跨集、跨镜头保持脸部与服装一致。先对号入座:
素材类型例子怎么用
动漫 / 风格化角色二次元、卡通、3D 卡通角色不含写实人脸,一般不触发拦截:直接用公网 URL / Base64 作参考图,无需入库,本页流程从「第 4 步生成」开始即可
虚拟人(AI 生成写实人像)模型生成、现实中无此人走「虚拟人像入库」(本页完整流程),拿 asset:// ID 引用
真人人脸艺人、模特、用户本人的照片先走「真人认证」全自动 API 流程拿真人素材组,入库时带上该组 groupId,其余代码与本页完全一致

前置条件

两把钥匙,不要混用
  • 素材库 KEY(icover.ai「设置 → 素材库 KEY」创建,sk-...):用于上传 / 入库 / 查询素材,创建方法见 素材库第 0 步
  • APIYI SeeDance2 令牌(api.apiyi.com 创建,sk-...,须勾选 SeeDance2 分组):用于视频生成接口。

链路总览

本地图片 → ① presign 申请直传地址并 PUT 上传,拿公网 URL → ② 素材入库,拿 asset Id → ③ 轮询到 Active(单图约 13 秒)→ ④ 生成请求里引用 asset://<Id>,提示词用「图片1」指代 → ⑤ 轮询任务到 succeeded → ⑥ 下载 content.video_url(签名直链 24 小时过期,立即转存)。 已有公网图片 URL 的可跳过 ①;素材 ID 永久有效,入库一次即可反复引用。

完整代码

import time
from pathlib import Path

import requests

ASSET_KEY = "sk-你的素材库KEY"    # icover.ai「设置 → 素材库 KEY」
APIYI_KEY = "sk-你的APIYI令牌"    # api.apiyi.com,须勾选 SeeDance2 分组
IMAGE_PATH = "portrait.jpg"       # 本地参考图(虚拟人像)
PROMPT = "图片1中的人物正面微笑,镜头缓慢推近,自然光"

ICOVER = "https://icover.ai/api"
SEEDANCE = "https://api.apiyi.com/seedance/api/v3/contents/generations/tasks"
ASSET_HEADERS = {"Authorization": f"Bearer {ASSET_KEY}"}
APIYI_HEADERS = {
    "Authorization": f"Bearer {APIYI_KEY}",
    "Content-Type": "application/json",
    "Accept-Encoding": "identity",  # 必加:规避网关 gzip 头与实际编码不符
}


def upload_image(path: str) -> str:
    """① presign + PUT 上传本地图片,返回公网 URL(已有公网 URL 可跳过)"""
    ext = Path(path).suffix.lstrip(".").lower() or "jpg"
    content_type = f"image/{'jpeg' if ext in ('jpg', 'jpeg') else ext}"
    data = requests.post(
        f"{ICOVER}/storage/presign", headers=ASSET_HEADERS,
        json={"ext": ext, "contentType": content_type}, timeout=30,
    ).json()["data"]
    resp = requests.put(
        data["uploadUrl"], data=Path(path).read_bytes(),
        headers={"Content-Type": content_type}, timeout=120,  # 与 presign 一致
    )
    resp.raise_for_status()
    return data["publicUrl"]


def ingest_asset(image_url: str, label: str = "", group_id: str = None) -> str:
    """② 素材入库,返回 asset Id。真人素材:带上真人认证拿到的 group_id"""
    body = {"imageUrl": image_url, "label": label}
    if group_id:
        body["groupId"] = group_id
    r = requests.post(
        f"{ICOVER}/asset-library/assets", headers=ASSET_HEADERS,
        json=body, timeout=60,
    ).json()
    return r["Result"]["Id"]


def wait_asset_active(asset_id: str, timeout: int = 90) -> None:
    """③ 轮询入库状态到 Active(单图约 13 秒,无 SLA)"""
    deadline = time.time() + timeout
    while time.time() < deadline:
        status = requests.get(
            f"{ICOVER}/asset-library/assets/{asset_id}",
            headers=ASSET_HEADERS, timeout=30,
        ).json()["Result"]["Status"]
        print("asset status:", status)
        if status == "Active":
            return
        if status == "Failed":
            raise RuntimeError("素材入库失败:检查格式 / 宽高比 0.4-2.5 / 边长 300-6000px 后重传")
        time.sleep(3)
    raise TimeoutError("90 秒未 Active,请排查后重试")


def create_video_task(asset_id: str) -> str:
    """④ 引用 asset:// 创建生成任务。提示词用「图片1」指代,不要写 asset ID 原文"""
    body = {
        "model": "doubao-seedance-2-0-260128",
        "content": [
            {"type": "text", "text": PROMPT},
            {"type": "image_url",
             "image_url": {"url": f"asset://{asset_id}"},
             "role": "reference_image"},
        ],
        "resolution": "720p", "ratio": "16:9", "duration": 5,
    }
    return requests.post(SEEDANCE, headers=APIYI_HEADERS, json=body, timeout=60).json()["id"]


def wait_video(task_id: str) -> dict:
    """⑤ 轮询任务到终态(succeeded / failed / expired)"""
    while True:
        time.sleep(20)
        task = requests.get(f"{SEEDANCE}/{task_id}", headers=APIYI_HEADERS, timeout=30).json()
        print("task status:", task.get("status"))
        if task.get("status") in ("succeeded", "failed", "expired"):
            return task


def download(task: dict, out: str) -> None:
    """⑥ 下载视频。直链 24 小时过期,立即转存;下载不要带 Authorization 头"""
    with requests.get(task["content"]["video_url"], stream=True, timeout=300) as r:
        r.raise_for_status()
        with open(out, "wb") as f:
            for chunk in r.iter_content(chunk_size=1 << 20):
                f.write(chunk)
    print("已保存", out)


if __name__ == "__main__":
    public_url = upload_image(IMAGE_PATH)
    asset_id = ingest_asset(public_url, label="角色A-正面")
    print("素材 ID:", f"asset://{asset_id}")
    wait_asset_active(asset_id)

    task_id = create_video_task(asset_id)
    print("task_id:", task_id)
    task = wait_video(task_id)
    if task["status"] == "succeeded":
        print("计费 tokens:", task["usage"]["completion_tokens"])
        download(task, f"{task_id}.mp4")
    else:
        print("任务未成功:", task.get("error"))
提示词里用「图片1」「图片2」指代素材(按 content 数组顺序对应),不要写 asset ID 原文

多素材进阶用法

  • 多张参考图content 里放多个 image_url(0–9 张,role 均为 reference_image),提示词用「图片1」「图片2」按顺序指代。同一人物建议「全身正面 + 人脸正面无表情特写」两张一起引用,一致性最好。
  • 混用来源asset:// 素材 ID、公网 URL、Base64(data:image/png;base64,...)可以在同一个 content 里混用——只有含写实人脸的图必须走 asset://
  • 参考视频 / 音频:还可加 0–3 个 video_urlrole: "reference_video")与 0–3 个 audio_urlrole: "reference_audio"),至少要有 1 图或 1 视频;音频需与图片或视频一起传。
  • 真人素材:先按 真人认证全自动流程 让艺人完成活体认证、拿到专属真人素材组 GroupId,入库时带上 groupId 即可;生成侧代码与本页完全一致。

常见报错速查

报错 / 现象原因处理
400 The specified asset ... is not foundasset:// 不在 APIYI 通道对应的账号下(如在其他渠道商入库),或 ID 写错按本页流程在 icover.ai 重新入库;核对素材 ID
「该模型无可用渠道」APIYI 令牌未勾选 SeeDance2 分组到 api.apiyi.com 令牌设置勾选后重试
Python 报 gzip 解码错误网关 gzip 响应头与实际编码不符请求头加 "Accept-Encoding": "identity"(脚本已带)
素材状态 Failed图片格式 / 尺寸不合规宽高比 0.4–2.5、边长 300–6000px、少于 30MB,修图后重传
直接传人脸图被拒写实人脸不能作直接参考图(防深伪拦截)先入库拿 asset:// 再引用;真人照片须先完成真人认证
PUBLIC_ 前缀错误上游官方内容审核拦截(该次不计费)调整素材 / 提示词后直接重试

相关页面

Seedance 2.0 概览

模型选型、定价、分辨率像素表与 FAQ

视频生成 API

完整参数表、四种生成模式与响应格式

素材库

素材库全部接口、网页零代码操作与真人认证