スタジオプロダクション
REST で、スタジオプロダクションを読み書きします。アトミックな操作を適用し、静止画とクリップを生成して、完了したジョブを結果に反映し、書き出しを計画して、映画を共有またはコピーします。
Nodaro Cloud で利用できます
スタジオプロダクション API は、Nodaro Studio で作る映画を読み書きします。プロダクションは、設定に順番に並んだショットのリストを持つ、Nodaro のワークフローです。各ショットには、構図を決めた静止画、任意のアニメーションクリップ、そしてそれらを作ったプラン、ルック、キャスト、ボイスがあります。スクリプト、AI アシスタント、スタジオのエディターは、これらのルートを通じて、同時に同じプロダクションを操作します。
スタジオプロダクションは、Nodaro Cloud でのみ動作します。この機能を提供していないデプロイメントでは、すべてのルートが 404 を返すため、一覧のルートで 1 回だけ機能を検出してください。セルフホスティング環境では、シーン(Scene)、動画生成(Generate Video)、動画を結合(Combine Videos)などのノードを使って、映画をワークフローとして構築します。ルートはベアラートークンを必要とします。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 | プロダクションをコピーします。 |
削除のルートはありません。アーカイブは操作の 1 つで、元に戻せます。
プロダクションを読み取る
すべてのレスポンスは、{ "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 を指定すると、1 つのショットだけを返します。生成のあとにもう一度読み取る、軽い方法です。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 } です。すべてのバッチに、次の 6 つの規則が当てはまります。
- すべてか無か。不正な操作が 1 つでもあると、バッチ全体が拒否され、何も書き込まれません。エラーは、0 から数える
opIndexでその操作を示します。バッチに入れられる操作は、最大 100 個です。 - デフォルトはリベース。
baseVersionは情報として送るだけです。古いバージョンをもとに作ったバッチも、最新のドキュメントに適用され、レスポンスにはrebased: trueと示されます。代わりに409 workflow_conflictを受け取るには、strict: trueを送ります。 - 安定したキーで指定する。ショットはその ID、フォルダーやカットはその ID、キャストの行はそのロールのスラッグ、結果はその結果キーで指定します。位置では指定しません。スタジオとスクリプトが、同じプロダクションを同時に編集する可能性があるためです。
- ID は自分で作る。ショット、フォルダー、カット、コピーを作成する操作は、自分が渡した ID を使います。これにより、手元のコピーとサーバーは、すべての ID で一致します。
- 削除はゴミ箱に入る。削除は、削除したものをプロダクションのゴミ箱に移動するだけで、操作によって復元できます。何かを完全に消すのは、明示的な完全削除だけです。
- レスポンスをそのまま採用する。手元のコピーは、マージするのではなく
productionに置き換え、次のbaseVersionにはversionを送ります。
共有は操作ではありません。共有を変えようとするバッチは拒否されます。receipts には、操作ごとに過去形の 1 行、{ op, summary, ids?, impact? } が入ります。アシスタントが何をしたかを知りたい人には、これを見せてください。warnings には、失敗ではないものの伝える価値があること、たとえば何も変えなかった操作などが挙げられます。
バッチを適用する前にプレビューする
dryRun: true は、バッチが何を行うかを尋ねます。サーバーは、実際の書き込みと同じコンテキスト、同じ拒否ルールで処理を実行しますが、保存する前に停止します。応答は { dryRun: true, version, receipts, warnings } で、production は含まれません。各レシートには class(S は安全、D は削除、P は作品にアクセスできる人の変更、$ はクレジットの消費)が加わり、削除を元に戻せる場合は restorable: true も加わります。
プレビューに対応する前のデプロイメントは、dryRun を無視してバッチを適用します。そのため、まず空のバッチでこのフラグを確認してください。空のバッチは、どのデプロイメントでも何も変更しません。
{ "ops": [], "dryRun": true }dryRun: true を含む応答だけが「はい」です。それ以外はすべて「いいえ」であり、ここではプレビューできないとユーザーに伝えて、バッチを送信しないでください。実際のプレビューの応答でも、このマーカーを確認してください。更新中のデプロイメントは、次のリクエストを別のサーバーで処理することがあるためです。マーカーの代わりに production を含む応答は、バッチが適用されたことを意味します。その場合は、その結果を採用し、もう一度送信しないでください。SDK は、この 2 つの確認をどちらも代わりに行い、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を使うと、ショット自体を変えずに、その 1 回の実行だけを変更できます。 - 先に見積もる。
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)ほかのメディアのルートは、1 つのショットに対して動作します。
| ルート | ボディ | 説明 |
|---|---|---|
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 を含む、このページのすべての有料ルートで受け付けられます。
リンクされたクリップと撮り直し
デプロイメントによっては、確認済みの 2 つのフレームの間を動くクリップに対応しています。まず 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 は、監督(Director)の実行を開始し、{ 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 3 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": []
}手順は、通常のノードのルート、たとえば動画とオーディオを結合(Merge Video & Audio)や動画を結合を使って、順番に実行します。{ "fromStep": "…" } という値は、前の手順の出力です。その手順が生成した URL に置き換えてください。そして、完成したファイルを、カットを追加する操作で、プロダクションにカットとして記録します。
組み立てるものがない場合、つまりクリップが 2 本未満の場合、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 を返します。クレジットはかからず、1 分あたり 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 | 必須です。再生順に、1 つ以上のシーンを指定します。各シーンが、動画トラック上の 1 つのクリップになります。 |
timeline.musicAssetUrl | 音楽トラックです。デフォルトの空文字列にすると、含まれません。 |
timeline.narrationAssetUrl | ナレーショントラックで、専用のオーディオレーンに配置されます。 |
timeline.fadeOutDurationSec | 音楽の末尾のフェードで、デフォルトは 0.8 秒です。FreeCut JSON のみです。 |
scene.sceneEntityId、scene.compositeUrl | 必須です。シーンの ID と、その結合済みクリップです。 |
scene.shots | 必須です。{ shot_id, duration_seconds, cut_decision? } からなる、1 つ以上のショットです。各ショットの長さを足すと、シーンの長さになります。 |
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 は、トランジションの長さの分だけ、2 つのクリップを重ねます。hard_cut と match_cut は、クリップをそのままつなぎます。どのショットにも cut_decision がない場合、書き出しは単純な連結になります。シーンごとに 1 つのクリップを、順に、すべてハードカットでつなぎ、音楽はタイムライン全体に流れます。シーンの中のトリムは適用されません。各シーンのクリップは、すでに結合されているためです。
共有とリミックスのレコード
ショットレコードは、自分が組み立てたショットの状態を保存します。ピッカーの選択、プロンプト、対象のモデル、@ でメンションしたアセット、結果です。レコードは推測できない短い ID で保存され、この ID が /s/:id の共有リンクとワンクリックでのリミックスを支えています。これらのルートは、プロダクションとは別のものです。
| メソッド | パス | 認証 | 説明 |
|---|---|---|---|
POST | /v1/shots | ベアラートークン | レコードを作成します。{ 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 を含む)、書き込みの 2 つの 409 コードに対する WorkflowConflictError、InsufficientCreditsError、StorageExceededError、NotFoundError です。
よくある質問
関連ページ
スタジオプロダクション
パイプライン
ジョブ
音声とメディア
ワークフロー
最終更新