# 편집

> TypeScript로 팟캐스트와 긴 동영상을 편집합니다. 호출 한 번으로 에피소드를 다듬고, 무음을 감지하고, 녹음을 동기화하고, 컷을 계획하고, 편집 결정 목록을 렌더링합니다.

Source: https://nodaro.ai/ko/docs/developers/sdk/editing

<strong>`client.edit`</strong>에는 팟캐스트와 긴 동영상을 위한 편집 도구가 들어 있습니다. 호출 한 번으로 에피소드 전체를 다듬고, 무음을 찾고, 여러 녹음이 서로 얼마나 어긋나 있는지 측정합니다. 또한 스크립트로 컷을 계획하고, **편집 결정 목록**(EDL)을 완성된 동영상 파일이나 오디오 파일로 렌더링합니다. 대부분의 메서드는 작업을 시작하고 그 ID를 반환하며, 이 작업은 [`client.jobs.getStatus()`](https://nodaro.ai/docs/developers/sdk/jobs-and-executions)로 폴링합니다. `estimateTightenEpisode()`는 실행 가격을 견적하고, `remapTranscript()`는 요청 없이 로컬에서 실행됩니다.

## 메서드
| 메서드 | 엔드포인트 | 기능 |
| --- | --- | --- |
| [`edit.tightenEpisode(input, options?)`](#edittighten-episode) | `POST /v1/tighten-episode` | 호출 한 번으로 녹음 하나를 완성된 에피소드로 다듬기 |
| [`edit.estimateTightenEpisode(input)`](#edittighten-episode) | `POST /v1/tighten-episode/estimate` | **에피소드 다듬기** 실행 가격 견적. 아무것도 만들거나 청구하지 않음 |
| [`edit.continueUseCase(input, options?)`](#editcontinueusecaseinput-options) | `POST /v1/tighten-episode/continue` | **에피소드 다듬기** 실행을 새 작업으로 이어 가기 |
| [`edit.silenceDetect(input)`](#editsilencedetectinput) | `POST /v1/silence-detect` | 오디오 소스나 동영상 소스의 무음 구간 찾기 |
| [`edit.audioSync(input)`](#editaudiosyncinput) | `POST /v1/audio-sync` | 녹음 2~6개 사이의 시간 오프셋 측정 |
| [`edit.editPlan(input)`](#editeditplaninput) | `POST /v1/edit-plan` | 스크립트로 간결한 컷, 짧은 클립, 챕터, 트레일러 계획 |
| [`edit.applyEdl(input)`](#editapplyedlinput) | `POST /v1/apply-edl` | EDL을 동영상 파일이나 오디오 파일로 렌더링 |
| [`edit.remapTranscript(edl, transcript)`](#editremaptranscriptedl-transcript) | 없음, 로컬 | 스크립트의 타이밍을 편집된 타임라인으로 옮기기 |

## 코드로 팟캐스트 편집하기
에피소드 전체라면 [`tightenEpisode()`](#edittighten-episode)가 아래의 모든 단계를 호출 한 번으로 실행합니다. 각 단계를 직접 제어해야 할 때는 단계를 하나씩 쓰세요.

### 녹음을 텍스트로 변환
단어 타이밍을 반환하는 엔진으로 [`client.audio.transcribe()`](https://nodaro.ai/docs/developers/sdk/voices-and-audio#audiotranscribeinput)를 실행하세요. 그 `output_data.json`이 계획에 필요한 스크립트입니다.

### 무음 찾기
마스터 녹음에서 `silenceDetect()`를 실행하세요. 그 `output_data.json`에 무음 구간이 들어 있습니다.

### 컷 계획
스크립트, 소스, 무음 구간을 넣어 `editPlan()`을 `tighten` 모드로 실행하세요. 계획은 `unwrapEditPlanOutput()`으로 읽으세요.

### 렌더링
계획을 `applyEdl()`에 전달하세요. 완료된 작업에 편집된 동영상이나 오디오가 들어 있습니다.

Workflow: 같은 편집 과정을 노드로 나타낸 것입니다. 텍스트로 변환하고 무음을 찾은 다음, 더 간결한 컷을 계획하고 렌더링합니다.

- 동영상 업로드 → 음성 텍스트 변환
- 동영상 업로드 → 무음 감지
- 음성 텍스트 변환 → 편집 계획
- 무음 감지 → 편집 계획
- 편집 계획 → EDL 적용

```ts

async function outputOf(jobId: string): Promise<any> {
for (;;) {
const { data } = await client.jobs.getStatus(jobId)
if (data.status === "completed") return data.output_data
if (data.status === "failed" || data.status === "cancelled") throw new Error(data.error_message ?? data.status)
await new Promise((resolve) => setTimeout(resolve, 3_000))
}
}

// 1. Transcribe with word timings
const tr = await client.audio.transcribe({ audioUrl: masterUrl, provider: "elevenlabs-stt" })
const transcript = (await outputOf(tr.jobId)).json

// 2. Find the silence
const sd = await client.edit.silenceDetect({ audioUrl: masterUrl, thresholdDb: -35 })
const silence = (await outputOf(sd.jobId)).json

// 3. Plan a tighter cut
const plan = await client.edit.editPlan({
mode: "tighten",
planTier: "standard",
transcript,
sources: [{ id: "ep", url: masterUrl, kind: "video", role: "master-audio" }],
silence,
})
const edl = unwrapEditPlanOutput(await outputOf(plan.jobId))

// 4. Render the plan
const render = await client.edit.applyEdl({ edl, output: "video", quality: "final" })
const { videoUrl } = await outputOf(render.jobId)
```

같은 단계를 노드로도 실행할 수 있습니다. [음성 텍스트 변환](https://nodaro.ai/docs/nodes/audio/transcribe), [무음 감지](https://nodaro.ai/docs/nodes/audio/silence-detect), [편집 계획](https://nodaro.ai/docs/nodes/video/edit-plan), [EDL 적용](https://nodaro.ai/docs/nodes/video/apply-edl)입니다.

## client.edit
### edit.tightenEpisode(input, options?)와 estimateTightenEpisode(input)
[**에피소드 다듬기**](https://nodaro.ai/docs/nodes/video/tighten-episode) 노드처럼 호출 한 번으로 팟캐스트 녹음을 다듬습니다. 실행은 녹음을 텍스트로 변환하고, 다듬은 컷을 계획해 렌더링합니다. 설정에 따라 에피소드에 자막을 넣고, 프레이밍하고, 그 아래에 배경 음악을 깝니다. `mediaUrl`만 필수이며, 생략한 필드는 노드의 기본값을 씁니다.

```ts
tightenEpisode(input: TightenEpisodeInput, options?: { idempotencyKey?: string }): Promise<TightenEpisodeJobResult>
estimateTightenEpisode(input: TightenEpisodeInput): Promise<{ data: TightenEpisodeEstimate }>
```

<TypeTable
type={{
mediaUrl: { type: 'string', required: true, description: "녹음입니다. 최대 3시간 길이의 동영상 또는 오디오 URL입니다." },
musicUrl: { type: 'string', description: "배경 음악입니다. 목소리 아래로 볼륨이 낮춰지고 에피소드와 함께 끝납니다." },
output: { type: '"video" | "audio"', description: "렌더링할 대상입니다. 오디오 녹음은 항상 오디오 에피소드가 됩니다." },
framing: { type: '{ aspect, layout } | null', description: "화자 뷰로 동영상 에피소드를 프레이밍합니다. aspect는 16:9, 9:16, 1:1, 4:5 중 하나입니다. layout은 auto, single, side-by-side, stacked, grid, pip 중 하나입니다. null이거나 없으면 프레이밍하지 않습니다." },
captions: { type: 'boolean', default: 'true', description: "동영상 에피소드의 자막입니다." },
captionStyle: { type: 'CaptionStyle', description: "자막의 모양입니다." },
reviewBeforeRender: { type: 'boolean', description: "사람이 검토할 수 있도록 빠른 미리보기에서 멈춥니다. 이후 continueUseCase를 render-final 모드로 호출해 최고 품질의 에피소드를 렌더링하세요." },
planTier: { type: '"economy" | "standard" | "premium"', description: "플래너의 모델 등급입니다." },
instructions: { type: 'string', description: "플래너에 주는 편집 지시로, 최대 4,000자입니다." },
styleGuide: { type: 'string', description: "플래너에 주는 스타일 가이드로, 최대 4,000자입니다." },
musicDuckAmount: { type: 'number', default: '75', description: "목소리 아래에서 음악을 얼마나 낮출지로, 0~100입니다." },
}}
/>

```ts
const quote = await client.edit.estimateTightenEpisode({ mediaUrl: episodeUrl })
console.log(quote.data.totalCredits)

const { jobId } = await client.edit.tightenEpisode(
{ mediaUrl: episodeUrl, framing: { aspect: "9:16", layout: "auto" } },
{ idempotencyKey: crypto.randomUUID() },
)
```

`tightenEpisode()`는 `{ jobId, executionId?, deduped? }`를 반환합니다. 같은 요청을 같은 `idempotencyKey`로 다시 시도하면, 응답은 이미 시작된 실행이며 `deduped: true`가 붙습니다. `jobId`를 폴링하세요. 완료된 작업의 `output_data`에는 에피소드가 `videoUrl`로, 오디오 에피소드라면 `audioUrl`로 담깁니다. 또한 `json`에 계획의 EDL이, `quality`에 검토용 미리보기라면 `"proxy"`, 그 밖에는 `"final"`이 담깁니다.

`estimateTightenEpisode()`는 `{ totalCredits, stepsCredits, feeCredits, durationSec }`를 반환합니다. 실행의 각 단계를 자체 요금으로 계산하고 실행당 고정 수수료를 더한 값입니다. 편집 결과를 알기 전에 녹음 전체 길이로 렌더링 가격을 매기므로 견적은 높게 나오는 편입니다. 청구는 실제로 렌더링된 분을 따릅니다. 녹음 길이를 읽을 수 없으면 `durationSec`은 `null`입니다. 크레딧이 없는 에디션에서는 모든 크레딧 금액이 0입니다.

두 메서드 모두 무언가를 만들거나 청구하기 전에 거부합니다. 400에는 `validation_error`, `media_unreadable`, `media_has_no_audio`, `media_too_long`, `music_has_no_audio`가 담깁니다. 403에는 `node_not_available`이 담깁니다. 503에는 `nodaro_connection_required` 또는 `use_case_unavailable`이 담깁니다. 잔액이 견적을 감당하지 못하면 `tightenEpisode()`는 `InsufficientCreditsError`도 발생시킵니다. 셀프 호스팅 설치에서는 Nodaro Cloud에서 실행되는 단계에 [Nodaro Cloud 연결](https://nodaro.ai/docs/self-hosting/cloud-connect)이 필요합니다.

### edit.continueUseCase(input, options?)
**에피소드 다듬기** 실행을 새 작업으로 이어 가며, `tightenEpisode()`처럼 `{ jobId, executionId?, deduped? }`를 반환합니다.

```ts
continueUseCase(input: UseCaseContinueInput, options?: { idempotencyKey?: string }): Promise<TightenEpisodeJobResult>
```

<TypeTable
type={{
jobId: { type: 'string', required: true, description: "이어 갈 실행입니다. tightenEpisode나 이전 이어 가기가 반환한 작업입니다." },
mode: { type: '"from-failed" | "render-final"', required: true, description: "from-failed는 끝난 단계를 유지하고 나머지를 실행합니다. 실패한 실행의 수수료가 환불되었으므로 수수료가 다시 예약됩니다. render-final은 검토용 미리보기 이후 미리보기의 설정으로 최고 품질의 에피소드를 렌더링하며, 수수료가 없습니다." },
editedEdl: { type: 'unknown', description: "render-final 전용입니다. 검토자가 수정한 계획을 EDL로 받습니다." },
}}
/>

```ts
const preview = await client.edit.tightenEpisode({ mediaUrl: episodeUrl, reviewBeforeRender: true })
// ...after a person reviews the preview:
const final = await client.edit.continueUseCase({ jobId: preview.jobId, mode: "render-final" })
```

완료된 **최종 렌더링**이 있어도 편집을 바꾼 같은 미리보기의 **최종 렌더링**을 다시 할 수 있습니다. 그러면 최종 단계가 다시 실행되고 다시 청구되며, 여전히 수수료는 없습니다. 완전히 같은 편집을 다시 보내면 거부됩니다. 거부는 실행이 생기기 전에 발생합니다. 다른 작업을 가리키는 거부는 `ContinuationRefusedError`를 발생시킵니다. 이 오류는 상태 409의 `NodaroError`이며, `jobId`가 그 작업입니다. 코드는 세 가지입니다. `already_running`은 이어 가기가 진행 중이거나 완료되었다는 뜻이므로 그 작업을 따라가세요. `continuation_superseded`는 이후의 이어 가기도 실패했다는 뜻이므로 대신 그 작업을 이어 가세요. `render_final_already_rendered`는 그 작업의 결과를 쓰라는 뜻입니다. 그 밖의 거부에는 `not_found`, `continuation_not_found`, `continuation_not_failed`, `continuation_not_completed`가 있습니다. `continuation_settling`은 잠시 후 다시 시도하라는 뜻이고, `render_final_not_preview`는 그 작업이 검토용 미리보기가 아니라는 뜻입니다. 결제자가 감당할 수 없는 실행은 `InsufficientCreditsError`를 발생시킵니다.

### edit.silenceDetect(input)
오디오 소스나 동영상 소스의 무음 구간을 찾습니다. AI 모델 없이 서버에서 실행됩니다.

```ts
silenceDetect(input: SilenceDetectInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
audioUrl: { type: 'string', required: true, description: "오디오 소스나 동영상 소스입니다." },
thresholdDb: { type: 'number', default: '-35', description: "이보다 작은 소리를 무음으로 간주하는 음량이며, 단위는 dBFS이고 0 이하입니다." },
minSilenceMs: { type: 'number', default: '700', description: "보고할 가장 짧은 무음 길이이며, 단위는 밀리초입니다." },
padMs: { type: 'number', default: '120', description: "음성 앞뒤에 남겨 두는 여유분이며, 단위는 밀리초입니다. 각 무음 구간이 이만큼 줄어듭니다." },
workflowId: { type: 'string', description: "이 실행을 실행 기록에 표시할 워크플로입니다." },
}}
/>

```ts
const { jobId } = await client.edit.silenceDetect({ audioUrl: masterUrl, minSilenceMs: 900 })
```

완료된 작업의 `output_data.json`은 `SilenceRanges` 객체 `{ version, ranges: [{ startMs, endMs }], durationMs }`입니다. 이 객체 전체를 `silence`로 `editPlan()`에 전달하세요.

### edit.audioSync(input)
[오디오 동기화](https://nodaro.ai/docs/nodes/audio/audio-sync) 노드처럼, 한 대화를 담은 녹음 2~6개의 시계가 서로 얼마나 어긋나 있는지 소리로 측정합니다. AI 모델 없이 서버에서 실행됩니다. 첫 번째 소스를 제외하고 소스 하나당 11크레딧이 듭니다. 소스 2개는 11크레딧, 4개는 33크레딧, 6개는 55크레딧입니다.

```ts
audioSync(input: AudioSyncInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
sources: { type: 'Array<{ id: string; url: string }>', required: true, description: "녹음 2~6개입니다. 각 id는 1~200자이고 고유해야 하며, 해당 오프셋의 sourceId로 돌아옵니다. 같은 녹음에는 EDL에서 쓰는 id를 사용하세요." },
reference: { type: 'string', description: "모든 오프셋을 측정하는 기준 소스의 id입니다. 기준 소스 자체의 오프셋은 0입니다. 기본값은 첫 번째 소스입니다." },
workflowId: { type: 'string', description: "이 실행을 표시할 워크플로입니다." },
}}
/>

```ts
const { jobId } = await client.edit.audioSync({
sources: [
{ id: "mic", url: micUrl },
{ id: "camA", url: camAUrl },
],
reference: "mic",
})
```

완료된 작업의 `output_data.json`은 `AudioSyncResult`입니다.

```ts
{
version: number
reference: string // the source every offset is measured against
offsets: Array<{
sourceId: string
offsetMs: number              // referenceMs = sourceMs + offsetMs
confidence: number            // 0 to 1; below 0.5 a note asks you to check by ear
driftMsPerHour: number | null // measured, never corrected; null when the overlap was too short
}>
notes: string[] // low confidence, drift above 33 ms over the overlap, no shared sound
}
```

마스터 녹음을 `reference`로 지정하면, 각 `offsetMs`가 그대로 EDL에서 해당 소스의 `offsetMs`가 됩니다. 잘못된 요청은 크레딧이 예약되기 전에, `code`가 `validation_error`인 `NodaroError`로 거부됩니다. 소스가 2개보다 적거나 6개보다 많은 경우, id가 중복된 경우, `reference`가 id 중 하나가 아닌 경우가 여기에 해당합니다.

### edit.editPlan(input)
[편집 계획](https://nodaro.ai/docs/nodes/video/edit-plan) 노드처럼, 타이밍이 있는 스크립트로 편집을 계획합니다. 네 종류의 계획 중 하나를 작성합니다. 녹음 전체의 간결한 컷, 짧은 클립 묶음, 챕터, 또는 트레일러입니다.

```ts
editPlan(input: EditPlanInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
mode: { type: '"tighten" | "clips" | "chapters" | "trailer"', required: true, description: "tighten은 쉬는 구간과 군말을 없애고, clips는 짧은 클립을 잘라 내고, chapters는 녹음을 챕터로 나누고, trailer는 트레일러를 만듭니다." },
planTier: { type: '"economy" | "standard" | "premium"', required: true, description: "모델 등급입니다. 품질과 가격이 정해집니다." },
transcript: { type: 'Transcript', required: true, description: "타이밍이 있는 스크립트이며, transcribe 작업의 output_data.json입니다." },
sources: { type: 'EditPlanSource[]', required: true, description: "소스 1~6개입니다. 각각 { id, url, kind, role?, speakers?, offsetMs? }이며, kind는 video 또는 audio입니다." },
silence: { type: 'SilenceRanges', description: "silenceDetect 작업의 output_data.json입니다. 객체 전체를 전달하세요. ranges가 없는 객체는 무시됩니다." },
instructions: { type: 'string', description: "자유 형식의 편집 지시입니다." },
styleGuide: { type: 'string', description: "따를 스타일 가이드입니다." },
count: { type: 'number', description: "clips 모드에서 잘라 낼 클립 수입니다." },
targetDurationSec: { type: 'number', description: "clips 모드에서 각 클립의 목표 길이입니다." },
targetAspect: { type: '"16:9" | "9:16" | "1:1" | "4:5"', description: "클립의 화면 비율입니다." },
platform: { type: 'string', description: "클립을 올릴 플랫폼입니다." },
workflowId: { type: 'string', description: "이 실행을 표시할 워크플로입니다." },
}}
/>

```ts

const { jobId } = await client.edit.editPlan({
mode: "clips",
planTier: "standard",
transcript,
sources: [{ id: "ep", url: masterUrl, kind: "video", role: "master-audio" }],
silence,
count: 5,
targetAspect: "9:16",
})
const { data } = await client.jobs.getStatus(jobId) // poll until completed
const clips = unwrapEditPlanOutput(data.output_data) // one EDL per clip
```

완료된 작업의 출력은 <strong>`unwrapEditPlanOutput()`</strong>으로 읽으세요. `tighten` 모드와 `trailer` 모드에서는 `Edl`을, `clips` 모드에서는 `Edl` 배열을, `chapters` 모드에서는 `ChapterSet`을 반환합니다.

셀프 호스팅 설치에서 이 메서드를 쓰려면 [Nodaro Cloud 연결](https://nodaro.ai/docs/self-hosting/cloud-connect)이 필요합니다. 연결이 없으면 `503 nodaro_connection_required`로 실패합니다.

### edit.applyEdl(input)
[EDL 적용](https://nodaro.ai/docs/nodes/video/apply-edl) 노드처럼, 편집 결정 목록을 동영상 파일이나 오디오 파일로 렌더링합니다.

```ts
applyEdl(input: ApplyEdlInput): Promise<{ jobId: string }>
```

<TypeTable
type={{
edl: { type: 'Edl', required: true, description: "렌더링할 편집 결정 목록입니다. 미디어는 각 소스의 url에서 가져옵니다." },
sources: { type: 'string[]', description: "EDL 소스를 대신할 미디어 URL이며, 같은 순서로 적습니다." },
transcript: { type: 'Transcript', description: "편집된 타임라인으로 옮길 스크립트입니다. 결과는 작업의 json 출력으로 반환됩니다." },
output: { type: '"video" | "audio"', default: '"video"', description: "렌더링할 대상입니다." },
quality: { type: '"proxy" | "final"', default: '"final"', description: "proxy는 미리 보기를 렌더링합니다. 최대 720p이고, 오디오 컷에서는 가벼운 모노 사운드이며, 출력 1분당 별도의 더 낮은 요율로 청구되고, 항상 비공개입니다. final은 최고 품질을 렌더링합니다." },
crossfadeMs: { type: 'number', default: '0', description: "자체 트랜지션이 없는 모든 컷에 적용하는 크로스페이드이며, 단위는 밀리초입니다. 0이면 하드 컷입니다." },
clipKey: { type: 'string', description: "이 렌더가 자르는 계획 클립입니다. @nodaro/shared의 edlSpanKey(clip) 값입니다. 작업의 output_data.clipKey로 반환됩니다." },
planBasis: { type: 'string', description: "이 렌더가 자르는 계획 값입니다. @nodaro/shared의 renderReadBasis(value) 값으로, 소문자 16진수 16자리입니다. 보내는 EDL이 그 값 자체일 때만 보내세요. 다른 형식은 400으로 거부됩니다. 작업의 output_data.planBasis로 반환됩니다." },
workflowId: { type: 'string', description: "이 실행을 표시할 워크플로입니다." },
}}
/>

```ts
const { jobId } = await client.edit.applyEdl({ edl, output: "video", quality: "proxy", crossfadeMs: 80 })
```

완료된 작업의 `output_data`에는 컷이 담깁니다. `videoUrl`과 `thumbnailUrl`, 또는 `audioUrl`입니다. 스크립트를 보냈다면 옮긴 스크립트가 `json`에, 그리고 `quality`(`"proxy"` 또는 `"final"`)가 담기며, `clipKey`와 `planBasis`를 보냈다면 그 값도 담깁니다. `proxy` 렌더는 미리 보기이며 공개 갤러리에 나타나지 않습니다. [미리 보기와 최종 렌더링](https://nodaro.ai/docs/nodes/video/apply-edl#preview-and-render-final)과 품질별 [크레딧](https://nodaro.ai/docs/nodes/video/apply-edl#credits)을 참고하세요.

EDL은 크레딧이 예약되기 전에 검사됩니다. 알 수 없는 소스, 동영상 편집에서 화면이 없는 세그먼트, 렌더링 한 번에 **출력 180분** 초과는 `code`가 `invalid_edl`인 `NodaroError`로 실패합니다. 길이는 크로스페이드를 적용한 뒤에 측정합니다. `message`가 문제를 알려 줍니다. 예를 들어 편집이 200분 분량으로 렌더링되므로 최대 180분짜리 부분으로 나눠야 한다고 알려 줍니다.

### edit.remapTranscript(edl, transcript)
스크립트를 편집의 타임라인으로 **요청 없이 로컬에서** 옮깁니다. 잘라 낸 구간 안의 단어는 빼고, 컷에 걸친 단어는 잘라 내고, 모든 타이밍을 편집된 출력에 맞게 옮깁니다. `applyEdl()`에 `transcript`를 전달하면 서버에서 같은 작업을 합니다.

```ts
remapTranscript(edl: Edl, transcript: Transcript): Transcript
```

<TypeTable
type={{
edl: { type: 'Edl', required: true, description: "편집입니다." },
transcript: { type: 'Transcript', required: true, description: "원본 녹음의 스크립트입니다." },
}}
/>

```ts
const editedTranscript = client.edit.remapTranscript(edl, transcript)
// caption the edited video without another transcription
```

렌더링이나 두 번째 텍스트 변환 없이 편집본에 자막을 달 때 사용하세요. 새 타이밍만 필요할 때는 큰 스크립트에서도 이쪽이 더 빠릅니다.

## 자주 묻는 질문

### Nodaro SDK로 팟캐스트의 쉬는 구간을 잘라 내려면 어떻게 하나요?

녹음을 텍스트로 변환하고 client.edit.silenceDetect를 실행한 다음, 스크립트와 무음 구간을 넣어 client.edit.editPlan을 tighten 모드로 실행하세요. 반환된 계획은 client.edit.applyEdl로 렌더링하세요.

### 팟캐스트 에피소드를 호출 한 번으로 다듬을 수 있나요?

네. client.edit.tightenEpisode는 최대 3시간 길이의 동영상 또는 오디오 녹음 URL 하나를 받아 작업을 반환합니다. 실행은 녹음을 텍스트로 변환하고, 다듬은 에피소드를 계획해 렌더링하며, 선택에 따라 자막, 음악, 프레이밍을 더합니다.

### EDL이란 무엇인가요?

편집 결정 목록입니다. 편집에 쓰는 소스와 남길 세그먼트를 순서대로, 트랜지션과 함께 담은 목록입니다. client.edit.editPlan이 EDL을 작성하고, client.edit.applyEdl이 이를 동영상 파일이나 오디오 파일로 렌더링합니다.

### EDL 렌더링 한 번의 길이는 최대 얼마인가요?

렌더링 한 번에 출력은 최대 180분입니다. 이보다 긴 편집은 크레딧이 예약되기 전에 invalid_edl로 거부됩니다. 여러 부분으로 나누세요.

### client.edit.audioSync의 비용은 얼마인가요?

첫 번째 소스를 제외하고 소스 하나당 11크레딧입니다. 녹음 2개는 11크레딧, 4개는 33크레딧, 6개는 55크레딧입니다.
