@nodaro/sdk 2.12.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { SCENE3D_V2_LIMITS, WORKSPACE_HEADER } from '@nodaro/shared';
2
- export { CHARACTER_ASPECT_DEFAULTS, CHARACTER_ASPECT_OPTIONS, CHARACTER_STYLES, OBJECT_ASPECT_DEFAULTS as CREATURE_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS as CREATURE_ASPECT_OPTIONS, CREATURE_ATTACH_COLUMNS, LOCATION_ASSET_TYPES, LOCATION_ATTACH_COLUMNS, OBJECT_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS, OBJECT_ASSET_TYPES, OBJECT_ATTACH_COLUMNS, OUTPUT_BEARING_NODE_STATUSES, PROMPT_PREFIX_KEY, PROMPT_SUFFIX_KEY, SURROUND_DIRECTIONS, USAGE_GROUP_BYS, WORKSPACE_HEADER, nodeStateMayCarryOutput } from '@nodaro/shared';
1
+ import { SCENE3D_V2_LIMITS, remapTranscriptThroughEdl, WORKSPACE_HEADER } from '@nodaro/shared';
2
+ export { CHARACTER_ASPECT_DEFAULTS, CHARACTER_ASPECT_OPTIONS, CHARACTER_STYLES, OBJECT_ASPECT_DEFAULTS as CREATURE_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS as CREATURE_ASPECT_OPTIONS, CREATURE_ATTACH_COLUMNS, LOCATION_ASSET_TYPES, LOCATION_ATTACH_COLUMNS, OBJECT_ASPECT_DEFAULTS, OBJECT_ASPECT_OPTIONS, OBJECT_ASSET_TYPES, OBJECT_ATTACH_COLUMNS, OUTPUT_BEARING_NODE_STATUSES, PROMPT_PREFIX_KEY, PROMPT_SUFFIX_KEY, SURROUND_DIRECTIONS, USAGE_GROUP_BYS, WORKSPACE_HEADER, nodeStateMayCarryOutput, unwrapEditPlanOutput } from '@nodaro/shared';
3
3
  import { buildPersonHints } from '@nodaro/prompts';
4
4
  export { PEOPLE, PERSON_DIMENSION_LABELS, PERSON_DIMENSION_ORDER, buildPersonHints } from '@nodaro/prompts';
5
5
 
