AI 生视频 v3.0

描述

根据文本描述生成视频,或结合输入图片和文本描述生成视频。支持文生视频和图生视频两种调用方式。

版本

1.0

输入要求

文生视频无需传入图片;图生视频通过 init_images 传入图片 URL。

调用 URL

正式环境:https://openapi.meitu.com

任务提交接口:

https://openapi.meitu.com/api/v1/sdk/sync/push

任务名称(task):/v1/v3_mvv_model/ai_video/453181

任务类型(task_type):formula

调用方法

POST

Content-Type: application/json

权限

使用已开通该能力的 AK/SK,通过签名 SDK 生成请求签名。详见开放平台接口签名。

请求参数

是否必选参数名类型参数说明
是taskstring固定为 /v1/v3_mvv_model/ai_video/453181。
是task_typestring固定为 formula。
否init_imagesobject[]输入图片列表。文生视频时省略;图生视频时传入。
是paramsstring算法参数对象序列化后的 JSON 字符串。
否sync_timeoutinteger同步等待时长,单位为秒,网关默认值为 30。

init_images 元素

是否必选参数名类型参数说明
是urlstring可访问的输入图片 URL。
是profileobject图片配置。

profile

是否必选参数名类型参数说明
是versionstring固定为 v1。
是media_profilesobject图片传输属性,本接口示例传入空对象 {}。

params

解码后的对象直接包含以下字段,不添加 parameter 包装层:

是否必选参数名类型参数说明
是user_promptstring视频生成的文本描述。

输入值示例

替换示例中的文本描述及图片 URL。HTTP 请求体为 JSON 对象,仅 params 的值为 JSON 字符串。

文生视频

{
  "task": "/v1/v3_mvv_model/ai_video/453181",
  "task_type": "formula",
  "params": "{\"user_prompt\":\"A gentle breeze moves through the trees.\"}",
  "sync_timeout": 30
}

图生视频

{
  "task": "/v1/v3_mvv_model/ai_video/453181",
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/input.jpg",
      "profile": {
        "version": "v1",
        "media_profiles": {}
      }
    }
  ],
  "params": "{\"user_prompt\":\"A gentle breeze moves through the trees.\"}",
  "sync_timeout": 30
}

params 解码后的对象:

{
  "user_prompt": "A gentle breeze moves through the trees."
}

返回值说明

code = 0 表示请求处理正常,仍需检查 data.status。当状态为 10 时,从 data.result.urls 读取结果地址。请及时下载保存结果。

字段类型参数说明
codeinteger业务状态码,0 表示请求处理正常。
messagestring响应消息,失败时可用于排查错误。
dataobject / null任务详情;请求失败时可能为 null。

data

字段类型参数说明
statusinteger-1 未找到任务;0 创建成功;1 执行中;2 失败;9 同步等待超时,需要继续查询;10 成功。
msgstring任务消息。
resultobject任务结果或任务标识。
progressnumber任务进度,例如 0.1、0.85、1。

result

字段类型参数说明
idstring网关任务 ID,用于查询任务结果。
urlsstring[]成功时返回的结果地址列表。

返回值示例

以下成功和失败结构来自原接口文档,任务 ID、结果地址已替换为占位值,未进行实际调用。原文结果地址以 .jpg 结尾,未明确视频封装格式或固定输出数量;以下地址不表示输出格式及数量保证。

请求成功返回示例

HTTP 200,Content-Type: application/json。

{
  "code": 0,
  "data": {
    "status": 10,
    "result": {
      "id": "example-task-id",
      "urls": [
        "https://example.com/result-1",
        "https://example.com/result-2"
      ]
    },
    "progress": 1
  }
}

需要查询返回示例

以下为 status = 9 的结构示意。同步等待超时不代表任务失败,使用 data.result.id 按任务查询接口继续查询,不要重复提交。

{
  "code": 0,
  "data": {
    "status": 9,
    "result": {
      "id": "example-task-id"
    },
    "progress": 0
  }
}

请求失败返回示例

原文失败示例为 HTTP 400。其他错误的状态码和消息以实际响应为准。

{
  "code": 20008,
  "message": "UNSUITABLE_IMAGE",
  "data": null
}

通用的错误代码与信息

错误码错误信息说明
20001PROCESS_ERROR处理错误。
20008UNSUITABLE_IMAGE图片不符合处理要求,见上方失败示例。

其他错误详见 API 错误码。

SDK 调用示例

以下示例均使用上方图生视频请求,内嵌完整请求体,无需额外 JSON 文件。按对应文档引入签名 SDK 后,替换 AK、SK、图片 URL 和文本描述。

文生视频时省略 init_images。示例提交一次并打印响应;签名和发送必须使用相同的最终请求体,返回状态为 9 时继续查询任务。

Python

Python 签名 SDK 接入文档

import json

import requests
from sign_sdk import sign

