@jtdigital/renderbox-sdk 0.2.0 → 0.3.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.
Files changed (72) hide show
  1. package/README.md +133 -53
  2. package/dist/graph.d.ts +127 -15
  3. package/dist/graph.d.ts.map +1 -1
  4. package/dist/graph.js +178 -40
  5. package/dist/graph.js.map +1 -1
  6. package/dist/index.d.ts +3 -10
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -10
  9. package/dist/index.js.map +1 -1
  10. package/dist/recipes/depthBlurPipeline.d.ts +2 -3
  11. package/dist/recipes/depthBlurPipeline.d.ts.map +1 -1
  12. package/dist/recipes/depthBlurPipeline.js +8 -11
  13. package/dist/recipes/depthBlurPipeline.js.map +1 -1
  14. package/dist/recipes/faceRedact.d.ts +3 -5
  15. package/dist/recipes/faceRedact.d.ts.map +1 -1
  16. package/dist/recipes/faceRedact.js +15 -19
  17. package/dist/recipes/faceRedact.js.map +1 -1
  18. package/dist/recipes/letterbox.d.ts +3 -2
  19. package/dist/recipes/letterbox.d.ts.map +1 -1
  20. package/dist/recipes/letterbox.js +5 -4
  21. package/dist/recipes/letterbox.js.map +1 -1
  22. package/dist/recipes/makeSlideshow.d.ts +2 -3
  23. package/dist/recipes/makeSlideshow.d.ts.map +1 -1
  24. package/dist/recipes/makeSlideshow.js +37 -37
  25. package/dist/recipes/makeSlideshow.js.map +1 -1
  26. package/dist/recipes/snip.d.ts +3 -2
  27. package/dist/recipes/snip.d.ts.map +1 -1
  28. package/dist/recipes/snip.js +2 -3
  29. package/dist/recipes/snip.js.map +1 -1
  30. package/dist/streams.d.ts +1481 -0
  31. package/dist/streams.d.ts.map +1 -0
  32. package/dist/streams.js +241 -0
  33. package/dist/streams.js.map +1 -0
  34. package/dist/types.d.ts +0 -5
  35. package/dist/types.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/dist/input.d.ts +0 -30
  38. package/dist/input.d.ts.map +0 -1
  39. package/dist/input.js +0 -167
  40. package/dist/input.js.map +0 -1
  41. package/dist/job.d.ts +0 -18
  42. package/dist/job.d.ts.map +0 -1
  43. package/dist/job.js +0 -15
  44. package/dist/job.js.map +0 -1
  45. package/dist/ops/ai.d.ts +0 -156
  46. package/dist/ops/ai.d.ts.map +0 -1
  47. package/dist/ops/ai.js +0 -134
  48. package/dist/ops/ai.js.map +0 -1
  49. package/dist/ops/audio.d.ts +0 -359
  50. package/dist/ops/audio.d.ts.map +0 -1
  51. package/dist/ops/audio.js +0 -296
  52. package/dist/ops/audio.js.map +0 -1
  53. package/dist/ops/combine.d.ts +0 -246
  54. package/dist/ops/combine.d.ts.map +0 -1
  55. package/dist/ops/combine.js +0 -230
  56. package/dist/ops/combine.js.map +0 -1
  57. package/dist/ops/source.d.ts +0 -41
  58. package/dist/ops/source.d.ts.map +0 -1
  59. package/dist/ops/source.js +0 -34
  60. package/dist/ops/source.js.map +0 -1
  61. package/dist/ops/video.d.ts +0 -695
  62. package/dist/ops/video.d.ts.map +0 -1
  63. package/dist/ops/video.js +0 -620
  64. package/dist/ops/video.js.map +0 -1
  65. package/dist/output.d.ts +0 -26
  66. package/dist/output.d.ts.map +0 -1
  67. package/dist/output.js +0 -144
  68. package/dist/output.js.map +0 -1
  69. package/dist/recipes/subtitleBurn.d.ts +0 -11
  70. package/dist/recipes/subtitleBurn.d.ts.map +0 -1
  71. package/dist/recipes/subtitleBurn.js +0 -16
  72. package/dist/recipes/subtitleBurn.js.map +0 -1
package/README.md CHANGED
@@ -1,81 +1,161 @@
1
1
  # @jtdigital/renderbox-sdk
2
2
 
