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

# Projects

> 미디어를 업로드하고 가라오케 프로젝트를 생성, 조회, 목록 조회, 삭제합니다

Projects는 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에 따라 달라집니다. [supported URLs list](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
  reference](/ko/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?)`

프로젝트를 생성하지 않고, 프로젝트 생성에 필요한 크레딧을 견적냅니다.

```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?)`

서명된 URL로 파일 바이트를 PUT합니다.

```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>

업로드가 non-2xx 상태를 반환하면 코드 `UPLOAD_FAILED`의 `YoukaRequestError`를 throw합니다.

## `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>
  삭제는 영구적입니다. 재시도가 안전하도록 idempotency key와 함께 사용하세요.
</Warning>

## 다음 단계

* [Stems](/ko/sdk/stems) — stem 분리를 다시 실행
* [Lyrics sync](/ko/sdk/lyrics-sync) — 가사를 다시 동기화
* [Exports](/ko/sdk/exports) — 완성된 비디오 렌더링
* [Tasks](/ko/sdk/tasks) — `client.projects.wait`로 프로젝트 operation을 대기
