@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.cjs +203 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +466 -12
- package/dist/index.d.ts +466 -12
- package/dist/index.js +201 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -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.
|
|
2160
|
-
*
|
|
2161
|
-
*
|
|
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
|
|
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 shared.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.
|
|
@@ -3788,6 +3982,10 @@ Object.defineProperty(exports, "nodeStateMayCarryOutput", {
|
|
|
3788
3982
|
enumerable: true,
|
|
3789
3983
|
get: function () { return shared.nodeStateMayCarryOutput; }
|
|
3790
3984
|
});
|
|
3985
|
+
Object.defineProperty(exports, "unwrapEditPlanOutput", {
|
|
3986
|
+
enumerable: true,
|
|
3987
|
+
get: function () { return shared.unwrapEditPlanOutput; }
|
|
3988
|
+
});
|
|
3791
3989
|
Object.defineProperty(exports, "PEOPLE", {
|
|
3792
3990
|
enumerable: true,
|
|
3793
3991
|
get: function () { return prompts.PEOPLE; }
|
|
@@ -3815,6 +4013,7 @@ exports.CopilotResource = CopilotResource;
|
|
|
3815
4013
|
exports.CreaturesResource = CreaturesResource;
|
|
3816
4014
|
exports.CreditsResource = CreditsResource;
|
|
3817
4015
|
exports.DeveloperAppsResource = DeveloperAppsResource;
|
|
4016
|
+
exports.EditResource = EditResource;
|
|
3818
4017
|
exports.ExecutionsResource = ExecutionsResource;
|
|
3819
4018
|
exports.ForbiddenError = ForbiddenError;
|
|
3820
4019
|
exports.InsufficientCreditsError = InsufficientCreditsError;
|