> ## Documentation Index
> Fetch the complete documentation index at: https://docs.youka.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 自动化歌词视频

> 选择模型、校正时间轴，并导出指定的项目版本

在这些示例中使用 `@youka/sdk` 和 `@youka/cli` 0.2.0 或更高版本。通过 API 创建的项目会一直保留在你的 Youka 资源库中。

## 选择工作流和模型

`kind` 用于选择项目工作流。`karaoke` 会分离音轨（stems）。`lyric-video` 使用原始音频，不会为分离计费。省略 `kind` 会保留现有的 karaoke 行为。歌词处理是一个独立选择：对齐（alignment）、转写（transcription）或不处理。

在选择模型之前，先发现可用模型并确认账号是否具备使用资格。

```sh theme={null}
youka capabilities --json
```

```ts theme={null}
const capabilities = await client.capabilities.get();
const eligibleModels = capabilities.models.filter(
  (model) => model.eligible && model.workflows.includes("lyric-video"),
);
```

响应会描述模型 ID、操作、音频输入、提供文本（supplied-text）的语义、工作流、语言支持以及所需功能。`suppliedTextHandling` 用于区分对齐、提供方关键术语（provider keyterms）以及在转写后的校正。资格（eligibility）反映的是已认证账号的状态。能力发现（discovery）描述的是已配置的支持，而不是对提供方可用性的保证。请求校验与价格报价仍以实际返回为准。

需要人声分离（isolated vocals）的模型无法处理不分离的歌词视频。要使用它们，请在 karaoke 工作流中配合一个能产出人声音轨的分离模型。Wav2Vec2 是对齐模型。ElevenLabs Scribe 是转写模型。提供的转写文本只是参考引导，并不保证输出文本完全一致。

## 创建与报价项目

```ts theme={null}
import { AlignmentModel, YoukaClient } from "@youka/sdk";

const client = new YoukaClient({ apiKey: process.env.YOUKA_API_KEY! });
const operation = await client.projects.create(
  {
    kind: "lyric-video",
    source: { type: "path", path: "./song.wav" },
    title: "Example song",
    lyricsSource: {
      type: "transcribe",
      syncModel: AlignmentModel.ElevenLabsTranscription,
      lyrics: "Optional reference lyrics",
    },
  },
  { idempotencyKey: "example-song-create-v1" },
);
const { project } = await client.projects.wait(operation);
```

用一个声明支持歌词视频的模型来对齐你提供的歌词。

```sh theme={null}
youka project create ./song.wav --kind lyric-video --mode align \
  --sync-model audioshake-alignment --lyrics 'Line one
Line two' --json
```

对于 karaoke 项目保留分离。省略 `--kind` 或传入 `--kind karaoke`，并选择 `--split-model`。用 `--mode none` 在不进行歌词处理的情况下创建项目。歌词视频（lyric-video）请求会拒绝分离相关选项。

在创建之前，对同一份来源、工作流和处理选项进行报价。

```sh theme={null}
youka project quote ./song.wav --kind lyric-video --mode transcribe \
  --sync-model elevenlabs-transcription --json
```

报价会检查与创建相同的模型兼容性、语言策略、账号功能、时长与存储规则。其积分明细中，歌词视频的分离积分为零。报价不会预留产能或积分；在创建前账号状态可能发生变化。

## 无需处理任务即可校正时间轴

通过 `GET /projects/{projectId}/alignments` 读取对齐结果，然后通过 `GET /projects/{projectId}/alignments/{alignmentId}` 获取选中的资源。列表包含 `alignments`、`selectedAlignmentId` 和 `selectionRevision`。每个资源包含时间轴载荷、`revision`、`selected` 和 `selectionRevision`。

更新会替换完整的 `alignment` 对象。它们不会运行同步，也不会创建处理积分冻结。编辑时请保留 ID、文本、行/词/子词索引、歌手信息以及翻译。时间是绝对的小数秒。把所需校正一次性体现在提交的数值里。不要再把同样的校正作为导出偏移重复叠加。

```ts theme={null}
const snapshot = await client.projects.alignments.list(project.id);
const current = snapshot.alignments.find(
  (alignment) => alignment.id === snapshot.selectedAlignmentId,
);
if (!current) throw new Error("Project has no alignment to edit");

const corrected = structuredClone(current.alignment);
corrected.items[0]!.start = 0.75;
corrected.items[0]!.end = 1.25;

const saved = await client.projects.alignments.update(project.id, current.id, {
  alignment: corrected,
  expectedRevision: current.revision,
  select: true,
  expectedSelectionRevision: snapshot.selectionRevision,
});
```

使用 `select: true` 可原子性地保存并选中。若要在不替换现有时间轴的情况下选中已有时间轴，请调用 `client.projects.alignments.select(projectId, alignmentId, { expectedRevision, expectedSelectionRevision })`。

对齐修订号（alignment revision）用于保护时间轴内容。选择修订号（selection revision）用于保护当前激活的是哪条对齐。过期的令牌会返回 HTTP 409。请获取最新资源、对比编辑内容，并提交一次明确的替换。不要自动刷新令牌并覆盖其他编辑者的更改。