@@ -2156,9 +2156,13 @@ var MediaResource = class {
2156
2156
  * Download a social video (YouTube / TikTok / Instagram / X / Facebook) into
2157
2157
  * your storage (`POST /v1/download-video`). `maxHeight` caps the resolution
2158
2158
  * (default "best"); `sectionStartSec` + `sectionEndSec` (both-or-neither) fetch
2159
- * ONLY that time range instead of the whole video. Returns a `downloadId`;
2160
- * progress streams from `GET /v1/download-video/progress/:downloadId`
2161
- * (server-sent events) and the finished file lands in your library.
2159
+ * ONLY that time range instead of the whole video. A download that arrives
2160
+ * with no audio stream FAILS by default (it is usually a degraded source
2161
+ * response, and is retried through other routes first); pass
2162
+ * `requireAudio: false` to accept a clip that really has no sound. Returns a
2163
+ * `downloadId`; progress streams from
2164
+ * `GET /v1/download-video/progress/:downloadId` (server-sent events) and the
2165
+ * finished file lands in your library.
2162
2166
  */
2163
2167
  downloadVideo(input) {
2164
2168
  return this.client.request("POST", "/v1/download-video", { body: input });
@@ -2260,6 +2264,52 @@ var MediaResource = class {
2260
2264
  trimVideo(input) {
2261
2265
  return this.client.request("POST", "/v1/trim-video", { body: input });
2262
2266
  }
2267
+ /**
2268
+ * Burn captions into a video (`POST /v1/add-captions`). Give the words as
2269
+ * `text`, word-timed `captions[]`, or let it transcribe (`autoTranscribe`,
2270
+ * the default when neither is set).
2271
+ *
2272
+ * `style: "subtitle"` renders statically (FFmpeg) UNLESS it carries a styling
2273
+ * lever, in which case it — like the KINETIC styles (`word-highlight` /
2274
+ * `karaoke` / `tiktok-words` / `word-pop` / `bouncy`) — renders via Remotion.
2275
+ * `look` picks a preset — `outline` (Montserrat 900, UPPERCASE, black outline,
2276
+ * yellow spoken word — the TikTok/Reels read) or `clean`; an UNSET look renders
2277
+ * as `outline`. The explicit STYLING levers (`fontFamily`, `fontWeight`,
2278
+ * `strokeColor`/`strokeWidth`, `uppercase`, `positionY`, `maxWordsPerLine`)
2279
+ * override individual fields of it and now apply to `subtitle` too. Only
2280
+ * `highlightColor` (the spoken-word cursor) and `animate` stay kinetic-only
2281
+ * and are REJECTED on `subtitle`.
2282
+ *
2283
+ * `animate` (default true) freezes the per-word MOTION on the kinetic styles
2284
+ * when set to false — the grouping, line-holding and spoken-word highlight
2285
+ * stay; only the movement stops.
2286
+ *
2287
+ * `maxWordsPerLine` (1-20) caps how many words a line — or a `tiktok-words`
2288
+ * page — may hold, on top of the width budget / sentence ends / pauses that
2289
+ * already close one; it is inert on `word-pop`, which is always one word.
2290
+ * Like every lever here it exists top-level AND per segment, and a segment
2291
+ * that does not name its own inherits the top-level value.
2292
+ *
2293
+ * `segments[]` applies DIFFERENT treatments to non-overlapping time ranges in
2294
+ * one call (e.g. a large top intro, then a small bottom body); a segment that
2295
+ * names its own `look` starts fresh from that preset and does not inherit the
2296
+ * top-level explicit levers. Poll `jobs.get(jobId)`.
2297
+ *
2298
+ * `word-highlight` shows ONE held line at a time and a word's
2299
+ * `startMs`/`endMs` is its SPOKEN window (what times the highlight, not how
2300
+ * long the text is on screen) — so `captions[]` can be handed over verbatim
2301
+ * from an `audio.transcribe()` job's `output_data.words`, with
2302
+ * `autoTranscribe: false`.
2303
+ */
2304
+ addCaptions(input) {
2305
+ const { autoTranscribe, transcribeProvider, ...rest } = input;
2306
+ const body = {
2307
+ ...rest,
2308
+ ...autoTranscribe !== void 0 ? { auto_transcribe: autoTranscribe } : {},
2309
+ ...transcribeProvider !== void 0 ? { transcribe_provider: transcribeProvider } : {}
2310
+ };
2311
+ return this.client.request("POST", "/v1/add-captions", { body });
2312
+ }
2263
2313
  /**
2264
2314
  * Trim (and extract) audio from a video or audio source
2265
2315
  * (`POST /v1/trim-audio`) to `[startTime, endTime]` seconds, in `audioFormat`
@@ -2292,6 +2342,25 @@ var MediaResource = class {
2292
2342
  slideshow(input) {
2293
2343
  return this.client.request("POST", "/v1/slideshow", { body: input });
2294
2344
  }
2345
+ /**
2346
+ * Place 1–20 timed image layers over a video (`POST /v1/video-overlay`) —
2347
+ * rendered locally (FFmpeg) in one pass, no AI; the base audio is kept
2348
+ * untouched. Each layer has an `imageUrl`, a `start` and an optional `end`
2349
+ * in seconds (no `end` = to the end of the video) and a placement: a
2350
+ * `preset` (`"card"`, `"corner-badge"` with a `corner`, `"full-frame"`) or
2351
+ * an explicit box in {@link MediaResource.imageOverlay}'s percent vocabulary
2352
+ * (`anchor`, `x`, `y`, `width`, `height`, `fit`) — an explicit box field
2353
+ * overrides the preset, and a layer with neither is a corner badge
2354
+ * (bottom-right, or the `corner` it names). `outputAspect` renders onto a
2355
+ * 16:9 / 9:16 / 1:1 / 4:5 canvas
2356
+ * (`baseFit` cover by default; `backgroundColor` pads `contain`). Poll
2357
+ * `jobs.get(jobId)`: the output carries `videoUrl`, `thumbnailUrl`, `width`,
2358
+ * `height`, `durationSec` and `warnings[]` (a layer clipped or skipped at the
2359
+ * video's end, an animated image's first frame, re-encoded audio).
2360
+ */
2361
+ videoOverlay(input) {
2362
+ return this.client.request("POST", "/v1/video-overlay", { body: input });
2363
+ }
2295
2364
  /**
2296
2365
  * Probe a social video's metadata (`POST /v1/video-metadata`) — duration,
2297
2366
  * dimensions, title, live status — WITHOUT downloading it. A direct read, not a
@@ -2363,6 +2432,32 @@ var AudioResource = class {
2363
2432
  combine(input) {
2364
2433
  return this.client.request("POST", "/v1/combine-audio", { body: input });
2365
2434
  }
2435
+ /**
2436
+ * Transcribe an audio (or video) track to text (`POST /v1/transcribe`).
2437
+ *
2438
+ * Three engines are accepted. `elevenlabs-stt` (Scribe) is always word-level,
2439
+ * flag or not, and is the only lane that honours `diarize` (who spoke) and
2440
+ * `tagAudioEvents` (laughter, applause, …); `incredibly-fast-whisper` returns
2441
+ * word timings when you ask for them. `whisper` returns NO word timings at
2442
+ * all — named explicitly or reached by OMITTING `provider`, which still falls
2443
+ * back to it — so asking it for them (`wordTimestamps: true`) is rejected with
2444
+ * a `400 validation_error` at ingress, before any credit is spent. A kinetic
2445
+ * caption render therefore has to name one of the other two.
2446
+ *
2447
+ * Poll `jobs.get(jobId)`; the finished job's `output_data` is a
2448
+ * {@link TranscribeJobOutput}: `text` (the whole transcript), `words`
2449
+ * (caption-shaped, in MILLISECONDS), `json` (the normalized
2450
+ * {@link Transcript}, also ms) — and a top-level `segments` array that is in
2451
+ * SECONDS, not ms.
2452
+ *
2453
+ * `words` is the caption source for a kinetic burn-in: hand it to
2454
+ * `media.addCaptions()` as `captions` with `autoTranscribe: false` and the
2455
+ * render uses those exact words (correct the `text` of an entry in between
2456
+ * and the fix is what burns in).
2457
+ */
2458
+ transcribe(input) {
2459
+ return this.client.request("POST", "/v1/transcribe", { body: input });
2460
+ }
2366
2461
  };
2367
2462
 
2368
2463
  // src/resources/credits.ts
@@ -3451,7 +3546,104 @@ var WorkspacesResource = class {
3451
3546
  return this.client.requestText("GET", `/v1/workspaces/${encodeURIComponent(id)}/usage`, { query: { ...opts, format: "csv" } });
3452
3547
  }
3453
3548
  };
3454
- var SDK_VERSION = "2.12.0" ;
3549
+ var EditResource = class {
3550
+ constructor(client) {
3551
+ this.client = client;
3552
+ }
3553
+ client;
3554
+ /**
3555
+ * Detect silence ranges in an audio/video source (`POST /v1/silence-detect`).
3556
+ * Keyless — one ffmpeg pass over the source's audio proxy.
3557
+ */
3558
+ silenceDetect(input) {
3559
+ return this.client.request("POST", "/v1/silence-detect", {
3560
+ body: {
3561
+ audioUrl: input.audioUrl,
3562
+ ...input.thresholdDb !== void 0 ? { thresholdDb: input.thresholdDb } : {},
3563
+ ...input.minSilenceMs !== void 0 ? { minSilenceMs: input.minSilenceMs } : {},
3564
+ ...input.padMs !== void 0 ? { padMs: input.padMs } : {},
3565
+ ...input.workflowId !== void 0 ? { workflowId: input.workflowId } : {}
3566
+ }
3567
+ });
3568
+ }
3569
+ /**
3570
+ * Measure how far apart the clocks of 2–6 recordings of one conversation are
3571
+ * (`POST /v1/audio-sync`), by cross-correlating their audio — keyless, one
3572
+ * ffmpeg decode per source. The finished job's `output_data.json` is an
3573
+ * {@link AudioSyncResult}. A malformed request (fewer than 2 or more than 6
3574
+ * sources, a repeated id, a `reference` that is not one of the ids) throws a
3575
+ * typed `NodaroError` (400, `code: "validation_error"`) before any credits
3576
+ * are reserved.
3577
+ */
3578
+ audioSync(input) {
3579
+ return this.client.request("POST", "/v1/audio-sync", {
3580
+ body: {
3581
+ sources: input.sources.map((s) => ({ id: s.id, url: s.url })),
3582
+ ...input.reference !== void 0 ? { reference: input.reference } : {},
3583
+ ...input.workflowId !== void 0 ? { workflowId: input.workflowId } : {}
3584
+ }
3585
+ });
3586
+ }
3587
+ /**
3588
+ * Render an edit decision list into a video or audio cut (`POST /v1/apply-edl`).
3589
+ * The EDL is validated at ingress — an unresolvable source or a picture-less
3590
+ * segment on a video edit throws a typed `NodaroError` (400, `code:
3591
+ * "invalid_edl"`) before any credits are reserved.
3592
+ */
3593
+ applyEdl(input) {
3594
+ return this.client.request("POST", "/v1/apply-edl", {
3595
+ body: {
3596
+ edl: input.edl,
3597
+ ...input.sources !== void 0 ? { sources: input.sources } : {},
3598
+ ...input.transcript !== void 0 ? { transcript: input.transcript } : {},
3599
+ ...input.output !== void 0 ? { output: input.output } : {},
3600
+ ...input.quality !== void 0 ? { quality: input.quality } : {},
3601
+ ...input.crossfadeMs !== void 0 ? { crossfadeMs: input.crossfadeMs } : {},
3602
+ ...input.workflowId !== void 0 ? { workflowId: input.workflowId } : {}
3603
+ }
3604
+ });
3605
+ }
3606
+ /**
3607
+ * Plan a transcript-driven cut (`POST /v1/edit-plan`). Reads a timed
3608
+ * transcript (plus optional silence ranges) and the media sources, and plans
3609
+ * the edit. The finished job's `output_data` holds the plan: an `Edl`
3610
+ * (`"tighten"`), an {@link EdlClipSet} (`"clips"`), or a {@link ChapterSet}
3611
+ * (`"chapters"`) — normalize it with {@link unwrapEditPlanOutput}.
3612
+ *
3613
+ * On a self-hosted install the request relays to nodaro.ai and needs the
3614
+ * install connected (a 503 `code: "nodaro_connection_required"` otherwise);
3615
+ * on nodaro.ai it runs directly.
3616
+ */
3617
+ editPlan(input) {
3618
+ return this.client.request("POST", "/v1/edit-plan", {
3619
+ body: {
3620
+ mode: input.mode,
3621
+ planTier: input.planTier,
3622
+ transcript: input.transcript,
3623
+ sources: input.sources,
3624
+ ...input.silence !== void 0 ? { silence: input.silence } : {},
3625
+ ...input.instructions !== void 0 ? { instructions: input.instructions } : {},
3626
+ ...input.styleGuide !== void 0 ? { styleGuide: input.styleGuide } : {},
3627
+ ...input.count !== void 0 ? { count: input.count } : {},
3628
+ ...input.targetDurationSec !== void 0 ? { targetDurationSec: input.targetDurationSec } : {},
3629
+ ...input.targetAspect !== void 0 ? { targetAspect: input.targetAspect } : {},
3630
+ ...input.platform !== void 0 ? { platform: input.platform } : {},
3631
+ ...input.workflowId !== void 0 ? { workflowId: input.workflowId } : {}
3632
+ }
3633
+ });
3634
+ }
3635
+ /**
3636
+ * Remap a transcript through an EDL — a PURE client-side transform (NO
3637
+ * request). Returns a new transcript whose word (and segment) timings are on
3638
+ * the EDL's rendered output clock, dropping words that fall in cut regions and
3639
+ * clipping straddlers. Runs `@nodaro/shared`'s canonical
3640
+ * `remapTranscriptThroughEdl`, the same remap `applyEdl` performs server-side.
3641
+ */
3642
+ remapTranscript(edl, transcript) {
3643
+ return remapTranscriptThroughEdl(edl, transcript);
3644
+ }
3645
+ };
3646
+ var SDK_VERSION = "2.16.0" ;
3455
3647
  var CLIENT_HEADER = "X-Nodaro-Client";
3456
3648
  var isBrowser = () => typeof window !== "undefined" && typeof window.document !== "undefined";
3457
3649
  var NodaroClient = class _NodaroClient {
@@ -3509,6 +3701,7 @@ var NodaroClient = class _NodaroClient {
3509
3701
  tutorials;
3510
3702
  organizations;
3511
3703
  workspaces;
3704
+ edit;
3512
3705
  /** The workspace this client acts in; undefined = the personal space. */
3513
3706
  workspaceId;
3514
3707
  constructor(opts) {
@@ -3556,6 +3749,7 @@ var NodaroClient = class _NodaroClient {
3556
3749
  this.tutorials = new TutorialsResource(this);
3557
3750
  this.organizations = new OrganizationsResource(this);
3558
3751
  this.workspaces = new WorkspacesResource(this);
3752
+ this.edit = new EditResource(this);
3559
3753
  }
3560
3754
  /**
3561
3755
  * A client that acts in `workspaceId`, sharing this one's auth and config.
@@ -3712,6 +3906,6 @@ function supabaseAuth(supabase) {
3712
3906
  };
3713
3907
  }
3714
3908
 
3715
- export { AppsResource, AudioResource, CREATURE_ASSET_TYPES, CallbackAuth, CatalogsResource, CharactersResource, CommunityResource, CopilotResource, CreaturesResource, CreditsResource, DeveloperAppsResource, ExecutionsResource, ForbiddenError, InsufficientCreditsError, JobAbortedError, JobBlockedError, JobFailedError, JobHeldError, JobTimeoutError, JobsResource, LibraryResource, LlmResource, LocationsResource, MediaResource, ModelsResource, NodaroClient, NodaroError, NodesResource, NotFoundError, OAuthResource, ObjectsResource, OrganizationsResource, PickerCatalogsResource, PipelinesResource, PresetsResource, ProjectsResource, PromptHelperResource, RateLimitedError, RecastResource, ReduceResource, SCENE3D_DELIVERY_ASSET_USAGE, Scene3DResource, ShotsResource, StaticTokenAuth, StorageExceededError, StudioOpError, StudioPreviewAppliedError, StudioPreviewUnavailable, StudioProductionsResource, StudioResource, TemplatesResource, TutorialsResource, UnauthorizedError, UploadsResource, VideoProResource, VoicesResource, WorkflowConflictError, WorkflowsResource, WorkspacesResource, buildPersonSeedPrompt, createClient, isStudioGenerateEstimate, supabaseAuth, throwFromResponse };
3909
+ export { AppsResource, AudioResource, CREATURE_ASSET_TYPES, CallbackAuth, CatalogsResource, CharactersResource, CommunityResource, CopilotResource, CreaturesResource, CreditsResource, DeveloperAppsResource, EditResource, ExecutionsResource, ForbiddenError, InsufficientCreditsError, JobAbortedError, JobBlockedError, JobFailedError, JobHeldError, JobTimeoutError, JobsResource, LibraryResource, LlmResource, LocationsResource, MediaResource, ModelsResource, NodaroClient, NodaroError, NodesResource, NotFoundError, OAuthResource, ObjectsResource, OrganizationsResource, PickerCatalogsResource, PipelinesResource, PresetsResource, ProjectsResource, PromptHelperResource, RateLimitedError, RecastResource, ReduceResource, SCENE3D_DELIVERY_ASSET_USAGE, Scene3DResource, ShotsResource, StaticTokenAuth, StorageExceededError, StudioOpError, StudioPreviewAppliedError, StudioPreviewUnavailable, StudioProductionsResource, StudioResource, TemplatesResource, TutorialsResource, UnauthorizedError, UploadsResource, VideoProResource, VoicesResource, WorkflowConflictError, WorkflowsResource, WorkspacesResource, buildPersonSeedPrompt, createClient, isStudioGenerateEstimate, supabaseAuth, throwFromResponse };
3716
3910
  //# sourceMappingURL=index.js.map
3717
3911
  //# sourceMappingURL=index.js.map