3
3
  Typed TypeScript SDK for [renderbox](https://renderbox.dev) video processing.
4
- Compose a video / audio / AI pipeline with a phantom-typed graph builder, then
5
- turn it into a job message ready to submit to a renderbox engine.
4
+ You describe a pipeline as a graph of sort-typed streams; renderbox's GPU/CPU
5
+ workers run it. The SDK builds the graph and the job message it does **not**
6
+ process video itself.
6
7
 
7
8
  ```bash
8
9
  npm install @jtdigital/renderbox-sdk
9
10
  ```
10
11
 
11
- ## Usage
12
+ ## Building a pipeline
12
13
 
13
- ### With a recipe
14
+ Open inputs, chain operations on the returned streams, attach outputs:
14
15
 
15
16
  ```ts
16
- import { faceRedact, buildJob } from '@jtdigital/renderbox-sdk';
17
-
18
- // a recipe returns a complete GraphBuilder
19
- const graph = faceRedact('input.mp4', 'redacted.mp4');
20
-
21
- const job = buildJob({
22
- jobId: 'job-123',
23
- userId: 'user-1',
24
- tenantId: 'my-tenant',
25
- graph,
26
- inputFiles: [
27
- { s3_key: 'uploads/clip.mp4', filename: 'input.mp4', content_type: 'video/mp4' },
28
- ],
29
- output: { bucket: 'my-bucket', key: 'out/job-123.mp4', format: 'mp4' },
30
- });
31
- // `job` is a plain JobMessage — publish it to your renderbox engine over AMQP,
32
- // then consume ProgressMessage / CompletionMessage from your result queue.
17
+ import { graph } from '@jtdigital/renderbox-sdk';
18
+
19
+ const g = graph();
20
+ const [v, a] = g.open('in.mp4'); // [VideoStream, AudioStream]
21
+
22
+ const out = v.scale({ w: 1280, h: 720 }) // ops are methods → autocomplete
23
+ .fade({ type: 'in', d: 1 });
24
+
25
+ g.write('out.mp4', out, a);
33
26
  ```
34
27
 
35
- ### With the low-level DSL
28
+ Streams are sort-typed: `VideoStream` exposes video ops, `AudioStream` audio
29
+ ops, `DetectionStream` tracking and filtering. A sort mismatch (piping audio
30
+ into a video op) is a **compile error**, not a runtime one.
31
+
32
+ ### Detection → tracking → redaction
36
33
 
37
34
  ```ts
38
- import { file, sinkAv, video, ai, combine } from '@jtdigital/renderbox-sdk';
35
+ const g = graph();
36
+ const [v, a] = g.open('street.mp4');
37
+
38
+ const faces = v
39
+ .detect({ model: 'scrfd_10g', threshold: 0.12 }) // VideoStream → DetectionStream
40
+ .track({ algorithm: 'bytetrack' }); // stable ids across frames
39
41
 
40
- const [v, a] = file('clip.mp4'); // source → [Video, Audio]
41
- const dets = ai.detect({ model: 'scrfd_10g' })(v); // Video → Detection
42
- const redacted = combine.redact(dets, { mode: 'black' })(v);
43
- const graph = sinkAv('out.mp4', video.scale({ w: 1280, h: 720 })(redacted), a);
42
+ g.write('redacted.mp4', v.redact(faces, { mode: 'black' }), a);
44
43
  ```
45
44
 
46
- Streams are phantom-typed (`Stream<'Video'>`, `Stream<'Audio'>`, …), so a sort
47
- mismatch is a compile error rather than a runtime one.
45
+ ### Multiple inputs
48
46
 
49
- ## What's exported
47
+ Combine ops are methods on the first stream; open more inputs on the same graph:
50
48
 
51
- - **Graph** — `GraphBuilder`, `pipe`, the `renderbox-ir` wire types (`GraphJSON`,
52
- `OpNode`, `Origin`, `SinkTarget`, )
53
- - **Sources / sinks** — `file`, `rtsp`, `mqtt`, `hls`, `camera`, `slot`;
54
- `sinkAv`, `sinkVideo`, `sinkAudio`, `sinkDetectionsJsonl`, `sinkTranscript*`,
55
- `sinkMqtt`, `sinkHttp`, `outputSlot`
56
- - **Ops** — the `video` / `audio` / `ai` / `combine` / `source` namespaces
57
- (220+ typed operations)
58
- - **Recipes** — `letterbox`, `snip`, `faceRedact`, `depthBlurPipeline`,
59
- `makeSlideshow`
60
- - **Job / wire** — `buildJob`, `JobMessage`, `ProgressMessage`,
61
- `CompletionMessage`, `FailureMessage`
49
+ ```ts
50
+ const g = graph();
51
+ const [v, a] = g.open('clip.mp4');
52
+ const [logo] = g.open('logo.png');
62
53
 
63
- ## Maintainers
54
+ g.write('branded.mp4', v.overlay(logo, { x: 20, y: 20 }), a);
55
+ ```
64
56
 
65
- Most of `src/` is **generated** from the Rust SDK's op registry — do not edit it
66
- by hand. Regenerate from the renderbox-sdk repo root:
57
+ ### Detections to a data sink
67
58
 
68
- ```bash
69
- cargo run --bin gen-ts-sdk -- ts-sdk
70
- npm --prefix ts-sdk run build # dist/ is committed for git/file consumers
59
+ Not every output is a video — write structured results too:
60
+
61
+ ```ts
62
+ const g = graph();
63
+ const [v] = g.open('traffic.mp4');
64
+ const cars = v.detect({ model: 'yolo11x', classes: 'car' }).track({ algorithm: 'bytetrack' });
65
+ g.writeDetections('cars.jsonl', cars);
71
66
  ```
72
67
 
73
- The generator owns `package.json`, `tsconfig.json`, `src/index.ts`, and every
74
- `src/ops/*.ts` — their content lives in `src/bin/gen_ts_sdk.rs`. Only
75
- `src/recipes/`, `src/job.ts`, `src/protocol.ts`, `tests/`, `README.md`, and
76
- `LICENSE` are hand-written (the generator re-exports them but never overwrites
77
- them). Wire-format parity with `renderbox-ir` is enforced by
78
- `tests/ts_wire_parity.rs` in the parent repo.
68
+ ### Recipes
69
+
70
+ One-liners for common pipelines. `faceRedact`, `depthBlurPipeline`, and
71
+ `makeSlideshow` return a ready `Graph`; `letterbox` and `snip` are stream
72
+ transforms:
73
+
74
+ ```ts
75
+ import { faceRedact, snip } from '@jtdigital/renderbox-sdk';
76
+
77
+ const g = faceRedact('in.mp4', 'out.mp4', { redactMode: 'blur' });
78
+
79
+ // or as a step in your own graph:
80
+ const [v] = graph().open('long.mp4');
81
+ const clip = snip(v, 30, 45); // seconds 30–45
82
+ ```
83
+
84
+ ## Running your pipeline on renderbox
85
+
86
+ A `Graph` is a *description*. To actually render it, submit it as a job to
87
+ renderbox infrastructure — the orchestrator compiles it and dispatches it to a
88
+ GPU or CPU worker, which streams back progress and a completion result.
89
+
90
+ You reference inputs and outputs **by S3 key**. Two calls give you everything the
91
+ worker needs: `g.json()` is the graph_ir, and `g.io()` is the S3 ⇄ storage
92
+ binding (which object to download for each input, where to upload each output).
93
+ The SDK assigns **no identity** — the job id, tenant, and user come from whoever
94
+ submits (your app/auth, or the managed client).
95
+
96
+ ```ts
97
+ import { graph, type ProgressMessage, type CompletionMessage } from '@jtdigital/renderbox-sdk';
98
+
99
+ const g = graph();
100
+ const [v, a] = g.open('uploads/raw.mp4'); // reference inputs by S3 key
101
+ g.write('renders/out.mp4', v.scale({ w: 1920, h: 1080 }), a);
102
+
103
+ const io = g.io();
104
+ // io.inputs → [{ s3_key: 'uploads/raw.mp4', filename: 'raw.mp4', content_type: 'video/mp4' }]
105
+ // io.outputs → [{ s3_key: 'renders/out.mp4', filename: 'out.mp4' }]
106
+
107
+ // Assemble the job with identity from YOUR context, then publish over AMQP.
108
+ // (The message omits pipeline_id so the orchestrator routes on graph_ir.)
109
+ const job = {
110
+ job_id: myApp.newJobId(), // assigned by your app/DB — not the SDK
111
+ tenant_id: ctx.tenantId, // from auth
112
+ graph_ir: g.json(),
113
+ input_files: io.inputs,
114
+ };
115
+ await channel.publish(EXCHANGE, ROUTING_KEY, Buffer.from(JSON.stringify(job)));
116
+ ```
117
+
118
+ You never write filenames by hand: `open('uploads/raw.mp4')` gives the graph a
119
+ sandbox-safe basename (`raw.mp4`) and records the S3 key for you — the worker
120
+ rejects slashes in the sandbox, which is why the graph and the binding differ.
121
+
122
+ Then consume results off your result queue. Progress arrives as the render
123
+ proceeds; completion carries the output location(s):
124
+
125
+ ```ts
126
+ function onProgress(msg: ProgressMessage) {
127
+ // percent is null until it can be computed (null ≠ 0%)
128
+ console.log(`${msg.job_id}: ${msg.percent ?? '—'}% @ ${msg.fps} fps`);
129
+ }
130
+
131
+ function onCompleted(msg: CompletionMessage) {
132
+ if (msg.status === 'success') {
133
+ for (const out of msg.outputs) console.log(out.kind, out.s3_key, out.bytes);
134
+ }
135
+ }
136
+ ```
137
+
138
+ ### Prefer a managed client?
139
+
140
+ [`@jtdigital/renderbox-client`](https://renderbox.dev/sdk/typescript) wraps the
141
+ renderbox SaaS HTTP API — API-key auth, asset upload, job submission, SSE
142
+ progress streaming, and result download — so you can hand it a `Graph` and get a
143
+ finished render back without running your own transport.
144
+
145
+ ## Surface
146
+
147
+ - **`graph()` → `Graph`** — inputs: `open` / `openVideo` / `openAudio` / `rtsp` /
148
+ `hls` / `mqtt` / `camera` / `screenCapture` / `slot`, plus synthetic sources.
149
+ Outputs: `write` / `writeVideo` / `writeAudio` / `writeDetections` /
150
+ `writeTranscript` / `writeMqtt` / `writeHttp` / `outputSlot`. Terminals:
151
+ `json()` (graph_ir) and `io()` (S3 ⇄ sandbox bindings).
152
+ - **Stream methods** — 220+ typed operations, one per engine op, on the sort it
153
+ applies to (`scale`, `crop`, `fade`, `detect`, `track`, `transcribe`,
154
+ `overlay`, `redact`, `depthBlur`, …).
155
+ - **Recipes** — `letterbox`, `snip`, `faceRedact`, `depthBlurPipeline`,
156
+ `makeSlideshow`.
157
+ - **Wire types** — `GraphJSON`, `OpNode`, `JobMessage`, `ProgressMessage`,
158
+ `CompletionMessage`, `FailureMessage`.
79
159
 
80
160
  ## License
81
161
 
package/dist/graph.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type { SortTag, Stream, ParamValue } from './types.js';
1
+ import type { ParamValue } from './types.js';
2
+ import type { VideoStream, AudioStream, DetectionStream, TranscriptStream } from './streams.js';
3
+ import type { InputFile } from './protocol.js';
2
4
  export type SortJSON = string | {
3
5
  Product: [SortJSON, SortJSON];
4
6
  };
@@ -170,25 +172,135 @@ export interface GraphJSON {
170
172
  roots: number[];
171
173
  io_manifest?: IoManifest;
172
174
  }
173
- export declare class GraphBuilder {
175
+ /** One file output the graph writes. `s3_key` is where you asked it stored;
176
+ * `filename` is the sandbox-local name the worker writes before uploading. */
177
+ export interface OutputBinding {
178
+ s3_key: string;
179
+ filename: string;
180
+ }
181
+ /** The S3 ⇄ sandbox mapping the submission layer needs: `inputs` drop straight
182
+ * into a job's `input_files`; each `output` names where a sink should land. */
183
+ export interface GraphIo {
184
+ inputs: InputFile[];
185
+ outputs: OutputBinding[];
186
+ }
187
+ export interface SinkAvOpts {
188
+ codec?: string;
189
+ crf?: number;
190
+ bitrate?: string;
191
+ preset?: string;
192
+ [key: string]: ParamValue | undefined;
193
+ }
194
+ export interface FileOpts {
195
+ loop?: number;
196
+ t?: number;
197
+ [key: string]: ParamValue | undefined;
198
+ }
199
+ export interface AmovieOpts {
200
+ filename: string;
201
+ seek_point?: number;
202
+ streams?: string;
203
+ loop?: number;
204
+ }
205
+ export interface AnullsrcOpts {
206
+ d?: number;
207
+ }
208
+ export interface BlankOpts {
209
+ width?: number;
210
+ height?: number;
211
+ duration?: number;
212
+ color?: string;
213
+ }
214
+ export interface MovieOpts {
215
+ filename: string;
216
+ seek_point?: number;
217
+ streams?: string;
218
+ loop?: number;
219
+ }
220
+ export interface SineOpts {
221
+ frequency?: number;
222
+ duration?: number;
223
+ }
224
+ export interface TestPatternOpts {
225
+ width?: number;
226
+ height?: number;
227
+ duration?: number;
228
+ }
229
+ /**
230
+ * A pipeline under construction. Open inputs by S3 key, chain ops on the
231
+ * returned streams, attach outputs, then read json() (graph_ir) and io()
232
+ * (the S3 ⇄ sandbox binding) and hand both to your submission layer.
233
+ */
234
+ export declare class Graph {
174
235
  /** @internal */ readonly _nodes: OpNode[];
175
236
  /** @internal */ readonly _roots: number[];
176
- /** @internal */ readonly _ioSlots: IoSlotDecl[];
237
+ /** @internal */ readonly _slots: IoSlotDecl[];
238
+ /** @internal */ readonly _inputs: InputFile[];
239
+ /** @internal */ readonly _outputs: OutputBinding[];
240
+ /** @internal */ readonly _names: Set<string>;
241
+ /** @internal — S3 key → a unique sandbox basename. The worker joins this onto
242
+ * the work dir and rejects slashes / `..`, so keys are reduced to a basename;
243
+ * collisions get a numeric prefix. */
244
+ _basename(s3Key: string): string;
177
245
  /** @internal */
178
- _addNode(node: OpNode): number;
246
+ _contentType(filename: string): string;
179
247
  /** @internal */
180
- _addRoot(id: number): void;
248
+ _addNode(node: OpNode): number;
181
249
  /** @internal */
182
250
  _addSlot(slot: IoSlotDecl): void;
183
- toJSON(): GraphJSON;
251
+ /** @internal */
252
+ _source(origin: Origin, sort: SortJSON, decoding: Decoding): number;
253
+ /**
254
+ * Open an S3 object as [video, audio]. Reference inputs by S3 key — the SDK
255
+ * records the input binding and gives the graph a sandbox-safe name, so
256
+ * `io().inputs` drops straight into a job's `input_files`.
257
+ */
258
+ open(s3Key: string, opts?: FileOpts): [VideoStream, AudioStream];
259
+ openVideo(s3Key: string): VideoStream;
260
+ openAudio(s3Key: string): AudioStream;
261
+ rtsp(url: string): [VideoStream, AudioStream];
262
+ hls(url: string): [VideoStream, AudioStream];
263
+ camera(device: number): [VideoStream, AudioStream];
264
+ screenCapture(): [VideoStream, AudioStream];
265
+ /** MQTT detection source (JSONL). */
266
+ mqtt(broker: string, topic: string): DetectionStream;
267
+ slot(label: string): [VideoStream, AudioStream];
268
+ slotVideo(label: string): VideoStream;
269
+ slotAudio(label: string): AudioStream;
270
+ aevalsrc(): AudioStream;
271
+ amovie(opts: AmovieOpts): AudioStream;
272
+ anoisesrc(): AudioStream;
273
+ anullsrc(opts?: AnullsrcOpts): AudioStream;
274
+ blank(opts?: BlankOpts): VideoStream;
275
+ movie(opts: MovieOpts): VideoStream;
276
+ sine(opts?: SineOpts): AudioStream;
277
+ testPattern(opts?: TestPatternOpts): VideoStream;
278
+ /** @internal */
279
+ _sink(inputs: number[], sorts: SortJSON[], target: SinkTarget, encoding: SinkEncoding, opts: Record<string, ParamValue>): void;
280
+ /** @internal — a file sink written to an S3 key (records the output binding). */
281
+ _fileSink(s3Key: string, inputs: number[], sorts: SortJSON[], encoding: SinkEncoding, opts: Record<string, ParamValue>): void;
282
+ /** Muxed video+audio, written to an S3 key. */
283
+ write(s3Key: string, video: VideoStream, audio: AudioStream, opts?: SinkAvOpts): this;
284
+ writeVideo(s3Key: string, video: VideoStream, opts?: SinkAvOpts): this;
285
+ writeAudio(s3Key: string, audio: AudioStream): this;
286
+ writeDetections(s3Key: string, detections: DetectionStream): this;
287
+ writeTranscript(s3Key: string, transcript: TranscriptStream, format?: 'srt' | 'vtt'): this;
288
+ writeMqtt(broker: string, topic: string, detections: DetectionStream, opts?: {
289
+ qos?: number;
290
+ }): this;
291
+ writeHttp(url: string, detections: DetectionStream, opts?: {
292
+ method?: string;
293
+ }): this;
294
+ outputSlot(label: string, video: VideoStream, audio: AudioStream, opts?: SinkAvOpts): this;
295
+ outputSlotVideo(label: string, video: VideoStream): this;
296
+ /** The graph_ir (renderbox-ir Graph serde shape) — submit this as a job's
297
+ * `graph_ir`. */
298
+ json(): GraphJSON;
299
+ /** The S3 ⇄ sandbox mapping the submission layer needs: `inputs` go into the
300
+ * job's `input_files`; pair each `output` with a destination. Identity
301
+ * (job id, tenant, user) is assigned by whoever submits — not here. */
302
+ io(): GraphIo;
184
303
  }
185
- /** @internal */
186
- export declare function _mk<S extends SortTag>(sort: S, id: number, g: GraphBuilder): Stream<S>;
187
- /** @internal */
188
- export declare function _id(s: Stream<SortTag>): number;
189
- /** @internal */
190
- export declare function _g(s: Stream<SortTag>): GraphBuilder;
191
- /** @internal */
192
- export declare function _p(opts: object): Record<string, ParamValue>;
193
- export declare function pipe<S extends SortTag>(...fns: Array<(s: Stream<S>) => Stream<S>>): (input: Stream<S>) => Stream<S>;
304
+ /** Start a new pipeline. */
305
+ export declare function graph(): Graph;
194
306
  //# sourceMappingURL=graph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
package/dist/graph.js CHANGED
@@ -1,8 +1,40 @@
1
- // --- GraphBuilder ---
2
- export class GraphBuilder {
1
+ import { mk, _p } from './streams.js';
2
+ const AV = { Product: ['Video', 'Audio'] };
3
+ /**
4
+ * A pipeline under construction. Open inputs by S3 key, chain ops on the
5
+ * returned streams, attach outputs, then read json() (graph_ir) and io()
6
+ * (the S3 ⇄ sandbox binding) and hand both to your submission layer.
7
+ */
8
+ export class Graph {
3
9
  /** @internal */ _nodes = [];
4
10
  /** @internal */ _roots = [];
5
- /** @internal */ _ioSlots = [];
11
+ /** @internal */ _slots = [];
12
+ /** @internal */ _inputs = [];
13
+ /** @internal */ _outputs = [];
14
+ /** @internal */ _names = new Set();
15
+ /** @internal — S3 key → a unique sandbox basename. The worker joins this onto
16
+ * the work dir and rejects slashes / `..`, so keys are reduced to a basename;
17
+ * collisions get a numeric prefix. */
18
+ _basename(s3Key) {
19
+ const base = (s3Key.split('/').pop() || s3Key).replace(/[/\\]/g, '_') || 'file';
20
+ let name = base;
21
+ for (let i = 1; this._names.has(name); i++)
22
+ name = `${i}_${base}`;
23
+ this._names.add(name);
24
+ return name;
25
+ }
26
+ /** @internal */
27
+ _contentType(filename) {
28
+ const ext = filename.includes('.') ? filename.split('.').pop().toLowerCase() : '';
29
+ const map = {
30
+ mp4: 'video/mp4', mov: 'video/quicktime', webm: 'video/webm', mkv: 'video/x-matroska',
31
+ mp3: 'audio/mpeg', wav: 'audio/wav', aac: 'audio/aac', m4a: 'audio/mp4', flac: 'audio/flac',
32
+ jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp', gif: 'image/gif',
33
+ srt: 'application/x-subrip', vtt: 'text/vtt',
34
+ jsonl: 'application/x-ndjson', json: 'application/json',
35
+ };
36
+ return map[ext] ?? 'application/octet-stream';
37
+ }
6
38
  /** @internal */
7
39
  _addNode(node) {
8
40
  const id = this._nodes.length;
@@ -10,53 +42,159 @@ export class GraphBuilder {
10
42
  return id;
11
43
  }
12
44
  /** @internal */
13
- _addRoot(id) {
14
- this._roots.push(id);
15
- }
16
- /** @internal */
17
45
  _addSlot(slot) {
18
- const dup = this._ioSlots.some((s) => s.label === slot.label && s.direction === slot.direction);
46
+ const dup = this._slots.some((s) => s.label === slot.label && s.direction === slot.direction);
19
47
  if (dup) {
20
- // Duplicate input labels share one binding (bind() rewrites every
21
- // matching source); duplicate output labels are ambiguous.
22
48
  if (slot.direction === 'Output') {
23
49
  throw new Error(`duplicate output slot label: ${slot.label}`);
24
50
  }
25
- return;
51
+ return; // duplicate input labels share one binding
26
52
  }
27
- this._ioSlots.push(slot);
53
+ this._slots.push(slot);
54
+ }
55
+ /** @internal */
56
+ _source(origin, sort, decoding) {
57
+ return this._addNode({ kind: 'Source', origin, sort, decoding, opts: {} });
58
+ }
59
+ // --- Inputs ---------------------------------------------------------------
60
+ /**
61
+ * Open an S3 object as [video, audio]. Reference inputs by S3 key — the SDK
62
+ * records the input binding and gives the graph a sandbox-safe name, so
63
+ * `io().inputs` drops straight into a job's `input_files`.
64
+ */
65
+ open(s3Key, opts) {
66
+ const filename = this._basename(s3Key);
67
+ this._inputs.push({ s3_key: s3Key, filename, content_type: this._contentType(filename) });
68
+ const id = this._addNode({
69
+ kind: 'Source',
70
+ origin: { File: { path: filename } },
71
+ sort: AV,
72
+ decoding: 'Passthrough',
73
+ opts: opts ? _p(opts) : {},
74
+ });
75
+ return [mk('Video', id, this), mk('Audio', id, this)];
76
+ }
77
+ openVideo(s3Key) {
78
+ return this.open(s3Key)[0];
28
79
  }
29
- toJSON() {
80
+ openAudio(s3Key) {
81
+ return this.open(s3Key)[1];
82
+ }
83
+ rtsp(url) {
84
+ const id = this._source({ Rtsp: { url, transport: null } }, AV, 'Passthrough');
85
+ return [mk('Video', id, this), mk('Audio', id, this)];
86
+ }
87
+ hls(url) {
88
+ const id = this._source({ Hls: { url } }, AV, 'Passthrough');
89
+ return [mk('Video', id, this), mk('Audio', id, this)];
90
+ }
91
+ camera(device) {
92
+ const id = this._source({ Camera: { device: String(device) } }, AV, 'Passthrough');
93
+ return [mk('Video', id, this), mk('Audio', id, this)];
94
+ }
95
+ screenCapture() {
96
+ const id = this._source({ ScreenCapture: { display: null } }, AV, 'Passthrough');
97
+ return [mk('Video', id, this), mk('Audio', id, this)];
98
+ }
99
+ /** MQTT detection source (JSONL). */
100
+ mqtt(broker, topic) {
101
+ const id = this._source({ Mqtt: { broker, topic, qos: null } }, 'Detection', 'JsonlDetection');
102
+ return mk('Detection', id, this);
103
+ }
104
+ slot(label) {
105
+ const id = this._source({ Slot: { label } }, AV, 'Passthrough');
106
+ this._addSlot({ label, node_ids: [[id, 'Video'], [id, 'Audio']], direction: 'Input', optional: false });
107
+ return [mk('Video', id, this), mk('Audio', id, this)];
108
+ }
109
+ slotVideo(label) {
110
+ const id = this._source({ Slot: { label } }, 'Video', 'Passthrough');
111
+ this._addSlot({ label, node_ids: [[id, 'Video']], direction: 'Input', optional: false });
112
+ return mk('Video', id, this);
113
+ }
114
+ slotAudio(label) {
115
+ const id = this._source({ Slot: { label } }, 'Audio', 'Passthrough');
116
+ this._addSlot({ label, node_ids: [[id, 'Audio']], direction: 'Input', optional: false });
117
+ return mk('Audio', id, this);
118
+ }
119
+ aevalsrc() { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'aevalsrc', params: {} } }, sort: 'Audio', decoding: 'Passthrough', opts: {} }); return mk('Audio', id, this); }
120
+ amovie(opts) { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'amovie', params: _p(opts) } }, sort: 'Audio', decoding: 'Passthrough', opts: {} }); return mk('Audio', id, this); }
121
+ anoisesrc() { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'anoisesrc', params: {} } }, sort: 'Audio', decoding: 'Passthrough', opts: {} }); return mk('Audio', id, this); }
122
+ anullsrc(opts) { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'anullsrc', params: opts ? _p(opts) : {} } }, sort: 'Audio', decoding: 'Passthrough', opts: {} }); return mk('Audio', id, this); }
123
+ blank(opts) { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'blank', params: opts ? _p(opts) : {} } }, sort: 'Video', decoding: 'Passthrough', opts: {} }); return mk('Video', id, this); }
124
+ movie(opts) { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'movie', params: _p(opts) } }, sort: 'Video', decoding: 'Passthrough', opts: {} }); return mk('Video', id, this); }
125
+ sine(opts) { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'sine', params: opts ? _p(opts) : {} } }, sort: 'Audio', decoding: 'Passthrough', opts: {} }); return mk('Audio', id, this); }
126
+ testPattern(opts) { const id = this._addNode({ kind: 'Source', origin: { Synthetic: { name: 'test_pattern', params: opts ? _p(opts) : {} } }, sort: 'Video', decoding: 'Passthrough', opts: {} }); return mk('Video', id, this); }
127
+ // --- Outputs --------------------------------------------------------------
128
+ /** @internal */
129
+ _sink(inputs, sorts, target, encoding, opts) {
130
+ const id = this._addNode({ kind: 'Sink', inputs, input_sorts: sorts, target, encoding, opts });
131
+ this._roots.push(id);
132
+ }
133
+ /** @internal — a file sink written to an S3 key (records the output binding). */
134
+ _fileSink(s3Key, inputs, sorts, encoding, opts) {
135
+ const filename = this._basename(s3Key);
136
+ this._outputs.push({ s3_key: s3Key, filename });
137
+ this._sink(inputs, sorts, { File: { path: filename } }, encoding, opts);
138
+ }
139
+ /** Muxed video+audio, written to an S3 key. */
140
+ write(s3Key, video, audio, opts) {
141
+ this._fileSink(s3Key, [video._id, audio._id], ['Video', 'Audio'], 'Container', opts ? _p(opts) : {});
142
+ return this;
143
+ }
144
+ writeVideo(s3Key, video, opts) {
145
+ this._fileSink(s3Key, [video._id], ['Video'], 'Container', opts ? _p(opts) : {});
146
+ return this;
147
+ }
148
+ writeAudio(s3Key, audio) {
149
+ this._fileSink(s3Key, [audio._id], ['Audio'], 'Container', {});
150
+ return this;
151
+ }
152
+ writeDetections(s3Key, detections) {
153
+ this._fileSink(s3Key, [detections._id], ['Detection'], 'Jsonl', {});
154
+ return this;
155
+ }
156
+ writeTranscript(s3Key, transcript, format = 'srt') {
157
+ this._fileSink(s3Key, [transcript._id], ['Transcript'], format === 'vtt' ? 'Vtt' : 'Srt', {});
158
+ return this;
159
+ }
160
+ writeMqtt(broker, topic, detections, opts) {
161
+ this._sink([detections._id], ['Detection'], { Mqtt: { broker, topic, qos: opts?.qos ?? null } }, 'Jsonl', {});
162
+ return this;
163
+ }
164
+ writeHttp(url, detections, opts) {
165
+ this._sink([detections._id], ['Detection'], { Http: { url, method: opts?.method ?? null } }, 'Jsonl', {});
166
+ return this;
167
+ }
168
+ outputSlot(label, video, audio, opts) {
169
+ const id = this._addNode({ kind: 'Sink', inputs: [video._id, audio._id], input_sorts: ['Video', 'Audio'], target: { Slot: { label } }, encoding: 'Container', opts: opts ? _p(opts) : {} });
170
+ this._roots.push(id);
171
+ this._addSlot({ label, node_ids: [[id, 'Video'], [id, 'Audio']], direction: 'Output', optional: false });
172
+ return this;
173
+ }
174
+ outputSlotVideo(label, video) {
175
+ const id = this._addNode({ kind: 'Sink', inputs: [video._id], input_sorts: ['Video'], target: { Slot: { label } }, encoding: 'Container', opts: {} });
176
+ this._roots.push(id);
177
+ this._addSlot({ label, node_ids: [[id, 'Video']], direction: 'Output', optional: false });
178
+ return this;
179
+ }
180
+ // --- Terminals ------------------------------------------------------------
181
+ /** The graph_ir (renderbox-ir Graph serde shape) — submit this as a job's
182
+ * `graph_ir`. */
183
+ json() {
30
184
  const g = { arena: this._nodes, roots: this._roots };
31
- if (this._ioSlots.length > 0)
32
- g.io_manifest = { slots: this._ioSlots };
185
+ if (this._slots.length > 0)
186
+ g.io_manifest = { slots: this._slots };
33
187
  return g;
34
188
  }
