스튜디오 프로덕션
REST로 스튜디오 프로덕션을 읽고 편집합니다. 원자적 오퍼레이션 적용, 스틸과 클립 생성, 완료된 작업 반영, 내보내기 계획, 영화 공유와 복사를 다룹니다.
Nodaro Cloud에서 사용할 수 있습니다
스튜디오 프로덕션 API는 Nodaro Studio에서 만드는 영화를 읽고 씁니다. 프로덕션은 설정에 순서가 정해진 샷 목록을 담은 Nodaro 워크플로입니다. 각 샷에는 구도를 잡은 스틸, 선택 사항인 애니메이션 클립, 그리고 이를 만든 플랜, 룩, 출연진, 보이스가 있습니다. 스크립트, AI 어시스턴트, 스튜디오 편집기가 모두 이 경로를 통해 같은 프로덕션을 동시에 다룹니다.
스튜디오 프로덕션은 Nodaro Cloud에서만 실행됩니다. 이 기능을 제공하지 않는 배포 환경은 모든 경로에 404로 응답하므로, 목록 경로로 한 번만 기능을 확인하세요. 셀프 호스팅 설치에서는 장면, 동영상 생성, 동영상 합치기 같은 노드로 영화를 워크플로로 만드세요. 경로는 Bearer 토큰을 받습니다. OAuth 앱 토큰은 읽기에 workflows:read, 쓰기에 workflows:write가 필요합니다. 인증을 참고하세요.
접근할 수 없는 프로덕션에는 모든 경로가 403이 아니라 404로 응답하므로, ID로 프로덕션이 있는지 알아낼 수 없습니다.
편집기의 용어와 문서의 키
편집기와 문서는 같은 것을 서로 다르게 부릅니다. 코드에서는 문서의 키를, 사람과 이야기할 때는 편집기의 용어를 사용하세요.
| 편집기에서 | 문서에서 |
|---|---|
| 영화 | 프로덕션 |
| 장면(예: 장면 3) | shotId로 지정하는 shots[] 항목 |
| 장면의 프레임과 그 테이크 | 샷의 still 결과 |
| 장면의 모션과 그 테이크 | 샷의 clip 결과 |
| 모션 안의 샷 | 샷의 beats[] |
엔드포인트
| 메서드 | 경로 | 기능 |
|---|---|---|
GET | /v1/studio/productions/skill | 작성 가이드, 카탈로그, 플랜의 JSON Schema, 오퍼레이션 어휘입니다. 무료입니다. |
GET | /v1/studio/productions/capabilities | 이 배포 환경이 지원하는 선택적 오퍼레이션입니다. |
POST | /v1/studio/productions/validate | 플랜을 검증합니다. 무료이며 아무것도 저장하지 않습니다. |
GET | /v1/studio/productions | 내 프로덕션을 최신순으로 나열합니다. |
POST | /v1/studio/productions | 프로덕션을 만듭니다. 플랜으로 만들 수도 있습니다. |
GET | /v1/studio/productions/:id | 프로덕션을 읽습니다. 아무것도 쓰지 않습니다. |
POST | /v1/studio/productions/:id/ops | 오퍼레이션을 일괄 적용하거나 미리 봅니다. |
POST | /v1/studio/productions/:id/reconcile | 완료된 작업을 결과로 반영합니다. |
POST | /v1/studio/productions/:id/import | 플랜의 샷을 프로덕션에 추가합니다. |
POST | /v1/studio/productions/:id/describe | 개요로 플랜을 작성합니다. |
POST | /v1/studio/productions/:id/generate | 샷의 스틸이나 클립을 생성하거나 견적을 냅니다. |
POST | /v1/studio/productions/:id/frame | 클립에서 프레임을 가져옵니다. |
POST | /v1/studio/productions/:id/voice | 샷 위에 대사를 입힙니다. |
POST | /v1/studio/productions/:id/revoice | 샷 클립의 보이스를 바꿉니다. |
POST | /v1/studio/productions/:id/music | 사운드트랙을 생성합니다. |
GET | /v1/studio/productions/:id/export-plan | 영화를 조립하는 단계와 각 가격입니다. |
POST | /v1/studio/productions/:id/share | 링크 공유를 켜거나 끕니다. |
POST | /v1/studio/productions/:id/unshare | 링크 공유를 끕니다. |
POST | /v1/studio/productions/:id/clone | 프로덕션을 복사합니다. |
삭제 경로는 없습니다. 보관은 오퍼레이션이며, 되돌릴 수 있습니다.
프로덕션 읽기
모든 응답은 { "data": { "production": { … } } }로 감싸져 있습니다. SDK는 이를 풀어 주므로, 프로덕션을 반환하는 메서드는 프로덕션 자체를 돌려줍니다. 프로덕션의 최상위에는 다음이 있습니다.
| 필드 | 내용 |
|---|---|
id, name, version, updatedAt | 식별 정보와, 모든 쓰기가 확인하는 카운터입니다. |
thumbnailUrl, shared, archived | 포스터 프레임, 링크 공유가 켜져 있는지, 보관되었는지입니다. |
film, cast, folders, storyboard, music, musicPlan, cuts | 영화 룩, 역할, 타임라인 폴더, 개요, 음악, 내보낸 컷입니다. |
trash | { count, items? }: 제거되었지만 복원할 수 있는 항목입니다. |
pending | { stills, clips, music, draft }: 지금 생성 중인 항목입니다. |
shots[] | 타임라인 순서의 샷입니다. 각 샷에는 still, clip, startFrame, endFrame, plan, beats, look, voice 등이 있습니다. |
GET /v1/studio/productions/:id는 detail=summary(기본값: 개수와 활성 URL) 또는 detail=full(각 샷 기록의 모든 결과와 그 결과를 만든 맥락)을 받습니다. shot_id는 샷 하나를 반환하며, 생성 후 다시 읽을 때 가장 가벼운 방법입니다. GET /v1/studio/productions는 limit, cursor, includeArchived를 받습니다.
결과는 쌓입니다. 생성은 절대 덮어쓰지 않습니다. 샷의 스틸은 지금까지 나온 모든 후보이며, 클립도 마찬가지입니다. 스틸을 삭제해도 클립에는 영향이 없습니다. 결과는 위치가 아니라 결과 키로 지정합니다. 결과 키는 작업 ID가 있으면 작업 ID이고, 없으면 URL입니다.
curl "https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b?detail=summary" \
-H "Authorization: Bearer $NODARO_API_KEY"import { createClient, StaticTokenAuth } from '@nodaro/sdk'
const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})
const production = await client.studio.productions.get(id, { detail: 'full' })
const shot = await client.studio.productions.get(id, { shotId: 'shot-2' })SDK는 래퍼(version, rebased, receipts, warnings, 견적의 credits, 실행의 jobIds)에 타입을 지정하고, 프로덕션 문서는 열린 JSON으로 둡니다.
오퍼레이션으로 편집
프로덕션의 모든 변경은 오퍼레이션입니다. 샷 이름 바꾸기, 샷 이동, 출연진 역할 연결, 후보 유지처럼 이름이 붙은 편집입니다. POST /v1/studio/productions/:id/ops는 오퍼레이션을 일괄로 검증하고 적용합니다. 어휘는 여기에 적지 않고 서버가 제공합니다. GET …/skill의 operating 부분을 읽으세요. 이 부분은 항상 지금 통신하는 배포 환경과 일치합니다.
# batch.json: { "ops": [ ...operations from GET /v1/studio/productions/skill... ], "baseVersion": 7 }
curl -X POST https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b/ops \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d @batch.jsonconst { production, version, rebased, receipts } =
await client.studio.productions.ops(id, { ops, baseVersion: 7 })응답은 { production, version, rebased, receipts, warnings }입니다. 모든 일괄 처리에는 다음 여섯 가지 규칙이 적용됩니다.
- 전부 아니면 전무. 잘못된 오퍼레이션이 하나라도 있으면 일괄 처리 전체가 거부되고 아무것도 쓰이지 않습니다. 오류는 0부터 시작하는
opIndex로 해당 오퍼레이션을 알려 줍니다. 일괄 처리 한 번에 오퍼레이션을 최대 100개까지 담을 수 있습니다. - 기본은 리베이스.
baseVersion은 참고용입니다. 이전 버전을 기준으로 작성한 일괄 처리는 최신 문서에 적용되고, 응답에rebased: true가 표시됩니다. 대신409 workflow_conflict를 받으려면strict: true를 보내세요. - 고정 키로 지정. 샷은 ID로, 폴더나 컷은 ID로, 출연진 행은 역할 슬러그로, 결과는 결과 키로 지정합니다. 편집기와 스크립트가 같은 프로덕션을 동시에 편집할 수 있으므로 절대 위치로 지정하지 마세요.
- ID는 직접 발급. 샷, 폴더, 컷, 사본을 만드는 오퍼레이션은 내가 준 ID를 사용합니다. 그러면 로컬 사본과 서버의 모든 ID가 일치합니다.
- 삭제는 휴지통으로. 제거하면 제거한 항목이 프로덕션의 휴지통으로 옮겨지고, 오퍼레이션으로 복원할 수 있습니다. 명시적으로 완전히 삭제할 때만 무언가가 사라집니다.
- 응답을 그대로 채택. 병합하지 말고 내 사본을
production으로 교체한 뒤,version을 다음baseVersion으로 보내세요.
공유는 오퍼레이션이 아니므로, 공유를 바꾸려는 일괄 처리는 거부됩니다. receipts에는 오퍼레이션마다 과거형 설명이 한 줄씩 { op, summary, ids?, impact? }로 들어 있습니다. 어시스턴트가 무엇을 했는지 알고 싶은 사람에게 보여 주세요. warnings는 실패는 아니지만 알릴 만한 내용을 나열합니다. 예를 들어 아무것도 바꾸지 않은 오퍼레이션이 있습니다.
적용 전에 일괄 처리 미리 보기
dryRun: true는 일괄 처리가 무엇을 할지 묻습니다. 서버는 같은 맥락과 같은 거부 조건으로 쓰기를 직접 실행하고, 저장하기 직전에 멈춥니다. 응답은 production 없이 { dryRun: true, version, receipts, warnings }입니다. 각 영수증에는 class(S 안전, D 삭제, P 작업물에 접근할 수 있는 사람 변경, $ 지출)가 추가되고, 삭제를 되돌릴 수 있는 경우 restorable: true가 추가됩니다.
미리 보기가 생기기 전의 배포 환경은 dryRun을 무시하고 일괄 처리를 적용합니다. 그러니 어떤 배포 환경에서도 아무것도 바꾸지 않는 빈 일괄 처리로 먼저 플래그를 확인하세요.
{ "ops": [], "dryRun": true }dryRun: true가 담긴 응답만 지원한다는 뜻입니다. 그 밖의 응답은 지원하지 않는다는 뜻이므로, 여기서는 미리 볼 수 없다고 상대에게 알리고 일괄 처리를 보내지 마세요. 업데이트 중인 배포 환경은 다음 요청을 다른 서버에서 처리할 수 있으므로, 실제 미리 보기 응답에서도 이 표시를 확인하세요. 표시 대신 production이 담긴 응답은 일괄 처리가 적용되었다는 뜻입니다. 그 응답을 채택하고 다시 보내지 마세요. SDK는 두 가지 확인을 모두 대신하고 StudioPreviewUnavailable 또는 StudioPreviewAppliedError를 발생시킵니다.
const preview = await client.studio.productions.ops(id, { ops, baseVersion, dryRun: true })
for (const r of preview.receipts) console.log(r.class, r.summary, r.restorable ?? false)스틸과 클립 생성
생성은 실행한 뒤 폴링하는 방식입니다. kind: "still" 또는 kind: "clip"과 shotId로 POST /v1/studio/productions/:id/generate를 호출하면 작업을 제출하고, 프로덕션에 대기 중으로 기록한 뒤, 바로 { jobIds, lane?, deduped?, production? }를 반환합니다.
- 샷을 바탕으로 구성. 서버는 샷의 플랜, 룩, 출연진, 연출로 요청을 구성합니다. 그래서 스크립트에서 호출하든 편집기에서 클릭하든 같은 이미지가 나옵니다.
overrides는 샷을 바꾸지 않고 실행 한 번만 바꿉니다. - 먼저 견적.
dryRun: true는 실행 가격을 계산하고 아무것도 쓰지 않습니다. 응답은{ dryRun: true, provider, count, credits, lane? }입니다.credits: null은 모델에 가격이 없다는 뜻이며, 무료가 아니라 알 수 없다는 뜻입니다. - 안전하게 재시도. 직접 만든
A-Za-z0-9_.:-문자 8~128자의clientRequestId를 쓰면 재시도가 안전해집니다. 같은 ID는 첫 호출의 작업을deduped: true와 함께 반환하고 요금을 부과하지 않습니다. 이 값 없이 유료 호출을 재시도하지 마세요. - 결과 반영.
POST /v1/studio/productions/:id/reconcile은 완료된 작업을 결과로 반영하고 실패한 작업을 정리합니다.{ landed, pending, failed, warnings, production, version }을 반환합니다.GET은 아무것도 쓰지 않으므로, 무언가를 기다리고 있을 때는 reconcile하세요. - 클립 레인은 자동으로 정해집니다. 클립의 모델 경로(
generate-video또는text-to-video)는 샷의 입력에 따라 정해지고lane으로 반환됩니다.mode는 어떤 입력 세트를 기준으로 연출할지만 정합니다."start"또는"references"입니다.
curl -X POST https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b/generate \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "kind": "still", "shotId": "shot-2", "count": 2, "clientRequestId": "still-shot-2-take-1" }'
curl -X POST https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b/reconcile \
-H "Authorization: Bearer $NODARO_API_KEY"import { isStudioGenerateEstimate } from '@nodaro/sdk'
const quote = await client.studio.productions.generateStill(id, 'shot-2', { count: 2, dryRun: true })
if (isStudioGenerateEstimate(quote)) console.log(quote.credits)
const run = await client.studio.productions.generateStill(id, 'shot-2', {
count: 2,
clientRequestId: crypto.randomUUID(),
})
// ...once the jobs finish:
const { landed, pending } = await client.studio.productions.reconcile(id)다른 미디어 경로는 샷 하나를 대상으로 합니다.
| 경로 | 본문 | 기능 |
|---|---|---|
frame | { shotId, mode?, timestamp?, target?, clientRequestId? } | 클립에서 프레임을 가져옵니다. 몇 초 기다린 뒤 { production, url? }을 반환합니다. |
voice | { shotId, text, voiceId?, voiceType?, ttsProvider?, delivery?, clientRequestId? } | 샷 위에 대사를 입힙니다. 기다린 뒤 { production }을 반환합니다. |
revoice | { shotId, plan, clientRequestId? } | 샷 클립의 보이스를 바꿉니다. { jobId, production }을 반환하며, reconcile로 반영됩니다. |
music | { prompt, duration?, instrumental?, vocalGender?, model?, clientRequestId? } | 사운드트랙을 생성합니다. { jobId, production }을 반환하며, reconcile로 반영됩니다. |
clientRequestId는 frame과 voice를 포함해 여기 있는 모든 유료 경로에서 받습니다.
연결된 클립과 리테이크
일부 배포 환경은 검토한 두 프레임 사이를 잇는 클립을 지원합니다. 먼저 GET /v1/studio/productions/capabilities를 확인하세요. operations.generateLinkedClips와 operations.retakeLinkedClips가 사용 가능 여부를 알려 줍니다.
- 연결된 클립 견적.
kind: "clip",shotId,dryRun: true로generate를 호출하면 두 프레임을 확인하고inputHash가 담긴 견적을 반환합니다. 실제 요청에서 이 값을expectedInputHash로 다시 보내세요. 그사이 설정이나 프레임이 바뀌었다면 경로는 아무것도 제출하기 전에409 sequence_quote_changed로 응답합니다. - 클립 리테이크.
retakeResultKey를 추가하면 기존 연결된 클립의 원래 설정과 프레임으로 새 테이크의 견적을 냅니다. 그다음 견적의inputHash와 새clientRequestId로 제출하세요.mode,overrides,count는 보내지 마세요. 원래 요청이나 프레임이 보관되지 않은 테이크는 리테이크할 수 없습니다.
개요에서 프로덕션까지
가이드 읽기
GET /v1/studio/productions/skill은 { skill, catalog, schema, operating, generatedFrom }을 반환합니다. 플랜을 작성하는 방법, 모든 피커와 모델과 허용 값, 플랜의 JSON Schema, 오퍼레이션 어휘입니다.
플랜 검증
{ plan }으로 POST /v1/studio/productions/validate를 호출하면 { valid, errors, warnings, summary? }를 반환합니다. valid가 true가 될 때까지 각 오류의 path를 고치세요. 출연진 이름은 내 라이브러리를 기준으로 확인합니다.
프로덕션 만들기
{ name?, plan? }으로 POST /v1/studio/productions를 호출하면 프로덕션을 만들고 플랜을 반영합니다. { plan, mode: "append" }로 POST …/:id/import를 호출하면 기존 프로덕션에 플랜의 샷을 추가합니다. 둘 다 미디어를 생성하지 않습니다.
또는 디렉터에게 플랜 맡기기
{ brief, llmModel, mode?, label?, clientRequestId? }로 POST …/:id/describe를 호출하면 디렉터 실행을 시작하고 { jobId, production }을 반환합니다. 작업이 끝나면 reconcile해서 초안으로 작성된 샷을 반영하세요.
const { production } = await client.studio.productions.create({ name: 'The Lighthouse' })
const { jobId } = await client.studio.productions.describe(production.id, {
brief: 'A keeper, a storm, and a light that will not start.',
llmModel: 'gpt-5-mini',
mode: 'replace',
})
// poll jobId with client.jobs.getStatus, then:
await client.studio.productions.reconcile(production.id)내보내기 계획
GET /v1/studio/productions/:id/export-plan은 영화를 조립하는 단계를 순서대로 반환합니다. 샷별 오디오 병합, 이어 붙이기, 그리고 선택 사항인 upscale을 사용한 4K 마무리입니다. 아무것도 실행하지 않습니다.
{
"canExport": true,
"steps": [
{
"id": "voice-shot-2",
"node": "merge-video-audio",
"label": "Voice over shot 2",
"credits": 20,
"params": { "videoUrl": "https://cdn.nodaro.ai/studio/shot-2.mp4", "audioUrl": "https://cdn.nodaro.ai/studio/vo.mp3" }
},
{
"id": "combine",
"node": "combine-videos",
"label": "Join 2 shots",
"credits": 4,
"params": {
"videoUrls": [{ "fromStep": "voice-shot-2" }, "https://cdn.nodaro.ai/studio/shot-3.mp4"],
"transition": "cut",
"audioMode": "keep"
}
}
],
"resultStepId": "combine",
"estimate": 24,
"unpriced": []
}동영상 및 오디오 병합, 동영상 합치기 같은 일반 노드 경로로 단계를 순서대로 실행하세요. { "fromStep": "…" } 값은 앞 단계의 출력이므로, 그 단계가 만든 URL로 바꿔 넣으세요. 그런 다음 컷을 추가하는 오퍼레이션으로 완성된 파일을 프로덕션에 컷으로 기록하세요.
조립할 것이 없을 때, 즉 클립이 두 개 미만일 때 canExport는 false입니다. 가격이 없는 단계가 하나라도 있으면 estimate는 null이고, unpriced가 해당 모델을 알려 줍니다. 일부만 더한 합계는 비용을 실제보다 적게 보여 주기 때문입니다.
프로덕션 공유와 복사
- 공유.
{ shared: true }로POST …/:id/share를 호출하면 읽기 전용 공유 링크가 켜지고,{ shared: false }나POST …/:id/unshare로 끕니다. 공유는 소유자나 워크스페이스 관리자만 바꿀 수 있습니다(그 외에는403 forbidden).operations.revisionedSharing을 사용할 수 있으면expectedVersion을 추가해 검토한 버전에 변경을 묶으세요. 그 뒤에 새 편집이 있으면409 workflow_conflict로 응답합니다. - 편집 가능한 사본 허용.
operations.editableSharedCopies가 있으면 소유자는shared: true,expectedVersion과 함께allowEditableCopy: true도 보낼 수 있습니다. 그러면 로그인한 링크 열람자가 플랜, 프롬프트, 출연진 설명, 유지된 입력, 테이크 기록을 복사할 수 있습니다. 휴지통과 비공개 검토 메모는 복사되지 않으며, 공유를 끄면 새 사본을 만들 수 없습니다. - 복사.
{ name? }으로POST …/:id/clone을 호출하면 내가 볼 수 있는 범위를 기준으로 프로덕션을 복사합니다. 사본은 비공개이고 보관되지 않은 상태로 시작하며, 내 저장 공간을 사용합니다.
타임라인을 편집 앱으로 내보내기
POST /v1/freecut-export는 장면 클립으로 된 타임라인을 편집 프로젝트 파일로 만들어, 외부 편집 앱에서 컷을 마무리할 수 있게 합니다. FreeCut JSON(freecut-v1) 또는 Final Cut Pro XML(fcpxml-v1.10) 파일을 내 저장 공간에 쓰고 그 URL을 반환합니다. 크레딧이 들지 않으며 분당 10개의 요청을 허용합니다. 이 페이지의 다른 기능처럼 Nodaro Cloud가 필요하며, 다른 에디션은 404로 응답합니다.
curl -X POST https://app.nodaro.ai/v1/freecut-export \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"format": "json",
"name": "The Lighthouse, cut 1",
"timeline": {
"musicAssetUrl": "https://cdn.nodaro.ai/studio/score.mp3",
"scenes": [
{
"sceneEntityId": "scene-1",
"compositeUrl": "https://cdn.nodaro.ai/studio/scene-1.mp4",
"shots": [{ "shot_id": "s1", "duration_seconds": 4 }]
},
{
"sceneEntityId": "scene-2",
"compositeUrl": "https://cdn.nodaro.ai/studio/scene-2.mp4",
"shots": [{
"shot_id": "s2",
"duration_seconds": 6,
"cut_decision": { "in_offset_sec": 0, "out_offset_sec": 0.5, "transition_to_next": "dissolve" }
}]
}
]
}
}'const file = await client.request('POST', '/v1/freecut-export', {
body: { format: 'json', name: 'The Lighthouse, cut 1', timeline },
}){
"url": "https://cdn.nodaro.ai/exports/7b2d4f6a/freecut-3c5e7a9b-1d2f-4a6c-8e3b-5f7a9c1e2d4b.json",
"format": "json",
"assetId": "1e3a5c7b-9d2f-4b4a-8c6e-5f7d9b1a3c2e"
}assetId는 내 라이브러리에 있는 파일의 항목이며, 항목을 만들 수 없었다면 null입니다. 어느 경우든 url은 유효합니다.
| 필드 | 내용 |
|---|---|
format | 필수. FreeCut JSON이면 json, Final Cut Pro XML이면 fcpxml입니다. |
timeline | 필수. 아래의 타임라인입니다. |
name | 기록용 라벨로, 최대 200자입니다. |
timeline.scenes | 필수. 재생 순서대로 된 장면 하나 이상입니다. 각 장면은 동영상 트랙의 클립 하나가 됩니다. |
timeline.musicAssetUrl | 음악 트랙입니다. 기본값인 빈 문자열이면 음악을 넣지 않습니다. |
timeline.narrationAssetUrl | 내레이션 트랙으로, 별도의 오디오 레인에 배치됩니다. |
timeline.fadeOutDurationSec | 음악 끝의 페이드로, 기본값은 0.8초입니다. FreeCut JSON에서만 적용됩니다. |
scene.sceneEntityId, scene.compositeUrl | 필수. 장면의 ID와 병합된 클립입니다. |
scene.shots | 필수. 샷 하나 이상으로, { shot_id, duration_seconds, cut_decision? } 형태입니다. 샷 길이의 합이 장면의 길이가 됩니다. |
cut_decision.in_offset_sec, cut_decision.out_offset_sec | 컷 결정에서 필수. 시작 부분(장면의 첫 샷에서 읽음)과 끝부분(마지막 샷에서 읽음)을 잘라 내는 길이입니다. |
cut_decision.transition_to_next | 컷 결정에서 필수. hard_cut, dissolve, match_cut, overlap 중 하나로, 다음 장면으로 넘어가는 전환입니다. |
cut_decision.transition_duration_sec | 전환의 기본 길이를 덮어씁니다. 기본값은 hard_cut과 match_cut이 0, dissolve가 0.5초, overlap이 1초입니다. |
dissolve와 overlap은 두 클립을 전환 길이만큼 겹치고, hard_cut과 match_cut은 두 클립을 바로 이어 붙입니다. cut_decision이 있는 샷이 하나도 없으면 내보내기는 단순 연결이 됩니다. 장면마다 클립 하나를 끝과 끝으로 하드 컷으로 잇고, 음악은 타임라인 전체에 깔립니다. 각 장면 클립은 이미 병합되어 있으므로 장면 안의 자르기는 적용되지 않습니다.
공유 및 리믹스 레코드
샷 레코드는 내가 만든 샷의 상태를 저장합니다. 피커 선택, 프롬프트, 대상 모델, @로 멘션한 에셋, 결과가 들어갑니다. 레코드는 짧고 추측할 수 없는 ID 뒤에 있으며, 이 ID로 /s/:id 공유 링크와 원클릭 리믹스가 동작합니다. 이 경로는 프로덕션과 별개입니다.
| 메서드 | 경로 | 인증 | 기능 |
|---|---|---|---|
POST | /v1/shots | Bearer 토큰 | 레코드를 만듭니다. { id }를 반환합니다. |
GET | /v1/shots/:id | 없음 | 레코드를 읽습니다. 비공개 레코드는 소유자를 제외한 모두에게 404로 응답합니다. IP 주소별로 제한됩니다. |
PATCH | /v1/shots/:id | 소유자 | visibility를 포함해 어떤 필드든 업데이트합니다. |
DELETE | /v1/shots/:id | 소유자 | 레코드를 삭제합니다. |
본문에는 mode(single, multi-shot, frame-to-motion, storyboard), selectionState(각 피커가 고른 값으로, { pickerNodeType: valueId } 또는 { pickerNodeType: { field: valueId } } 형태), 그리고 선택적으로 freeText, negativePrompt, assembledPrompt, perModelPrompts, models, entityRefs, resultUrls가 들어갑니다. visibility는 기본값이 private이며, 레코드를 공유할 수 있게 하려면 public으로 설정하세요.
resultUrls는 일반 공개 http 또는 https URL만 받습니다. 서명된 URL은 거부되므로 공유 레코드에 토큰이 새어 들어가지 않습니다. 레코드에는 schemaVersion이 있습니다. 레코드가 더 이상 없는 카탈로그 항목을 가리키면 리믹스를 실패시키지 말고 메모를 남긴 채 그 항목을 건너뛰세요.
curl -X POST https://app.nodaro.ai/v1/shots \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "single",
"selectionState": { "mood": "melancholy", "lighting": { "timeOfDay": "blue-hour" } },
"freeText": "a woman at a bus stop",
"models": ["nano-banana-pro"],
"visibility": "public"
}'const { id: shotId } = await client.shots.create({
mode: 'single',
selectionState: { mood: 'melancholy', lighting: { timeOfDay: 'blue-hour' } },
freeText: 'a woman at a bus stop',
})
await client.shots.update(shotId, { visibility: 'public' })
const { shot } = await client.shots.get(shotId)nodaro shots create --file shot.json --visibility public
nodaro shots get <id> --json
nodaro shots delete <id>MCP에서 사용
AI 어시스턴트는 get_studio_production_skill, validate_studio_plan, create_studio_production, edit_studio_production, generate_studio_still, generate_studio_clip, plan_studio_export 같은 도구로 같은 프로덕션을 다룹니다. 쓰기 권한으로 프로덕션을 읽으면 완료된 작업도 반영됩니다. MCP로 스튜디오 프로덕션 사용을 참고하세요.
오류
| 상태 | 코드 | 의미 |
|---|---|---|
400 | validation_error | 본문이나 플랜이 잘못되었습니다. path가 해당 필드를 알려 주며, 내보내기 경로에서는 issues가 문제를 나열합니다. |
400 | op_invalid, op_target_missing | …/ops에서: 오퍼레이션 하나가 잘못되었으며, opIndex가 그 오퍼레이션을 알려 줍니다. 아무것도 쓰이지 않았습니다. 고친 뒤 일괄 처리를 다시 보내세요. |
402 | insufficient_credits | 계정의 크레딧이 생성 비용에 못 미칩니다. |
403 | forbidden | 공유는 소유자나 워크스페이스 관리자만 바꿀 수 있습니다. |
404 | not_found | 이 호출자에게 해당 프로덕션이 없거나, 배포 환경이 프로덕션을 제공하지 않습니다. |
404 | op_target_missing | 생성이나 미디어 경로에서: 지정한 샷, 결과, 역할이 프로덕션에 없습니다. |
409 | workflow_conflict | strict: true나 expectedVersion을 보냈는데 프로덕션이 바뀌었습니다. 다시 읽고 다시 적용하세요. |
409 | production_busy | 쓰는 동안 프로덕션이 계속 바뀌었습니다. 다시 읽고 재시도하세요. |
409 | sequence_quote_changed | 견적을 낸 뒤 연결된 클립의 설정이나 프레임이 바뀌었습니다. 견적을 다시 내세요. |
413 | storage_exceeded | 계정이 저장 공간 한도를 넘었습니다. |
429 | rate_limit_exceeded | 1분에 타임라인 내보내기를 10번 넘게 요청했습니다. Retry-After에 적힌 시간만큼 기다리세요. |
SDK에서는 이 오류가 타입이 있는 오류로 전달됩니다. StudioOpError(opIndex 포함), 쓰기의 두 409 코드에 대한 WorkflowConflictError, InsufficientCreditsError, StorageExceededError, NotFoundError입니다.
자주 묻는 질문
스튜디오 프로덕션이란 무엇인가요?
Nodaro Studio에서 만드는 영화입니다. 설정에 순서가 정해진 샷 목록을 담은 워크플로이며, 편집기에서는 샷을 장면이라고 부릅니다. 각 샷에는 구도를 잡은 스틸, 선택 사항인 애니메이션 클립, 그리고 이를 만든 플랜, 룩, 출연진, 보이스가 있습니다.
생성한 뒤 reconcile을 호출해야 하는 이유는 무엇인가요?
생성은 바로 응답하고 백그라운드에서 실행됩니다. POST /v1/studio/productions/:id/reconcile은 완료된 작업을 각 샷의 결과로 반영합니다. 일반 GET은 아무것도 쓰지 않으므로, reconcile하기 전까지 완료된 작업은 대기 중으로 남습니다.
유료 생성을 안전하게 재시도하려면 어떻게 하나요?
직접 만든 8~128자의 clientRequestId를 보내세요. 같은 ID를 다시 보내면 첫 호출이 시작한 작업이 deduped 표시와 함께 반환되며, 요금이 부과되지 않습니다.
오퍼레이션 일괄 처리가 무엇을 할지 미리 볼 수 있나요?
네, dryRun을 true로 보내면 됩니다. 먼저 빈 일괄 처리에 dryRun을 붙여 보내 서버가 미리 보기를 지원하는지 확인하세요. 미리 보기를 지원하지 않는 서버는 일괄 처리를 그대로 적용하기 때문입니다.
스튜디오 타임라인을 Final Cut Pro나 다른 편집 앱으로 내보낼 수 있나요?
네. POST /v1/freecut-export는 장면 클립으로 된 타임라인을 내 저장 공간에 FreeCut JSON 또는 Final Cut Pro XML 파일로 만듭니다. 크레딧이 들지 않습니다.
관련 문서
스튜디오 프로덕션
파이프라인
작업
보이스 및 미디어
워크플로
최종 업데이트