def main():
    key = "YOUR_ACCESS_KEY"
    secret = "YOUR_SECRET_KEY"
    url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
    inner_params = {
        "user_prompt": "A gentle breeze moves through the trees."
    }
    payload = {
        "task": "/v1/v3_mvv_model/ai_video/453181",
        "task_type": "formula",
        "init_images": [
            {
                "url": "https://example.com/input.jpg",
                "profile": {
                    "version": "v1",
                    "media_profiles": {}
                }
            }
        ],
        "params": json.dumps(inner_params, ensure_ascii=True),
        "sync_timeout": 30
    }
    body = json.dumps(payload, ensure_ascii=True)
    headers = {
        "Content-Type": "application/json",
        sign.HeaderHost: "openapi.meitu.com",
    }
    signer = sign.Signer(key, secret)
    signed_request = signer.sign(url, "POST", headers, body)
    with requests.Session() as session:
        response = session.send(
            signed_request, timeout=(10, 60), verify=True, allow_redirects=False
        )
        print("Status:", response.status_code)
        print("Response:", response.text)

if __name__ == "__main__":
    main()

Go

Go 签名 SDK 接入文档

package main

import (
    "fmt"
    "io"
    "net/http"
    "time"

    "github.com/formula/api/signer"
)

func main() {
    body := `{
  "task": "/v1/v3_mvv_model/ai_video/453181",
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/input.jpg",
      "profile": {
        "version": "v1",
        "media_profiles": {}
      }
    }
  ],
  "params": "{\"user_prompt\":\"A gentle breeze moves through the trees.\"}",
  "sync_timeout": 30
}`
    url := "https://openapi.meitu.com/api/v1/sdk/sync/push"
    headers := make(http.Header)
    headers.Set("Content-Type", "application/json")
    headers.Set(signer.HeaderHost, "openapi.meitu.com")
    signObj := signer.NewSigner("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY")
    req, err := signObj.Sign(url, http.MethodPost, headers, body)
    if err != nil {
        fmt.Println("Sign request failed:", err)
        return
    }
    client := &http.Client{
        Timeout: 60 * time.Second,
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            return http.ErrUseLastResponse
        },
    }
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Send request failed:", err)
        return
    }
    defer resp.Body.Close()
    responseBody, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("Read response failed:", err)
        return
    }
    fmt.Println("Status:", resp.StatusCode)
    fmt.Println("Response:", string(responseBody))
}

PHP

PHP 签名 SDK 接入文档

<?php
require_once __DIR__ . '/signer.php';

$innerParams = json_encode([
    'user_prompt' => 'A gentle breeze moves through the trees.'
], JSON_UNESCAPED_SLASHES);
$body = json_encode([
    'task' => '/v1/v3_mvv_model/ai_video/453181',
    'task_type' => 'formula',
    'init_images' => [
        [
            'url' => 'https://example.com/input.jpg',
            'profile' => [
                'version' => 'v1',
                'media_profiles' => (object) []
            ]
        ]
    ],
    'params' => $innerParams,
    'sync_timeout' => 30
], JSON_UNESCAPED_SLASHES);
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$headers = [
    'Content-Type' => 'application/json',
    'Host' => 'openapi.meitu.com',
];
$signer = new Signer('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY');
$curl = $signer->sign($url, 'POST', $headers, $body);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($curl);
if ($response === false) {
    echo 'Error: ' . curl_error($curl) . PHP_EOL;
} else {
    echo 'Status: ' . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
    echo 'Response: ' . $response . PHP_EOL;
}
curl_close($curl);

Java

Java 签名 SDK 接入文档

import com.meitu.openai.common.Signer;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws Exception {
        String body =
                "{\n" +
                "  \"task\": \"/v1/v3_mvv_model/ai_video/453181\",\n" +
                "  \"task_type\": \"formula\",\n" +
                "  \"init_images\": [\n" +
                "    {\n" +
                "      \"url\": \"https://example.com/input.jpg\",\n" +
                "      \"profile\": {\n" +
                "        \"version\": \"v1\",\n" +
                "        \"media_profiles\": {}\n" +
                "      }\n" +
                "    }\n" +
                "  ],\n" +
                "  \"params\": \"{\\\"user_prompt\\\":\\\"A gentle breeze moves through the trees.\\\"}\",\n" +
                "  \"sync_timeout\": 30\n" +
                "}";
        String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put(Signer.HeaderHost, "openapi.meitu.com");
        Signer signer = new Signer("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY");
        Map<String, String> signedHeaders = signer.sign(url, "POST", headers, body);

        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        try {
            connection.setRequestMethod("POST");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(60000);
            connection.setInstanceFollowRedirects(false);
            connection.setDoOutput(true);
            for (Map.Entry<String, String> entry : signedHeaders.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }
            byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
            connection.setFixedLengthStreamingMode(bodyBytes.length);
            try (OutputStream output = connection.getOutputStream()) {
                output.write(bodyBytes);
            }
            int status = connection.getResponseCode();
            System.out.println("Status: " + status);
            InputStream responseStream = status >= 400
                    ? connection.getErrorStream() : connection.getInputStream();
            if (responseStream != null) {
                try (InputStream input = responseStream;
                     ByteArrayOutputStream output = new ByteArrayOutputStream()) {
                    byte[] buffer = new byte[4096];
                    int length;
                    while ((length = input.read(buffer)) != -1) {
                        output.write(buffer, 0, length);
                    }
                    System.out.println("Response: " + output.toString("UTF-8"));
                }
            }
        } finally {
            connection.disconnect();
        }
    }
}