CLI 接受一个 JSON 文件或标准输入。导入文件包含 `{ "alignment": { "items": [...] }, "expectedRevision": "..." }`。在原子选择时包含 `select` 和 `expectedSelectionRevision`。导出的资源也会包含响应元数据，因此导入前请先提取请求字段。

```sh theme={null}
youka project timings list proj_example --json
youka project timings get proj_example aln_example --json > timing-resource.json
youka project timings import proj_example aln_example --body corrected-request.json --json
youka project timings select proj_example aln_example \
  --expected-revision "$ALIGNMENT_REVISION" \
  --expected-selection-revision "$SELECTION_REVISION" --json
```

导入最多接受 100,000 个条目。每个结束时间必须严格大于其开始时间。无效数字、负时间、无效索引、零长度或反向区间，以及超出已知项目时长的时间轴都会被拒绝。合法的重叠与对唱时间轴仍受支持。在外部编辑后，请重新打开或刷新已打开的应用项目。

## 导出指定版本

```sh theme={null}
youka project versions proj_example --json
youka export create proj_example --version-id ver_example --local \
  --transparent --mute-all --fps 30 --output ./overlay.mov --json
```

在导出请求与设置更新中，以及设置 GET 查询中，`versionId` 都是可选的。省略它会保留主版本（primary-version）行为。选中的对齐属于项目本身，而样式与设置属于所选版本。

```ts theme={null}
const versions = await client.projects.versions.list(project.id);
const versionId = versions[0]!.id;
const stemVolumes = Object.fromEntries(
  project.stems.map((stem) => [stem.id, 0]),
);
const payload = await client.exports.prepareLocal(project.id, {
  versionId,
  transparent: true,
  stemVolumes,
  fps: 30,
});
console.log(payload.versionId, payload.alignmentId, payload.alignmentRevision);

await client.exports.create(project.id, {
  target: "local",
  versionId,
  transparent: true,
  stemVolumes,
  fps: 30,
  outputPath: "./overlay.mov",
});
```

云端导出操作句柄与本地载荷都包含 `versionId`、`alignmentId` 和 `alignmentRevision`。这些字段用于标识该次导出或已准备载荷所使用的时间轴快照。每次准备都会读取当前状态，因此之后单独发起的导出可能会观察到更新的编辑。若外部渲染器必须严格渲染该已准备的快照，请保存返回的载荷，并在签名媒体 URL 过期时刷新它。

透明本地输出在 MOV 容器中使用 ProRes 4444。`--mute-all` 会把所有项目 stems 映射为零音量。静音与缺少音频流是两种不同的输出属性；如果下游工具要求其中之一，请检查渲染文件。本地渲染不消耗云导出积分，并仍受现有功能资格限制。

单词时间轴、全局音频偏移与歌词提前量（lyric anticipation）是相互独立的设置。现有布局控制每行的可见性。本次发布不承诺为每一行提供固定的揭示间隔，也不会新增单独的伴奏轨附件 API。

## 安全地运行一个目录（catalogue）

维护一个清单（manifest），包含稳定的条目 ID、来源指纹、操作设置、幂等键、项目/任务 ID 以及最近一次终态结果。仅在重试同一请求时复用同一个键。在等待完成前先持久化创建结果，以便重启后可以继续轮询。上传与本地文件准备有各自的生命周期；创建幂等键不会对每一次上传都做去重。

可运行的 [catalogue example](/examples/catalogue.js) 接受预先上传的输入文件 ID，并为每个条目持久化一个检查点。清单示例如下。

```json theme={null}
[
  {
    "id": "song-001",
    "request": {
      "kind": "lyric-video",
      "inputFileId": "owned-upload-id",
      "title": "Example song",
      "lyricsSource": {
        "type": "transcribe",
        "syncModel": "elevenlabs-transcription"
      }
    }
  }
]
```

将示例下载为 `catalogue.ts`。安装其依赖并设置 `YOUKA_API_KEY`。使用一个 Node.js 项目，并在其 `package.json` 中设置 `"type": "module"`。

```sh theme={null}
npm install @youka/sdk@^0.2.0 tsx
npx tsx ./catalogue.ts ./manifest.json ./catalogue-state 1
```

用相同的清单与状态目录重新运行，以继续轮询已接受的任务，或用原始键重试一次不确定是否提交成功的请求。已完成与失败的任务会被记录并跳过。更改后的请求需要新的条目 ID。目录锁可防止两个进程同时提交同一份清单。在硬崩溃后，只有在确认其中记录的 PID 已不再运行时，才移除过期的 `.lock`。

从单个进行中的条目开始。只有在测量了你账号的处理延迟与限流响应之后，再增加一个有上限的 worker 数。对传输错误与限流进行带退避的重试，并遵循 `Retry-After`。不要盲目重试校验错误、时间轴冲突或终态处理失败。认证错误需要有效凭据；这不代表处理任务失败。

保留现有的云任务轮询与失败积分处理。这里不会引入批量端点、webhook 承诺、价格变化、授权变化或保留策略。
