Apps API

Img2Vid app 是预先配置好的生成工作流。当你希望直接调用某个 app,而不是自己选择媒体模型、mode 和模型参数时,可以使用 Apps API。

App 公开 API 保持极简:查询接口只返回 app id 和扁平输入参数;创建生成任务时,直接把这些参数名放在请求体根层级。

接口

Method Endpoint 用途
GET /api/v1/apps 查询可调用 app 及输入参数。
GET /api/v1/apps/{app} 查询单个 app 的输入参数。
POST /api/v1/apps/{app}/generations 基于 app 创建异步生成任务。
GET /api/v1/apps/{app}/generations/{id} 查询 app 生成任务状态和结果。

所有接口都需要 Authorization: Bearer $BUBLE_API_KEY

查询 app 列表

GET /api/v1/apps

Query 参数

Name Type Required Description
page number 页码,默认 1
limit number 每页数量,默认 50,最大 100

请求示例

curl "https://img2vid.net/api/v1/apps?limit=20" \
  -H "Authorization: Bearer $BUBLE_API_KEY"

响应结构

{
  "data": [
    {
      "id": "video-background-remover",
      "input_parameters": [
        {
          "name": "source_video",
          "type": "array"
        },
        {
          "name": "output_format",
          "type": "string",
          "values": ["webm", "mp4"]
        }
      ]
    }
  ]
}

输入参数

每个 app 都会暴露一组扁平输入参数。

Field 说明
name 创建生成任务时使用的参数名。
type JSON 值类型:stringnumberbooleanarray
values 可选值范围;只有配置了离散可选值的参数才会返回该字段。

媒体输入参数的 typearray;调用时传入源文件 URL 数组。

查询单个 app

GET /api/v1/apps/{app}
curl https://img2vid.net/api/v1/apps/video-background-remover \
  -H "Authorization: Bearer $BUBLE_API_KEY"

响应中的 dataGET /api/v1/apps 返回列表中的单个 app 结构一致。

创建 app 生成任务

POST /api/v1/apps/{app}/generations
Content-Type: application/json

请求体是扁平结构。只能使用 /api/v1/apps 返回的参数名;未知字段会被拒绝。

请求示例

curl https://img2vid.net/api/v1/apps/video-background-remover/generations \
  -H "Authorization: Bearer $BUBLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_video": ["https://example.com/source-video.mp4"]
  }'

响应结构

{
  "data": {
    "id": "task_id",
    "status": "pending"
  }
}

如果 app 使用视频时长计费,Img2Vid 会在服务端根据提交的视频 URL 自动计算源视频时长。不要通过传递 duration 字段来控制计费。

查询 app 生成任务

GET /api/v1/apps/{app}/generations/{id}
curl https://img2vid.net/api/v1/apps/video-background-remover/generations/task_id \
  -H "Authorization: Bearer $BUBLE_API_KEY"

状态值

Status 含义
pending 任务已创建,等待执行。
processing 供应商正在生成结果。
success 生成成功。
failed 生成失败,查看 error
canceled 任务已取消。

成功响应示例

{
  "data": {
    "id": "task_id",
    "status": "success",
    "result": {
      "videos": [
        {
          "url": "https://..."
        }
      ]
    }
  }
}

失败响应示例

{
  "data": {
    "id": "task_id",
    "status": "failed",
    "error": {
      "message": "Generation failed."
    }
  }
}

App API 与媒体模型 API 的区别

当你希望使用预设工作流和简化输入时,使用 /api/v1/apps。当你希望直接选择具体媒体模型、mode 和模型参数时,使用 /api/v1/media_models/api/v1/generations

SDK 示例

SDK 的 app 生成方法对应 POST /api/v1/apps/{app}/generations。只发送 listretrieve 返回的参数名;媒体参数必须接收 URL,不能直接传本地文件路径。

下面的片段假设你已经按快速开始初始化了 SDK client,并引入了对应语言需要的 SDK 类型。

JavaScript / TypeScript

const app = await buble.apps.retrieve('video-background-remover');
console.log(app.data.input_parameters);

const task = await buble.apps.generations.create('video-background-remover', {
  source_video: ['https://example.com/source.mp4'],
  refine_foreground_edges: true,
  subject_is_person: true,
});

const result = await buble.apps.generations.wait(
  'video-background-remover',
  task.data.id
);

Python

app = client.apps.retrieve("video-background-remover")
print(app["data"]["input_parameters"])