189
+ /** The S3 ⇄ sandbox mapping the submission layer needs: `inputs` go into the
190
+ * job's `input_files`; pair each `output` with a destination. Identity
191
+ * (job id, tenant, user) is assigned by whoever submits — not here. */
192
+ io() {
193
+ return { inputs: [...this._inputs], outputs: [...this._outputs] };
194
+ }
35
195
  }
36
- // --- Internal helpers (used by generated op functions) ---
37
- /** @internal */
38
- export function _mk(sort, id, g) {
39
- return { _id: id, _graph: g, _sort: sort };
40
- }
41
- /** @internal */
42
- export function _id(s) {
43
- return s._id;
44
- }
45
- /** @internal */
46
- export function _g(s) {
47
- return s._graph;
48
- }
49
- /** @internal */
50
- export function _p(opts) {
51
- const out = {};
52
- for (const [k, v] of Object.entries(opts)) {
53
- if (v !== undefined)
54
- out[k] = v;
55
- }
56
- return out;
57
- }
58
- // --- Composition ---
59
- export function pipe(...fns) {
60
- return (input) => fns.reduce((s, fn) => fn(s), input);
196
+ /** Start a new pipeline. */
197
+ export function graph() {
198
+ return new Graph();
61
199
  }
62
200
  //# sourceMappingURL=graph.js.map