> ## 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.

# 项目

> 上传媒体并创建、查看、列出和删除卡拉 OK 项目

项目是 Youka 中的顶层资源。每个项目都拥有其源文件、分离的 stem、同步歌词、导出结果以及项目设置。

## 创建项目

在大多数情况下，使用 `client.projects.create()` ——它会为你处理上传。

```ts theme={null}
const operation = await client.projects.create({
  source: { type: "path", path: "./song.mp3" },
  lyricsSource: { type: "transcribe" },
});
// => ProjectOperation
```

### Source 类型

<Tabs>
  <Tab title="path">
    从磁盘读取文件。

    ```ts theme={null}
    source: {
      type: "path",
      path: "./song.mp3",
      contentType: "audio/mpeg", // optional, inferred if omitted
    }
    ```
  </Tab>

  <Tab title="bytes">
    内存中的 `Blob`、`File`、`ArrayBuffer` 或 `Uint8Array`。

    ```ts theme={null}
    source: {
      type: "bytes",
      data: fileBuffer,
      filename: "song.mp3",
      contentType: "audio/mpeg", // optional
    }
    ```
  </Tab>

  <Tab title="url">
    远程 HTTP 或 HTTPS URL。

    支持的托管页面取决于 yt-dlp。请参见[支持的 URL 列表](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)。

    ```ts theme={null}
    source: {
      type: "url",
      url: "https://example.com/song.mp4",
      maxVideoQuality: "1080p", // optional: "720p", "1080p", "4k", or "best"
    }
    ```

    `maxVideoQuality` 控制 URL source 的最大下载视频质量。默认值为 `1080p`。SDK 会在该上限之内使用可用的最佳质量；如果平台不提供带上限的流，则会回退到可用的最佳格式。若不设上限，请使用 `best`。

    <Note>
      在 Node.js 和 Bun 上，SDK 会在首次使用时自动确保所需的 URL 依赖二进制文件（`ffmpeg`、`ffprobe`、`yt-dlp`）。对应的 CLI 命令是 `youka deps ensure --for url`。
    </Note>
  </Tab>
</Tabs>

### 其他字段

<ParamField path="title" type="string">
  项目标题。默认使用源文件名。
</ParamField>

<ParamField path="splitModel" type="SplitModel">
  Stem 分离模型。默认值为 `mdx23c`。参见 [Split model 参考](/zh/desktop/split-model)。
</ParamField>

<ParamField path="presetId" type="string">
  在创建时应用可复用的预设。
</ParamField>

<ParamField path="lyricsSource" type="LyricsSource">
  配置歌词同步。见下文。
</ParamField>

### Lyrics source

```ts theme={null}
// 从音频转写歌词
lyricsSource: {
  type: "transcribe",
  syncModel: "audioshake-transcription", // optional
  language: "en",                        // optional
}

// 将精确歌词与音频对齐
lyricsSource: {
  type: "align",
  lyrics: "First line\nSecond line\n...",
  syncModel: "audioshake-alignment",
}

```

## `client.projects.create(input, options?)`

当你已经有上传好的 `inputFileId` 时，`client.projects.create()` 也接受更底层的 `inputFile` source。

```ts theme={null}
const upload = await client.uploads.create({
  filename: "song.mp3",
  contentType: "audio/mpeg",
  contentLength: buffer.byteLength,
});

await client.uploads.upload(upload.uploadUrl, buffer, {
  contentType: "audio/mpeg",
});

const operation = await client.projects.create({
  source: {
    type: "inputFile",
    inputFileId: upload.inputFileId,
    filename: "song.mp3",
  },
  title: "My Song",
});
```

## `client.projects.quote(input, options?)`

在不创建项目的情况下，报价创建项目所需的 credits。

```ts theme={null}
const quote = await client.projects.quote({
  source: { type: "path", path: "./song.mp3" },
  lyricsSource: { type: "transcribe", language: "en" },
  splitModel: "mdx23c",
});

console.log(quote.creditsRequired, quote.sufficientBalance);
```

`client.projects.quote(...)` 接受与 `client.projects.create(...)` 相同的 `source` 形式，包括 URL 的 `maxVideoQuality`。如果你已经知道媒体时长，并且不想为了报价而上传文件，可以传入更底层的 REST 形状：

```ts theme={null}
const quote = await client.projects.quote({
  durationSeconds: 210,
  lyricsSource: null,
  splitModel: "mdx23c",
});
```

## `client.uploads.create(body, options?)`

分配一个上传槽位并获取签名 URL。

```ts theme={null}
const upload = await client.uploads.create({
  filename: "song.mp3",
  contentType: "audio/mpeg",
  contentLength: 4_521_344,
});
// => { inputFileId, uploadUrl }
```

## `client.uploads.upload(uploadUrl, body, options?)`

将文件字节以 PUT 方式上传到签名 URL。

```ts theme={null}
await client.uploads.upload(upload.uploadUrl, fileBuffer, {
  contentType: "audio/mpeg",
  signal: abortController.signal,
});
```

<ParamField path="body" type="FetchBody" required>
  任何与 `fetch` 兼容的 body：`Blob`、`File`、`ArrayBuffer`、`Uint8Array`、`ReadableStream` 或 `string`。
</ParamField>

如果上传返回非 2xx 状态，会抛出 code 为 `UPLOAD_FAILED` 的 `YoukaRequestError`。

## `client.projects.get(projectId, options?)`

获取完整的项目状态，包括 stem、歌词和导出结果。

```ts theme={null}
const project = await client.projects.get("prj_abc123");
console.log(project.title, project.stems, project.lyrics);
```

## `client.projects.update(projectId, body, options?)`

对项目元数据打补丁更新。

```ts theme={null}
const project = await client.projects.update("prj_abc123", {
  title: "Updated title",
  artists: ["Artist name"],
});
```

至少传入 `title` 或 `artists` 之一。

## `client.projects.list(options?)`

列出经认证账户拥有的所有项目。

```ts theme={null}
const projects = await client.projects.list();
projects.forEach((p) => console.log(p.id, p.title));
```

返回项目列表条目的数组（形状比 `getProject` 更精简）。

## `client.projects.delete(projectId, options?)`

删除项目以及其关联的所有 stem、歌词和导出结果。

```ts theme={null}
await client.projects.delete("prj_abc123", {
  idempotencyKey: "delete-prj_abc123",
});
```

<Warning>
  删除是永久性的。请配合幂等键使用，以便重试时安全。
</Warning>

## 接下来

* [Stems](/zh/sdk/stems) — 重新运行 stem 分离
* [Lyrics sync](/zh/sdk/lyrics-sync) — 重新同步歌词
* [Exports](/zh/sdk/exports) — 渲染最终视频
* [Tasks](/zh/sdk/tasks) — 使用 `client.projects.wait` 等待项目操作完成