task = client.apps.generations.create(
    "video-background-remover",
    source_video=["https://example.com/source.mp4"],
    refine_foreground_edges=True,
    subject_is_person=True,
)

result = client.apps.generations.wait("video-background-remover", task["data"]["id"])

Go

app, err := client.Apps.Retrieve(ctx, "video-background-remover")
if err != nil {
	return err
}
fmt.Println(app.Data.InputParameters)

task, err := client.Apps.Generations.Create(ctx, "video-background-remover", map[string]any{
	"source_video":            []string{"https://example.com/source.mp4"},
	"refine_foreground_edges": true,
	"subject_is_person":       true,
})
if err != nil {
	return err
}

result, err := client.Apps.Generations.Wait(ctx, "video-background-remover", task.Data.ID)

Rust

let app = client.apps().retrieve("video-background-remover").await?;
println!("{:?}", app.data.input_parameters);

let mut body = serde_json::Map::new();
body.insert(
    "source_video".to_string(),
    serde_json::json!(["https://example.com/source.mp4"]),
);
body.insert("refine_foreground_edges".to_string(), serde_json::json!(true));
body.insert("subject_is_person".to_string(), serde_json::json!(true));

let task = client
    .apps()
    .generations()
    .create("video-background-remover", body)
    .await?;

let result = client
    .apps()
    .generations()
    .wait("video-background-remover", &task.data.id, WaitOptions::default())
    .await?;

Swift

let app = try await client.apps.retrieve("video-background-remover")
print(app.data.inputParameters)

let task = try await client.apps.generations.create(
    "video-background-remover",
    body: [
        "source_video": ["https://example.com/source.mp4"],
        "refine_foreground_edges": true,
        "subject_is_person": true
    ]
)

let result = try await client.apps.generations.wait(
    "video-background-remover",
    task.data.id
)

Dart / Flutter

final app = await client.apps.retrieve('video-background-remover');
print(app.data.inputParameters);

final task = await client.apps.generations.create('video-background-remover', {
  'source_video': ['https://example.com/source.mp4'],
  'refine_foreground_edges': true,
  'subject_is_person': true,
});

final result = await client.apps.generations.wait(
  'video-background-remover',
  task.data.id,
);

Elixir

{:ok, app} = Buble.Apps.retrieve(client, "video-background-remover")
IO.inspect(app["data"]["input_parameters"])

{:ok, task} =
  Buble.Apps.Generations.create(client, "video-background-remover", %{
    source_video: ["https://example.com/source.mp4"],
    refine_foreground_edges: true,
    subject_is_person: true
  })

{:ok, result} =
  Buble.Apps.Generations.wait(client, "video-background-remover", task["data"]["id"])

Java

Envelope<PublicApp> app = client.apps().retrieve("video-background-remover");
System.out.println(app.getData().getInputParameters());

Envelope<AppGenerationTask> task = client.apps().generations().create(
        "video-background-remover",
        Map.of(
                "source_video", List.of("https://example.com/source.mp4"),
                "refine_foreground_edges", true,
                "subject_is_person", true));

Envelope<AppGenerationTask> result = client.apps().generations().wait(
        "video-background-remover",
        task.getData().getId());

.NET

var app = await client.Apps.RetrieveAsync("video-background-remover");
Console.WriteLine(app!.Data!.InputParameters);

var task = await client.Apps.Generations.CreateAsync(
    "video-background-remover",
    new Dictionary<string, object?>
    {
        ["source_video"] = new[] { "https://example.com/source.mp4" },
        ["refine_foreground_edges"] = true,
        ["subject_is_person"] = true
    });

var result = await client.Apps.Generations.WaitAsync(
    "video-background-remover",
    task!.Data!.Id!);

PHP

$app = $client->apps()->retrieve('video-background-remover');
var_dump($app['data']['input_parameters']);

$task = $client->apps()->generations()->create('video-background-remover', [
    'source_video' => ['https://example.com/source.mp4'],
    'refine_foreground_edges' => true,
    'subject_is_person' => true,
]);

$result = $client->apps()->generations()->wait(
    'video-background-remover',
    $task['data']['id']
);

Ruby

app = client.apps.retrieve("video-background-remover")
puts app.dig("data", "input_parameters")

task = client.apps.generations.create("video-background-remover", {
  "source_video" => ["https://example.com/source.mp4"],
  "refine_foreground_edges" => true,
  "subject_is_person" => true
})

result = client.apps.generations.wait("video-background-remover", task.dig("data", "id"))

App 参考页面