Recast
TypeScript로 Recast 실행의 견적을 받고, 구매하고, 추적합니다. 선택에 답하고, 직접 쓴 스크립트를 가져오고, 완성된 Recast의 음악 믹스를 바꿉니다.
Nodaro Cloud에서 사용할 수 있습니다
client.recast는 Recast를 실행합니다. 분석된 원본 동영상을 내 출연진으로 다시 생성하며, 내가 쓴 스크립트, 즉 “JSON으로 쓴 영화”를 렌더링할 수도 있습니다. 실행의 견적을 받고, 구매하고, 추적하며, 출연진, 시트, 스틸, 음악을 고르는 선택에 답합니다. 이 메서드들은 Recast REST API를 호출합니다. 작성 가이드는 MCP에서 Recast 사용을 참조하세요.
Recast는 Nodaro Cloud에서 실행됩니다. 셀프 호스팅 설치에서는 이 메서드들이 NotFoundError를 발생시킵니다.
메서드
| 메서드 | 하는 일 |
|---|---|
authoringSkill() | 스크립트 작성 가이드를 읽습니다 |
validateScript(script) | 직접 쓴 스크립트를 검사합니다 |
importScript(script, opts) | 직접 쓴 스크립트를 분석으로 저장합니다 |
estimate(input) | 실행의 견적을 무료로 받습니다 |
create(input) | 계획을 구매하고 실행을 만듭니다 |
get(recastId) | 실행의 상태와 대기 중인 선택을 읽습니다 |
start(recastId, opts?) | 계획된 실행의 렌더링을 시작합니다 |
resolveGate(recastId, input) | 대기 중인 선택에 답합니다 |
estimateRescore(recastId, input) | 새 음악 믹스나 음악 트랙의 견적을 받습니다 |
rescore(recastId, input) | 견적을 받은 오디오 변경을 적용합니다 |
직접 쓴 스크립트
authoringSkill()
작성 가이드를 Markdown으로 반환합니다(GET /v1/video-analysis/authoring-skill). 스크립트 문서, 그 어휘와 제한, 오디오 규칙, 검증된 예시를 다룹니다. 무료입니다.
authoringSkill(): Promise<string>const guide = await client.recast.authoringSkill()이 가이드를 내 스크립트를 쓰는 언어 모델에 전달하세요.
validateScript(script)
직접 쓴 스크립트를 검사하고 { valid, errors, warnings }를 반환합니다(POST /v1/video-analysis/import/validate). 무료이며, 아무것도 저장하지 않습니다.
validateScript(script: Record<string, unknown>): Promise<{
valid: boolean
errors: Array<{ path: string; message: string; hint?: string }>
warnings: string[]
}>속성
타입
let check = await client.recast.validateScript(script)
while (!check.valid) {
script = await fixWithModel(script, check.errors) // each error has a path, a message and usually a hint
check = await client.recast.validateScript(script)
}각 오류는 잘못된 path를 알려 주며, 대개 언어 모델을 위해 쓴 hint가 함께 있습니다. valid가 true가 될 때까지 고치고 다시 검증하세요.
importScript(script, opts)
유효한 스크립트를 완료된 분석 작업으로 저장합니다(POST /v1/video-analysis/import). 반환된 jobId를 Recast의 analysisJobId로 사용하세요. 무료입니다.
importScript(script: Record<string, unknown>, opts: { rightsAttested: true }): Promise<{
jobId: string
created: boolean
warnings: string[]
json: Record<string, unknown>
}>속성
타입
const { jobId: analysisJobId, json } = await client.recast.importScript(script, { rightsAttested: true })직접 쓴 스크립트로 만든 Recast는 쓴 그대로 렌더링되므로, 내가 소유한 작품만 가져오세요. json은 서버가 추가한 필드가 들어간 내 문서입니다. 내가 보낸 입력보다 이 값을 우선해서 사용하세요. 같은 스크립트를 다시 가져오면 created: false가 반환됩니다.
실행
estimate(input)
실행의 견적을 크레딧으로 받습니다(POST /v1/recast/estimate). 무료입니다. 본문은 create()와 같지만, workflowId, rightsAttested, clientCapabilities는 포함하지 않습니다.
estimate(input: EstimateRecastInput): Promise<{ totalCredits?: number; breakdown?: Record<string, number> }>속성
타입
const quote = await client.recast.estimate({ analysisJobId, fidelity: "faithful" })
console.log(quote.totalCredits, quote.breakdown)SDK 2.17.0의 segmentSec. Recast 라우트는 segmentSec에 팩 이름을 받습니다. "scenes-max", "scenes", "max" 중 하나입니다. SDK 2.17.0은 이 필드의 타입을 숫자로 지정하므로, segmentSec: "scenes" as unknown as number처럼 캐스트해서 이름을 전달하거나, 필드를 생략해 서버의 기본값을 사용하세요. 10 같은 숫자는 400으로 거부됩니다. create()와 start()도 마찬가지입니다.
create(input)
실행을 만들고 계획을 구매합니다(POST /v1/recast). 먼저 estimate()로 견적을 받으세요.
create(input: CreateRecastInput): Promise<{ recastId: string }>속성
타입
const { recastId } = await client.recast.create({
workflowId,
analysisJobId,
fidelity: "faithful",
rightsAttested: true,
interactive: true,
clientCapabilities: ["sheet-gate"],
})workflowId가 없으면 호출이 400 workflow_id_required로 실패하고, 알 수 없는 워크플로나 다른 사람의 워크플로는 404로 실패합니다.
get(recastId)
실행을 읽습니다(GET /v1/recast/:id). 진행 상황을 추적하려면 이 메서드를 폴링하세요. 대화형 실행에서는 interactive.next가 대기 중인 단계나 선택을 알려 줍니다.
get(recastId: string): Promise<RecastRunSnapshot>속성
타입
const run = await client.recast.get(recastId)
console.log(run.status, run.interactive)스냅샷에는 status, interactive, capabilities가 있으며, 오디오 레인이 분리된 완료된 실행에는 audio도 있습니다. 음악 믹스 변경을 참조하세요.
start(recastId, opts?)
planned 상태인 실행의 렌더링을 시작합니다(POST /v1/recast/:id/start). 계획의 견적에 이미 포함되어 있으며, 여러 번 호출해도 아무것도 바뀌지 않습니다.
start(recastId: string, opts?: { segmentSec?: number; provider?: string }): Promise<{ gvpJobId?: string }>속성
타입
const { gvpJobId } = await client.recast.start(recastId)resolveGate(recastId, input)
대화형 실행에서 대기 중인 선택에 답합니다(POST /v1/recast/:id/select). 그 밖의 모든 단계는 플랫폼이 스스로 진행하므로, 클라이언트는 get()을 폴링하고 선택에 답하기만 하면 됩니다. 선택 자체는 무료입니다.
resolveGate(recastId: string, input: ResolveRecastGateInput): Promise<Record<string, unknown>>속성
타입
await client.recast.resolveGate(recastId, { gate: "music", musicPick: 1 })
await client.recast.resolveGate(recastId, { finishAuto: true })선택은 create()의 clientCapabilities에 선언한 종류에 대해서만 열립니다. 나머지는 플랫폼이 자동으로 결정합니다.
음악 믹스 변경
완성된 Recast는 음악과 동영상 자체의 사운드를 별도의 레인으로 유지할 수 있습니다. get()이 capabilities.audioLayers를 1로 반환하면, audio 매니페스트가 그 레인들을 설명합니다.
revision은 현재 오디오를 식별합니다. 모든 변경에 필요합니다.present는 Recast에 있는 레인인music과video를 나열합니다.layers에는 존재하는 레인의 미리보기 파일이 들어 있습니다.bakedEffectiveGain은 현재 다운로드의 레벨을 설명합니다.pendingRescore는 아직 진행 중인 변경을 설명합니다. 페이지를 새로 고쳐도 유지됩니다.
리비전을 지어내지 말고, 미리보기가 없다는 이유로 믹스를 짐작하지 마세요.
estimateRescore(recastId, input)
오디오 변경의 견적을 받습니다. 새 믹스, 새 음악 트랙, 또는 둘 다입니다(POST /v1/recast/:id/estimate-rescore). 무료이며 { credits, audioRevision, noOp }를 반환합니다.
estimateRescore(recastId: string, input: EstimateRecastRescoreInput): Promise<{ credits: number; audioRevision: string; noOp: boolean }>속성
타입
rescore(recastId, input)
오디오 변경을 적용합니다(POST /v1/recast/:id/rescore). 견적을 받은 것과 같은 변경 내용에 UUID인 새 requestId를 더해 보내세요. 그 requestId는 네트워크 오류 후 같은 요청을 다시 시도할 때만 재사용하세요.
rescore(recastId: string, input: EstimateRecastRescoreInput & { requestId: string }): Promise<
| { recastId: string; jobId: string }
| { recastId: string; noOp: true; audioRevision: string }
>속성
타입
const { total: available } = await client.credits.balance()
const run = await client.recast.get(recastId)
if (run.capabilities?.audioLayers === 1 && run.audio) {
const operation = {
expectedAudioRevision: run.audio.revision,
mix: {
music: { gain: 55, muted: false },
video: { gain: 100, muted: false },
},
}
const quote = await client.recast.estimateRescore(recastId, operation)
if (!quote.noOp && available >= quote.credits) {
await client.recast.rescore(recastId, { ...operation, requestId: crypto.randomUUID() })
}
}변경은 작업으로 실행됩니다. audio.pendingRescore가 사라지고 audio.revision이 바뀔 때까지 get(recastId)를 폴링하세요. 아무것도 바꾸지 않는 변경은 작업 없이 noOp: true를 반환합니다.
- 한 번의 변경 내용에는 믹스, 음악 교체 하나(
audioUrl또는sections), 또는 믹스를 포함한 교체가 들어갑니다. - 교체할 때는 원하는 완전한 믹스를 보내세요. 믹스를 생략해도 되는 것은 예전 기본 레벨과 정확히 같을 때뿐이며, 그 밖에는
409 legacy_mix_mismatch로 실패합니다. - 오래된
expectedAudioRevision은409 stale_audio_revision으로 실패하고, 변경이 진행 중일 때 두 번째 변경을 보내면409 rescore_in_progress로 실패합니다. Recast를 다시 읽은 다음, 견적을 다시 받으세요. - 잘못된 변경 내용은
validation_error,unknown_section,duplicate_section,all_audio_silent같은 400 오류로 실패합니다. - 견적은 크레딧을 예약하지 않으며, 잔액이 부족해도 가격을 반환합니다.
자주 묻는 질문
Recast는 무엇을 하나요?
Recast는 분석된 원본 동영상을 내 출연진으로 다시 생성합니다. Nodaro는 분석을 바탕으로 실행을 계획하고, 세그먼트별로 렌더링하며, 그 과정에서 출연진, 시트, 스틸, 음악을 고를 수 있게 합니다.
Recast를 만들면 크레딧이 드나요?
네. create가 계획을 구매하므로, 먼저 무료인 client.recast.estimate로 견적을 받으세요. resolveGate로 선택에 답하는 것은 무료입니다.
실제 동영상 대신 내가 쓴 스크립트를 Recast할 수 있나요?
네. validateScript로 스크립트가 유효해질 때까지 검증한 다음, rightsAttested를 true로 설정해 importScript를 호출하세요. 가져오기는 jobId를 반환하며, 이 값을 새 Recast의 analysisJobId로 사용합니다.
완성된 Recast의 음악 볼륨을 바꾸려면 어떻게 하나요?
get으로 Recast를 읽고, estimateRescore로 새 믹스의 견적을 받은 다음, 같은 변경 내용을 새 requestId와 함께 rescore로 보내세요. 오디오 리비전이 바뀔 때까지 get을 폴링하세요.
관련 문서
Recast
Recast
작업과 실행
스튜디오 프로덕션
최종 업데이트