@remavideo/sdk 0.18.2 → 0.19.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.d.ts +32 -0
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -108,6 +108,8 @@ declare const CreateNodeInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
108
108
|
nodeId: z.ZodString;
|
|
109
109
|
rtmpAnnouncer: z.ZodObject<{
|
|
110
110
|
app: z.ZodDefault<z.ZodString>;
|
|
111
|
+
forceReconnectAttempts: z.ZodOptional<z.ZodNumber>;
|
|
112
|
+
forceReconnectDelayMs: z.ZodOptional<z.ZodNumber>;
|
|
111
113
|
}, z.core.$strip>;
|
|
112
114
|
}, z.core.$strip>, z.ZodObject<{
|
|
113
115
|
workflowId: z.ZodString;
|
|
@@ -117,6 +119,8 @@ declare const CreateNodeInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
117
119
|
app: z.ZodDefault<z.ZodString>;
|
|
118
120
|
passphrase: z.ZodOptional<z.ZodString>;
|
|
119
121
|
latencyMs: z.ZodOptional<z.ZodNumber>;
|
|
122
|
+
forceReconnectAttempts: z.ZodOptional<z.ZodNumber>;
|
|
123
|
+
forceReconnectDelayMs: z.ZodOptional<z.ZodNumber>;
|
|
120
124
|
}, z.core.$strip>;
|
|
121
125
|
}, z.core.$strip>, z.ZodObject<{
|
|
122
126
|
workflowId: z.ZodString;
|
|
@@ -624,6 +628,8 @@ declare const appRouter: _trpc_server.TRPCBuiltRouter<{
|
|
|
624
628
|
nodeId: string;
|
|
625
629
|
rtmpAnnouncer: {
|
|
626
630
|
app?: string | undefined;
|
|
631
|
+
forceReconnectAttempts?: number | undefined;
|
|
632
|
+
forceReconnectDelayMs?: number | undefined;
|
|
627
633
|
};
|
|
628
634
|
} | {
|
|
629
635
|
workflowId: string;
|
|
@@ -633,6 +639,8 @@ declare const appRouter: _trpc_server.TRPCBuiltRouter<{
|
|
|
633
639
|
app?: string | undefined;
|
|
634
640
|
passphrase?: string | undefined;
|
|
635
641
|
latencyMs?: number | undefined;
|
|
642
|
+
forceReconnectAttempts?: number | undefined;
|
|
643
|
+
forceReconnectDelayMs?: number | undefined;
|
|
636
644
|
};
|
|
637
645
|
} | {
|
|
638
646
|
workflowId: string;
|
|
@@ -1546,6 +1554,17 @@ interface RtmpAnnouncerOperations {
|
|
|
1546
1554
|
interface RtmpAnnouncerSettings extends NodeCallbacks {
|
|
1547
1555
|
/** RTMP app name to watch (default: "live"). */
|
|
1548
1556
|
app?: string;
|
|
1557
|
+
/**
|
|
1558
|
+
* Silently kick the first N connections for each `sourceName` — never
|
|
1559
|
+
* calling `onConnect`/`onDisconnect` for them — before letting the
|
|
1560
|
+
* (N+1)th through. Useful for publishers whose stream is incomplete for
|
|
1561
|
+
* the first few seconds of a connection. Resets once a connection is let
|
|
1562
|
+
* through, so the next new session for this `sourceName` gets the same
|
|
1563
|
+
* warm-up. Default: 0 (disabled).
|
|
1564
|
+
*/
|
|
1565
|
+
forceReconnectAttempts?: number;
|
|
1566
|
+
/** Delay before kicking each of the `forceReconnectAttempts` connections. Default: 5000. */
|
|
1567
|
+
forceReconnectDelayMs?: number;
|
|
1549
1568
|
/**
|
|
1550
1569
|
* Called when a publisher connects. Create nodes in any workflow from
|
|
1551
1570
|
* here, or call `operations.disconnectSource(info.connectionId)` to kick
|
|
@@ -1678,6 +1697,19 @@ interface SrtAnnouncerSettings extends NodeCallbacks {
|
|
|
1678
1697
|
passphrase?: string;
|
|
1679
1698
|
/** SRT latency in milliseconds to include in connection events. */
|
|
1680
1699
|
latencyMs?: number;
|
|
1700
|
+
/**
|
|
1701
|
+
* Silently kick the first N connections for each `sourceName` — never
|
|
1702
|
+
* calling `onConnect`/`onDisconnect` for them — before letting the
|
|
1703
|
+
* (N+1)th through. Useful for publishers whose stream is incomplete for
|
|
1704
|
+
* the first few seconds of a connection (e.g. some CDN restream outputs
|
|
1705
|
+
* emit an MPEG-TS PMT that doesn't declare every elementary stream until
|
|
1706
|
+
* slightly after the connection starts). Resets once a connection is let
|
|
1707
|
+
* through, so the next new session for this `sourceName` gets the same
|
|
1708
|
+
* warm-up. Default: 0 (disabled).
|
|
1709
|
+
*/
|
|
1710
|
+
forceReconnectAttempts?: number;
|
|
1711
|
+
/** Delay before kicking each of the `forceReconnectAttempts` connections. Default: 5000. */
|
|
1712
|
+
forceReconnectDelayMs?: number;
|
|
1681
1713
|
/**
|
|
1682
1714
|
* Called when a publisher connects. Create nodes in any workflow from
|
|
1683
1715
|
* here, or call `operations.disconnectSource(info.connectionId)` to kick
|
package/dist/index.js
CHANGED
|
@@ -408,7 +408,15 @@ async function createRtmpAnnouncerNode(workflow, settings, nodeId) {
|
|
|
408
408
|
kind: "RTMP_ANNOUNCER",
|
|
409
409
|
workflowId: workflow.workflowId,
|
|
410
410
|
nodeId: id,
|
|
411
|
-
rtmpAnnouncer: {
|
|
411
|
+
rtmpAnnouncer: {
|
|
412
|
+
app,
|
|
413
|
+
...settings.forceReconnectAttempts !== void 0 && {
|
|
414
|
+
forceReconnectAttempts: settings.forceReconnectAttempts
|
|
415
|
+
},
|
|
416
|
+
...settings.forceReconnectDelayMs !== void 0 && {
|
|
417
|
+
forceReconnectDelayMs: settings.forceReconnectDelayMs
|
|
418
|
+
}
|
|
419
|
+
}
|
|
412
420
|
}).catch((err) => {
|
|
413
421
|
removeHandler();
|
|
414
422
|
throw err;
|
|
@@ -587,6 +595,12 @@ async function createSrtAnnouncerNode(workflow, settings, nodeId) {
|
|
|
587
595
|
},
|
|
588
596
|
...settings.latencyMs !== void 0 && {
|
|
589
597
|
latencyMs: settings.latencyMs
|
|
598
|
+
},
|
|
599
|
+
...settings.forceReconnectAttempts !== void 0 && {
|
|
600
|
+
forceReconnectAttempts: settings.forceReconnectAttempts
|
|
601
|
+
},
|
|
602
|
+
...settings.forceReconnectDelayMs !== void 0 && {
|
|
603
|
+
forceReconnectDelayMs: settings.forceReconnectDelayMs
|
|
590
604
|
}
|
|
591
605
|
}
|
|
592
606
|
}).catch((err) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/input/caption-source.ts","../src/node.ts","../src/input/file.ts","../src/input/node-stream.ts","../src/input/rtmp-announcer.ts","../src/input/rtmp-multiplexer.ts","../src/input/rtmp-reader.ts","../src/input/srt-announcer.ts","../src/input/srt-reader.ts","../src/output/file.ts","../src/output/hls.ts","../src/output/hls-renditions.ts","../src/output/rtmp.ts","../src/output/srt.ts","../src/processor/azure-translate.ts","../src/processor/buffer.ts","../src/processor/camb-ai-translate.ts","../src/processor/cea608-encoder.ts","../src/processor/deepgram-stt.ts","../src/processor/ffprobe-monitor.ts","../src/processor/gate.ts","../src/processor/gemini-translate.ts","../src/processor/openai-translate.ts","../src/client.ts","../src/workflow.ts","../src/processor/audio-mixer.ts","../src/rema.ts","../src/selectors.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface CaptionSourceSettings extends NodeCallbacks {\n /**\n * How many past caption entries to replay to an encoder that subscribes\n * after some captions have already been sent.\n *\n * - `0` (default) — no replay; each encoder only receives captions sent\n * after it connects. Correct for live captioning where old text is stale.\n * - `Infinity` — replay the full history. Use this when pre-scheduling\n * all captions upfront (e.g. from an SRT file) before the pipeline starts,\n * so encoders that activate later still see every entry.\n * - `N` — replay the last N entries only.\n */\n replayCount?: number;\n /**\n * BCP-47 language tag written into the HLS master playlist when this caption\n * source is wired as a subtitle track. Default: \"und\".\n */\n language?: string;\n /** Human-readable track label shown in player subtitle menus. Default: \"Subtitles\". */\n label?: string;\n /** Whether this track is pre-selected when the player first loads. Default: true. */\n defaultTrack?: boolean;\n /**\n * Maximum character length per caption line. When set, captions whose text\n * exceeds this length are automatically split into sequential chunks on the\n * server. Each chunk's display duration is proportional to its character\n * count relative to the original caption's total duration.\n */\n maxLineLength?: number;\n}\n\nexport class CaptionSourceNode extends BaseNode {\n readonly settings: CaptionSourceSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: CaptionSourceSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /**\n * Queue caption text for injection into the video stream.\n *\n * @param options.startAt Stream-elapsed milliseconds (relative to the\n * first video frame received by the server) at which\n * to show the caption. The server holds the text and\n * injects it at the matching PTS, so you can schedule\n * captions before that point in the stream is reached.\n * Omit (or 0) to inject immediately at the next frame.\n * @param options.duration How long (ms) the caption stays visible before\n * being automatically erased. Required.\n */\n async send(\n text: string,\n options: { duration: number; startAt?: number },\n ): Promise<void> {\n await this.client.trpc.sendCaption.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n text,\n durationMs: options.duration,\n startAtMs: options.startAt ?? 0,\n });\n }\n\n /**\n * Parse raw SRT or WebVTT content and schedule all entries on the server.\n *\n * Use `replayCount: Infinity` when creating this node if the pipeline may\n * connect encoders after the captions are loaded.\n *\n * @returns The number of caption entries loaded.\n */\n async sendFile(content: string): Promise<{ count: number }> {\n return this.client.trpc.loadCaptionFile.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n content,\n });\n }\n\n /**\n * Read a local SRT or WebVTT file and schedule all its entries on the server.\n * The file is read client-side; only the content string is sent over the wire.\n *\n * @param filePath Absolute or relative path to the subtitle file.\n * @returns The number of caption entries loaded.\n */\n async sendFileByPath(filePath: string): Promise<{ count: number }> {\n const content = await readFile(filePath, \"utf-8\");\n return this.sendFile(content);\n }\n}\n\nexport async function createCaptionSourceNode(\n workflow: WorkflowHandle,\n settings: CaptionSourceSettings = {},\n nodeId?: string,\n): Promise<CaptionSourceNode> {\n const id = nodeId ?? `caption_source_${Date.now()}`;\n const node = new CaptionSourceNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"CAPTION_SOURCE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n captionSource: {\n replayCount: settings.replayCount ?? 0,\n language: settings.language ?? \"und\",\n label: settings.label ?? \"Subtitles\",\n defaultTrack: settings.defaultTrack ?? true,\n ...(settings.maxLineLength !== undefined && {\n maxLineLength: settings.maxLineLength,\n }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type {\n ActivationGate,\n NodeEventMsg,\n NodeState,\n TrackInfo,\n} from \"@remavideo/server\";\nimport { TypedEmitter } from \"tiny-typed-emitter\";\nimport type { RemaClient } from \"./client.js\";\n\n/** Lifecycle callbacks available on every node's settings object. */\nexport interface NodeCallbacks {\n /** Called when the node transitions into the `ERROR` state. */\n onError?: (err: Error) => void;\n /** Called on every lifecycle state transition (`READY`, `ACTIVE`, `ENDED`, `ERROR`, `CLOSED`). */\n onStatusChange?: (state: NodeState, message?: string) => void;\n /** Called when the node closes — locally via `.close()`, or remotely (workflow close, crash). */\n onClose?: () => void;\n}\n\n/** Events emitted by every node, mirroring {@link NodeCallbacks} for `.on()`-style consumers. */\nexport interface NodeEvents {\n error: (err: Error) => void;\n statusChange: (state: NodeState, message?: string) => void;\n close: () => void;\n}\n\nconst NODE_STATES = new Set<NodeState>([\n \"READY\",\n \"ACTIVE\",\n \"ENDED\",\n \"ERROR\",\n \"CLOSED\",\n]);\n\n/**\n * Report an error to a node's `onError` callback and its `NodeEvents`\n * emitter. The SDK does no logging of its own — the caller decides whether\n * and how an unhandled error surfaces, via `onError` or `.on(\"error\", ...)`.\n *\n * `BaseNode` extends Node's `EventEmitter` (via `tiny-typed-emitter`), which\n * throws synchronously from `emit()` when an `\"error\"` event has no\n * listener. Since callers typically only set the `onError` callback (not\n * `.on(\"error\", ...)`), emitting unconditionally would throw back into\n * whatever triggered this call — e.g. the `workflow.events` subscription's\n * `onData` handler, tearing down event delivery for the entire workflow.\n * Only emit when someone has opted in.\n */\nexport function reportNodeError(\n node: BaseNode,\n callbacks: NodeCallbacks,\n err: Error,\n): void {\n callbacks.onError?.(err);\n if (node.listenerCount(\"error\") > 0) node.emit(\"error\", err);\n}\n\n/**\n * Dispatch a raw node event to the generic lifecycle callbacks on a node's\n * settings object, and to the node's `NodeEvents` emitter. Call this from a\n * node factory's event handler alongside any node-type-specific event handling.\n */\nexport function dispatchNodeCallbacks(\n node: BaseNode,\n evt: NodeEventMsg,\n callbacks: NodeCallbacks,\n): void {\n if (NODE_STATES.has(evt.kind as NodeState)) {\n const state = evt.kind as NodeState;\n callbacks.onStatusChange?.(state, evt.message);\n node.emit(\"statusChange\", state, evt.message);\n }\n if (evt.kind === \"ERROR\") {\n reportNodeError(node, callbacks, new Error(evt.message ?? \"node error\"));\n }\n if (evt.kind === \"CLOSED\") {\n callbacks.onClose?.();\n node.emit(\"close\");\n }\n}\n\n/**\n * FFmpeg codec specification for one stream of a subscription.\n *\n * The receiving output node applies it where appropriate (e.g. per-rendition\n * `-c:v:N`/`-c:a:N` for HLS, `-c:v`/`-c:a` for RTMP/File outputs). Omitted =\n * stream copy.\n */\nexport interface CodecSpec {\n /** FFmpeg encoder name: `\"copy\"`, `\"aac\"`, `\"libx264\"`, … */\n name: string;\n /**\n * Extra encoder options keyed by bare FFmpeg option name (no stream\n * specifier), e.g. `{ preset: \"veryfast\", b: \"3M\" }`.\n */\n options?: Record<string, string | number>;\n}\n\n/**\n * Audio rendition metadata for HLS output nodes. Sets the language tag and\n * label that appear in the player's track menu. Ignored by non-HLS nodes.\n */\nexport interface AudioRenditionOptions {\n /** BCP-47 language tag, e.g. `\"en\"`, `\"fr\"`. Default: `\"und\"`. */\n language?: string;\n /** Label shown in player track menus. Default: `\"Audio\"`. */\n label?: string;\n /** Pre-select this track on first play. Default: `false`. */\n defaultTrack?: boolean;\n}\n\n/**\n * Video rendition metadata for HLS output nodes. Ignored by non-HLS nodes.\n */\nexport interface VideoRenditionOptions {\n /**\n * Label used to name the HLS variant playlist/segments (sanitized to a\n * URL-safe variant name).\n */\n label?: string;\n}\n\n/**\n * One rung of an HLS adaptive-bitrate (ABR) ladder — an independent\n * resolution/bitrate tier transcoded from the same video source, wired via\n * `VideoSubscriptionSpec.additionalRenditions` / `AllSubscriptionSpec.additionalRenditions`.\n * HLS-only; ignored by non-HLS output nodes.\n */\nexport interface VideoRendition {\n /**\n * Output height in pixels. Also derives the default label (`\"{height}p\"`)\n * and, when `width` is omitted, an aspect-preserving `scale=-2:height`\n * filter.\n */\n height: number;\n /** Output width in pixels. Omit to preserve the source aspect ratio. */\n width?: number;\n /** Target video bitrate — ffmpeg bitrate syntax (`\"3M\"`) or bits/sec. */\n bitrate: string | number;\n /** VBV maxrate. Default: same as `bitrate`. */\n maxrate?: string | number;\n /** VBV bufsize. Default: 2x `bitrate`. */\n bufsize?: string | number;\n /** libx264 preset. Default: `\"veryfast\"`. */\n preset?: string;\n /**\n * Encoder name override — escape hatch for hardware encoders (e.g.\n * `\"h264_videotoolbox\"`, `\"h264_nvenc\"`). Default: `\"libx264\"`. No\n * server-side hardware-encoder plumbing exists; passed through as-is.\n */\n codecName?: string;\n /**\n * Variant label. Default: `\"{height}p\"`. Must be unique across the\n * ladder — colliding labels are auto-suffixed server-side.\n */\n label?: string;\n /**\n * Extra raw codec options, merged in last — overrides any default above\n * (including the keyframe-alignment `force_key_frames` default).\n */\n extraOptions?: Record<string, string | number>;\n}\n\n/** Fields shared by every subscription variant. */\ninterface SubscriptionSpecBase {\n source: BaseNode;\n /**\n * Delay this stream by N milliseconds before it reaches the subscribing\n * node (applied server-side). Typical use: delay the A/V subscriptions so\n * a slower caption source has lead time.\n */\n delayMs?: number;\n /**\n * Read from a keyed output port of a multi-output source node (Gate).\n * Ports are named by the `name` field of the specs the gate itself was\n * subscribed with. Mutually exclusive with `track`.\n */\n port?: string;\n /**\n * When the *subscribing* node is a multi-output node (Gate), names the\n * pass-through port it creates for this source (default: the source's\n * index as a string). Ignored by other node types.\n */\n name?: string;\n}\n\n/** Subscribe to a video-only stream of a source node. */\nexport interface VideoSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"VIDEO\";\n /**\n * Per-type video track index within the source container (0 = first video\n * track). Discover available indices with `await source.tracks()`.\n * Default: 0.\n */\n track?: number;\n /** Codec applied to the video stream by the receiving output node. */\n codec?: CodecSpec;\n}\n\n/** Subscribe to an audio-only stream of a source node. */\nexport interface AudioSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"AUDIO\";\n /**\n * Per-type audio track index within the source container (0 = first audio\n * track). Discover available indices with `await source.tracks()`.\n * Default: 0.\n */\n track?: number;\n /** Codec applied to the audio stream by the receiving output node. */\n codec?: CodecSpec;\n}\n\n/** Subscribe to the combined A/V stream of a source node. */\nexport interface AllSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"ALL\";\n /** Codec applied to the video track by the receiving output node. */\n videoCodec?: CodecSpec;\n /** Codec applied to the audio track by the receiving output node. */\n audioCodec?: CodecSpec;\n}\n\n/** Subscribe to the caption stream of a source node. */\nexport interface CaptionsSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"CAPTIONS\";\n}\n\n/**\n * One source wired into a subscribing node, discriminated by\n * `sourceSelector` so per-stream options (codec) are only available where\n * they apply. Rendition-metadata fields (`video`/`audio`/\n * `additionalRenditions`/`omitOriginalRendition`) are HLS-only and live on\n * {@link HlsSubscriptionSpec} instead, since only `HlsOutputNode.subscribe()`\n * consumes them.\n */\nexport type SubscriptionSpec =\n | VideoSubscriptionSpec\n | AudioSubscriptionSpec\n | AllSubscriptionSpec\n | CaptionsSubscriptionSpec;\n\n/** Subscribe to a video-only stream of a source node, for an HLS output. */\nexport interface HlsVideoSubscriptionSpec extends VideoSubscriptionSpec {\n /** Video rendition metadata (HLS). */\n video?: VideoRenditionOptions;\n /**\n * Additional ABR ladder tiers transcoded from this same source, each\n * becoming its own HLS variant alongside this subscription's own\n * rendition.\n */\n additionalRenditions?: VideoRendition[];\n /**\n * When true, this subscription's own rendition (this `codec`/`video`) is\n * NOT subscribed as a variant — only `additionalRenditions` are.\n */\n omitOriginalRendition?: boolean;\n}\n\n/** Subscribe to an audio-only stream of a source node, for an HLS output. */\nexport interface HlsAudioSubscriptionSpec extends AudioSubscriptionSpec {\n /** Audio rendition metadata (HLS). */\n audio?: AudioRenditionOptions;\n}\n\n/** Subscribe to the combined A/V stream of a source node, for an HLS output. */\nexport interface HlsAllSubscriptionSpec extends AllSubscriptionSpec {\n /** Video rendition metadata (HLS). */\n video?: VideoRenditionOptions;\n /** Audio rendition metadata (HLS). */\n audio?: AudioRenditionOptions;\n /**\n * Additional ABR ladder tiers transcoded from this same source, each\n * becoming its own video-only HLS variant alongside this subscription's\n * own combined A/V rendition.\n *\n * `omitOriginalRendition: true` on an `ALL` spec also drops this\n * subscription's own audio (since `ALL` bundles audio+video) — subscribe\n * audio separately (`sourceSelector: \"AUDIO\"`) if you still need it.\n */\n additionalRenditions?: VideoRendition[];\n /**\n * When true, this subscription's own combined rendition is NOT\n * subscribed — only `additionalRenditions` are. See the audio caveat\n * above.\n */\n omitOriginalRendition?: boolean;\n}\n\n/**\n * One source wired into an {@link HlsOutputNode}, adding rendition-metadata\n * fields (`video`/`audio`/`additionalRenditions`/`omitOriginalRendition`) on\n * top of the common {@link SubscriptionSpec} — these only type-check here\n * since only `HlsOutputNode.subscribe()` consumes them; every other node's\n * `subscribe()` uses the plain `SubscriptionSpec` and rejects them at\n * compile time.\n */\nexport type HlsSubscriptionSpec =\n | HlsVideoSubscriptionSpec\n | HlsAudioSubscriptionSpec\n | HlsAllSubscriptionSpec\n | CaptionsSubscriptionSpec;\n\n/**\n * Minimal interface passed to factory functions. Defined here (not in workflow.ts)\n * to avoid a circular import: workflow.ts imports factory files, factory files\n * import this interface.\n */\nexport interface WorkflowHandle {\n readonly workflowId: string;\n readonly client: RemaClient;\n _onNodeEvent(\n nodeId: string,\n handler: (evt: NodeEventMsg) => void,\n ): () => void;\n}\n\n/**\n * Base class for all rema nodes.\n * Provides the .subscribe() method that wires nodes together on the server,\n * and a `NodeEvents` emitter (`.on(\"error\"/\"statusChange\"/\"close\", ...)`) as\n * an alternative to the `onError`/`onStatusChange`/`onClose` settings callbacks.\n */\nexport abstract class BaseNode extends TypedEmitter<NodeEvents> {\n readonly nodeId: string;\n readonly workflowId: string;\n protected readonly client: RemaClient;\n private _eventCleanup: (() => void) | null = null;\n private _callbacks: NodeCallbacks = {};\n\n constructor(nodeId: string, client: RemaClient, workflowId: string) {\n super();\n this.nodeId = nodeId;\n this.workflowId = workflowId;\n this.client = client;\n }\n\n /** Called by create functions to register the workflow event handler cleanup. */\n _setEventCleanup(fn: () => void): void {\n this._eventCleanup = fn;\n }\n\n /**\n * Called by create functions to register the node's lifecycle callbacks\n * (`onError`/`onStatusChange`/`onClose`), so `.close()` can invoke `onClose`\n * directly on a locally-initiated close.\n */\n _bindCallbacks(callbacks: NodeCallbacks): void {\n this._callbacks = callbacks;\n }\n\n /**\n * Subscribe this node to one or more source nodes.\n * This tells the server to route streams from the sources into this node.\n */\n async subscribe(\n specs: SubscriptionSpec[],\n options?: { gate?: ActivationGate },\n ): Promise<void> {\n await this.client.trpc.subscribeNode.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n sources: specs.map((s) => BaseNode._toSourceInput(s)),\n gate: options?.gate,\n });\n }\n\n /**\n * Map one source spec to the wire shape expected by the server. Typed to\n * accept {@link HlsSubscriptionSpec} (a superset of {@link SubscriptionSpec})\n * since it serves both `BaseNode.subscribe` and `HlsOutputNode.subscribe`.\n */\n private static _toSourceInput(spec: HlsSubscriptionSpec) {\n const base = {\n sourceNodeId: spec.source.nodeId,\n ...(spec.delayMs !== undefined ? { delayMs: spec.delayMs } : {}),\n ...(spec.port !== undefined ? { port: spec.port } : {}),\n ...(spec.name !== undefined ? { name: spec.name } : {}),\n };\n const audio = (a: AudioRenditionOptions) => ({\n language: a.language ?? \"und\",\n label: a.label ?? \"Audio\",\n defaultTrack: a.defaultTrack ?? false,\n });\n // `video` is only sent when a label is provided — it is the only field\n // and is required on the wire.\n const video = (v: VideoRenditionOptions) =>\n v.label !== undefined ? { video: { label: v.label } } : {};\n\n switch (spec.sourceSelector) {\n case \"CAPTIONS\":\n return { ...base, selector: \"CAPTIONS\" as const };\n case \"VIDEO\":\n return {\n ...base,\n selector: \"VIDEO\" as const,\n ...(spec.track !== undefined ? { track: spec.track } : {}),\n ...(spec.codec !== undefined ? { codec: spec.codec } : {}),\n ...(spec.video !== undefined ? video(spec.video) : {}),\n };\n case \"AUDIO\":\n return {\n ...base,\n selector: \"AUDIO\" as const,\n ...(spec.track !== undefined ? { track: spec.track } : {}),\n ...(spec.codec !== undefined ? { codec: spec.codec } : {}),\n ...(spec.audio !== undefined ? { audio: audio(spec.audio) } : {}),\n };\n case \"ALL\":\n return {\n ...base,\n selector: \"ALL\" as const,\n ...(spec.videoCodec !== undefined\n ? { videoCodec: spec.videoCodec }\n : {}),\n ...(spec.audioCodec !== undefined\n ? { audioCodec: spec.audioCodec }\n : {}),\n ...(spec.video !== undefined ? video(spec.video) : {}),\n ...(spec.audio !== undefined ? { audio: audio(spec.audio) } : {}),\n };\n }\n }\n\n /**\n * Probe this node's media stream (ffprobe, server-side) and return its\n * track list. Each entry's `track` is the per-type index to pass back as\n * `track` in a VIDEO/AUDIO subscription:\n *\n * ```ts\n * const tracks = await input.tracks();\n * const ita = tracks.find((t) => t.type === \"audio\" && t.language === \"ita\");\n * await output.subscribe([\n * { source: input, sourceSelector: \"VIDEO\" },\n * { source: input, sourceSelector: \"AUDIO\", track: ita!.track },\n * ]);\n * ```\n *\n * Probing consumes real-time-paced stream data, so it can take several\n * seconds; `probeSizeBytes` (default 1 MB) trades discovery depth for\n * latency. The server errors after ~15 s if the source produces no data\n * (e.g. no publisher connected yet, or a disabled file input).\n */\n async tracks(options?: { probeSizeBytes?: number }): Promise<TrackInfo[]> {\n return this.client.trpc.probeTracks.query({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n ...(options?.probeSizeBytes !== undefined\n ? { probeSizeBytes: options.probeSizeBytes }\n : {}),\n });\n }\n\n /** Tear down this node on the server. */\n async close(): Promise<void> {\n this._eventCleanup?.();\n await this.client.trpc.closeNode.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n });\n this._callbacks.onClose?.();\n this.emit(\"close\");\n }\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface FileInputSettings extends NodeCallbacks {\n /** Absolute path to the media file (MP4, TS, MKV, …) */\n fileName: string;\n /** Loop the file indefinitely. Default: false */\n loop?: boolean;\n /** Source name used to identify streams from this node */\n sourceName?: string;\n /**\n * Start in a disabled state — the file is not read until `.enable()` is called\n * (or the node is enabled from the web UI). Default: false.\n */\n disabled?: boolean;\n /** Called when the node transitions to the enabled / ready state */\n onReady?: () => void;\n /**\n * Called once when the first data packet from this input arrives at the server.\n * Use this to convert absolute timestamps to the stream-relative `startAt`\n * expected by `CaptionSourceNode.send()`:\n *\n * ```ts\n * onStreamStart(wallClockMs) {\n * const startAt = myAbsoluteCaptionMs - wallClockMs;\n * await captions.send(text, { startAt });\n * }\n * ```\n *\n * @param wallClockMs `Date.now()` on the server at the moment the first\n * MPEG-TS data packet arrived from this input.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called once the file has finished playing (reaches EOF). Never fires when `loop` is true. */\n onEnded?: () => void;\n}\n\nexport class FileInputNode extends BaseNode {\n readonly settings: FileInputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: FileInputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /** Enable this node — starts reading the file and streaming to downstream nodes. */\n async enable(): Promise<void> {\n await this.client.trpc.enableNode.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n });\n }\n}\n\nexport async function createFileInputNode(\n workflow: WorkflowHandle,\n settings: FileInputSettings,\n nodeId?: string,\n): Promise<FileInputNode> {\n const id = nodeId ?? `file_input_${uid(12)}`;\n const node = new FileInputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") settings.onReady?.();\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined)\n settings.onStreamStart?.(Number(evt.message));\n if (evt.kind === \"ENDED\") settings.onEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"FILE_INPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n fileInput: {\n fileName: settings.fileName,\n loop: settings.loop ?? false,\n sourceName: settings.sourceName,\n disabled: settings.disabled ?? false,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface NodeStreamInputSettings extends NodeCallbacks {\n /**\n * FFmpeg format identifier for the incoming audio data.\n *\n * Raw PCM examples: `\"s16le\"` (16-bit signed LE), `\"f32le\"` (32-bit float LE).\n * Encoded examples: `\"wav\"`, `\"mp3\"`, `\"aac\"`, `\"flac\"`.\n */\n format: string;\n /** Sample rate in Hz. Required when `format` is a raw PCM format. */\n sampleRate?: number;\n /** Number of audio channels. Required when `format` is a raw PCM format. */\n channels?: number;\n /**\n * FFmpeg codec for the MPEG-TS output. Defaults to `\"aac\"`, which is safe\n * for any input format. Use `\"copy\"` only when the input is already\n * MPEG-TS-compatible AAC.\n */\n audioCodec?: string;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n /** Called when the node transitions to the ready state on the server. */\n onReady?: () => void;\n /**\n * Called once when the first audio packet is written to the server.\n * The argument is `Date.now()` on the server at that moment.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called once the underlying FFmpeg process exits cleanly after `end()`. */\n onEnded?: () => void;\n}\n\nexport class NodeStreamInputNode extends BaseNode {\n readonly settings: NodeStreamInputSettings;\n private readonly _apiBase: string;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: NodeStreamInputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n const { host, port } = workflow.client.options;\n this._apiBase = `http://${host}:${port}`;\n }\n\n /**\n * Push an audio stream to the server.\n *\n * Accepts any `AsyncIterable<Buffer | Uint8Array>`, which includes Node.js\n * `Readable` streams (async-iterable since Node 10) and async generators.\n * The call resolves when the source iterable is exhausted or the server\n * closes the connection.\n *\n * @example\n * ```ts\n * import { createReadStream } from \"node:fs\";\n * await audioInput.stream(createReadStream(\"audio.mp3\"));\n * ```\n *\n * @example\n * ```ts\n * // Raw PCM from a microphone via node-record-lpcm16\n * const mic = recorder.start({ sampleRate: 16000, channels: 1 });\n * await audioInput.stream(mic);\n * ```\n */\n async stream(source: AsyncIterable<Buffer | Uint8Array>): Promise<void> {\n const url = `${this._apiBase}/api/node-stream/${this.workflowId}/${this.nodeId}`;\n\n const body = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n for await (const chunk of source) {\n controller.enqueue(\n chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk),\n );\n }\n controller.close();\n } catch (err) {\n controller.error(err);\n }\n },\n });\n\n await fetch(url, {\n method: \"POST\",\n body,\n // Required for streaming request bodies in Node.js 18+ native fetch.\n duplex: \"half\",\n } as RequestInit);\n }\n}\n\nexport async function createNodeStreamInputNode(\n workflow: WorkflowHandle,\n settings: NodeStreamInputSettings,\n nodeId?: string,\n): Promise<NodeStreamInputNode> {\n const id = nodeId ?? `node_stream_${uid(12)}`;\n const node = new NodeStreamInputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") settings.onReady?.();\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined)\n settings.onStreamStart?.(Number(evt.message));\n if (evt.kind === \"ENDED\") settings.onEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"NODE_STREAM_INPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n nodeStreamInput: {\n format: settings.format,\n ...(settings.sampleRate !== undefined && {\n sampleRate: settings.sampleRate,\n }),\n ...(settings.channels !== undefined && {\n channels: settings.channels,\n }),\n audioCodec: settings.audioCodec ?? \"aac\",\n ...(settings.label !== undefined && { label: settings.label }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Connection info passed to `onConnect` and `onDisconnect`. */\nexport interface RtmpConnectionInfo {\n app: string;\n sourceName: string;\n /**\n * MediaMTX connection id for this publisher, if it could be resolved at\n * connect time. Pass directly to `operations.disconnectSource`.\n */\n connectionId?: string;\n}\n\n/** Operations available on a live RTMP publisher connection. */\nexport interface RtmpAnnouncerOperations {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Safe to\n * call even if it already disconnected (resolves as a no-op).\n */\n disconnectSource(connectionId: string): Promise<void>;\n}\n\nexport interface RtmpAnnouncerSettings extends NodeCallbacks {\n /** RTMP app name to watch (default: \"live\"). */\n app?: string;\n /**\n * Called when a publisher connects. Create nodes in any workflow from\n * here, or call `operations.disconnectSource(info.connectionId)` to kick\n * the publisher synchronously (e.g. to reject an invalid `sourceName`).\n */\n onConnect: (\n info: RtmpConnectionInfo,\n operations: RtmpAnnouncerOperations,\n ) => void;\n /** Called when a publisher disconnects. */\n onDisconnect?: (info: RtmpConnectionInfo) => void;\n}\n\n/** SDK handle for an RTMP announcer node. */\nexport class RtmpAnnouncerNode extends BaseNode {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Can be\n * called at any time after the node is created — e.g. from an async\n * moderation decision made after `onConnect` already returned.\n */\n async disconnectSource(connectionId: string): Promise<void> {\n await this.client.trpc.disconnectSource.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n connectionId,\n });\n }\n}\n\nexport async function createRtmpAnnouncerNode(\n workflow: WorkflowHandle,\n settings: RtmpAnnouncerSettings,\n nodeId?: string,\n): Promise<RtmpAnnouncerNode> {\n const id = nodeId ?? `rtmp_announcer_${uid(12)}`;\n const app = settings.app ?? \"live\";\n const node = new RtmpAnnouncerNode(id, workflow.client, workflow.workflowId);\n const operations: RtmpAnnouncerOperations = {\n disconnectSource: (connectionId) => node.disconnectSource(connectionId),\n };\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"STREAM_CONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as RtmpConnectionInfo;\n settings.onConnect(info, operations);\n }\n if (evt.kind === \"STREAM_DISCONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as RtmpConnectionInfo;\n settings.onDisconnect?.(info);\n }\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_ANNOUNCER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpAnnouncer: { app },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport type { RemaClient } from \"../client.js\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n reportNodeError,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Normalize a `catch`-clause value (which TypeScript types as `unknown`) into an `Error`. */\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err));\n}\n\n// ── RtmpStreamNode ─────────────────────────────────────────────────────────────\n\n/**\n * Represents a single active RTMP stream created by an `RtmpMultiplexerNode`.\n *\n * The server creates this node automatically when a publisher connects. The SDK\n * wraps the server-side node ID so it can be passed as a source to downstream\n * nodes (encoders, outputs) via `subscribe()`.\n */\nexport class RtmpStreamNode extends BaseNode {\n readonly app: string;\n readonly sourceName: string;\n\n constructor(\n nodeId: string,\n client: RemaClient,\n app: string,\n sourceName: string,\n workflowId: string,\n ) {\n super(nodeId, client, workflowId);\n this.app = app;\n this.sourceName = sourceName;\n }\n}\n\n// ── RtmpMultiplexerNode ────────────────────────────────────────────────────────\n\n/**\n * SDK handle for an RTMP multiplexer node.\n *\n * Calling `close()` deactivates the multiplexer and all of its active stream\n * nodes on the server.\n */\nexport class RtmpMultiplexerNode extends BaseNode {}\n\n// ── Settings ──────────────────────────────────────────────────────────────────\n\nexport interface RtmpMultiplexerSettings extends NodeCallbacks {\n /** RTMP app name to listen on (default: \"live\"). */\n app?: string;\n\n /**\n * Called when a publisher connects. Return `{ accept: false }` to kick the\n * connection immediately; return `{ accept: true }` or omit the return value\n * to accept and trigger `onStream`.\n */\n onConnect?: (info: {\n app: string;\n sourceName: string;\n }) =>\n | Promise<{ accept: boolean } | undefined>\n | { accept: boolean }\n | undefined;\n\n /**\n * Called with the per-publisher `RtmpStreamNode` when a new RTMP stream is\n * accepted. Create your downstream pipeline (encoders, outputs) from this node.\n */\n onStream: (node: RtmpStreamNode) => Promise<void> | void;\n\n /**\n * Called when a publisher disconnects or is rejected. Use this to close any\n * downstream nodes you created in `onStream`.\n */\n onStreamEnd?: (node: RtmpStreamNode) => Promise<void> | void;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport async function createRtmpMultiplexerNode(\n workflow: WorkflowHandle,\n settings: RtmpMultiplexerSettings,\n nodeId?: string,\n): Promise<RtmpMultiplexerNode> {\n const id = nodeId ?? `rtmp_mux_${uid(12)}`;\n const app = settings.app ?? \"live\";\n const node = new RtmpMultiplexerNode(\n id,\n workflow.client,\n workflow.workflowId,\n );\n\n // Track active stream nodes by sourceName for onStreamEnd lookup.\n const activeStreams = new Map<string, RtmpStreamNode>();\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n if (evt.kind === \"CHILD_CREATED\" && evt.message !== undefined) {\n const { childNodeId, sourceName } = JSON.parse(evt.message) as {\n childNodeId: string;\n sourceName: string;\n };\n\n Promise.resolve(settings.onConnect?.({ app, sourceName }))\n .then(async (result) => {\n if (result?.accept === false) {\n await workflow.client.trpc.rejectPublisher\n .mutate({\n workflowId: workflow.workflowId,\n nodeId: id,\n sourceName,\n })\n .catch((err: unknown) => {\n reportNodeError(node, settings, toError(err));\n });\n return;\n }\n const streamNode = new RtmpStreamNode(\n childNodeId,\n workflow.client,\n app,\n sourceName,\n workflow.workflowId,\n );\n activeStreams.set(sourceName, streamNode);\n await settings.onStream(streamNode);\n })\n .catch((err: unknown) => {\n reportNodeError(node, settings, toError(err));\n });\n return;\n }\n\n if (evt.kind === \"CHILD_CLOSED\" && evt.message !== undefined) {\n const { sourceName } = JSON.parse(evt.message) as { sourceName: string };\n const streamNode = activeStreams.get(sourceName);\n activeStreams.delete(sourceName);\n if (streamNode) {\n Promise.resolve(settings.onStreamEnd?.(streamNode)).catch(\n (err: unknown) => {\n reportNodeError(node, settings, toError(err));\n },\n );\n }\n }\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_MULTIPLEXER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpMultiplexer: { app, port: 1935 },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface RtmpReaderSettings extends NodeCallbacks {\n /** RTMP app name. */\n app: string;\n /** Source name (stream key). */\n sourceName: string;\n /**\n * Retry policy for a failed FFmpeg pull from the internal MediaMTX relay\n * (e.g. a transient network blip). All fields optional; defaults to 5\n * attempts with 1s→10s exponential backoff before the node errors out.\n */\n retry?: {\n /** Max retries before giving up and marking the node ERROR. Default: 5. */\n maxAttempts?: number;\n /** Initial backoff delay in ms before the first retry. Default: 1000. */\n baseDelayMs?: number;\n /** Backoff delay ceiling in ms. Default: 10000. */\n maxDelayMs?: number;\n };\n /** Called when an RTMP publisher connects. */\n onPublish?: (info: { app: string; internalUrl: string }) => void;\n /**\n * Called once when the first data packet from this input arrives at the server.\n * @param wallClockMs `Date.now()` on the server at the moment the first MPEG-TS packet arrived.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called when the publisher disconnects (the underlying FFmpeg pull exits). */\n onStreamEnded?: () => void;\n}\n\nexport class RtmpReaderNode extends BaseNode {\n readonly settings: RtmpReaderSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: RtmpReaderSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createRtmpReaderNode(\n workflow: WorkflowHandle,\n settings: RtmpReaderSettings,\n nodeId?: string,\n): Promise<RtmpReaderNode> {\n const id = nodeId ?? `rtmp_reader_${uid(12)}`;\n const node = new RtmpReaderNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") {\n settings.onPublish?.({\n app: settings.app,\n internalUrl: evt.message ?? \"\",\n });\n }\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined) {\n settings.onStreamStart?.(Number(evt.message));\n }\n if (evt.kind === \"ENDED\") settings.onStreamEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_READER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpReader: {\n app: settings.app,\n sourceName: settings.sourceName,\n ...(settings.retry !== undefined && { retry: settings.retry }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Connection info passed to `onConnect` and `onDisconnect`. */\nexport interface SrtConnectionInfo {\n app: string;\n sourceName: string;\n /** Passphrase configured on the announcer, if any. */\n passphrase?: string;\n /** Negotiated SRT latency in milliseconds, if configured. */\n latencyMs?: number;\n /**\n * MediaMTX connection id for this publisher, if it could be resolved at\n * connect time. Pass directly to `operations.disconnectSource`.\n */\n connectionId?: string;\n}\n\n/** Operations available on a live SRT publisher connection. */\nexport interface SrtAnnouncerOperations {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Safe to\n * call even if it already disconnected (resolves as a no-op).\n */\n disconnectSource(connectionId: string): Promise<void>;\n}\n\nexport interface SrtAnnouncerSettings extends NodeCallbacks {\n /** SRT app name to watch (default: \"live\"). */\n app?: string;\n /** Passphrase to include in connection events (mirrors MediaMTX path config). */\n passphrase?: string;\n /** SRT latency in milliseconds to include in connection events. */\n latencyMs?: number;\n /**\n * Called when a publisher connects. Create nodes in any workflow from\n * here, or call `operations.disconnectSource(info.connectionId)` to kick\n * the publisher synchronously (e.g. to reject an invalid `sourceName`).\n */\n onConnect: (\n info: SrtConnectionInfo,\n operations: SrtAnnouncerOperations,\n ) => void;\n /** Called when a publisher disconnects. */\n onDisconnect?: (info: Pick<SrtConnectionInfo, \"app\" | \"sourceName\">) => void;\n}\n\n/** SDK handle for an SRT announcer node. */\nexport class SrtAnnouncerNode extends BaseNode {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Can be\n * called at any time after the node is created — e.g. from an async\n * moderation decision made after `onConnect` already returned.\n */\n async disconnectSource(connectionId: string): Promise<void> {\n await this.client.trpc.disconnectSource.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n connectionId,\n });\n }\n}\n\nexport async function createSrtAnnouncerNode(\n workflow: WorkflowHandle,\n settings: SrtAnnouncerSettings,\n nodeId?: string,\n): Promise<SrtAnnouncerNode> {\n const id = nodeId ?? `srt_announcer_${uid(12)}`;\n const app = settings.app ?? \"live\";\n const node = new SrtAnnouncerNode(id, workflow.client, workflow.workflowId);\n const operations: SrtAnnouncerOperations = {\n disconnectSource: (connectionId) => node.disconnectSource(connectionId),\n };\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"STREAM_CONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as SrtConnectionInfo;\n settings.onConnect(info, operations);\n }\n if (evt.kind === \"STREAM_DISCONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as Pick<\n SrtConnectionInfo,\n \"app\" | \"sourceName\"\n >;\n settings.onDisconnect?.(info);\n }\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"SRT_ANNOUNCER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n srtAnnouncer: {\n app,\n ...(settings.passphrase !== undefined && {\n passphrase: settings.passphrase,\n }),\n ...(settings.latencyMs !== undefined && {\n latencyMs: settings.latencyMs,\n }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface SrtReaderSettings extends NodeCallbacks {\n /** SRT app name. */\n app: string;\n /** Source name (stream key / stream ID suffix). */\n sourceName: string;\n /**\n * Retry policy for a failed FFmpeg pull from the internal MediaMTX relay\n * (e.g. a transient network blip). All fields optional; defaults to 5\n * attempts with 1s→10s exponential backoff before the node errors out.\n */\n retry?: {\n /** Max retries before giving up and marking the node ERROR. Default: 5. */\n maxAttempts?: number;\n /** Initial backoff delay in ms before the first retry. Default: 1000. */\n baseDelayMs?: number;\n /** Backoff delay ceiling in ms. Default: 10000. */\n maxDelayMs?: number;\n };\n /** Called when an SRT publisher connects and the stream is ready to read. */\n onPublish?: (info: { app: string; internalUrl: string }) => void;\n /**\n * Called once when the first data packet from this input arrives at the server.\n * @param wallClockMs `Date.now()` on the server at the moment the first MPEG-TS packet arrived.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called when the publisher disconnects (the underlying FFmpeg pull exits). */\n onStreamEnded?: () => void;\n}\n\n/** SDK handle for an SRT reader node. */\nexport class SrtReaderNode extends BaseNode {\n readonly settings: SrtReaderSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: SrtReaderSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createSrtReaderNode(\n workflow: WorkflowHandle,\n settings: SrtReaderSettings,\n nodeId?: string,\n): Promise<SrtReaderNode> {\n const id = nodeId ?? `srt_reader_${uid(12)}`;\n const node = new SrtReaderNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") {\n settings.onPublish?.({\n app: settings.app,\n internalUrl: evt.message ?? \"\",\n });\n }\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined) {\n settings.onStreamStart?.(Number(evt.message));\n }\n if (evt.kind === \"ENDED\") settings.onStreamEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"SRT_READER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n srtReader: {\n app: settings.app,\n sourceName: settings.sourceName,\n ...(settings.retry !== undefined && { retry: settings.retry }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface FileOutputSettings extends NodeCallbacks {\n /** Absolute path for the output file */\n fileName: string;\n /** Container format: \"mp4\" | \"ts\" | \"mkv\". Default: inferred from extension */\n format?: string;\n /** Overwrite `fileName` if it already exists. Default: `true`. */\n overwrite?: boolean;\n}\n\nexport class FileOutputNode extends BaseNode {\n readonly settings: FileOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: FileOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createFileOutputNode(\n workflow: WorkflowHandle,\n settings: FileOutputSettings,\n nodeId?: string,\n): Promise<FileOutputNode> {\n const id = nodeId ?? `file_output_${uid(12)}`;\n const node = new FileOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"FILE_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n fileOutput: {\n fileName: settings.fileName,\n format: settings.format ?? \"\",\n overwrite: settings.overwrite ?? true,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { ActivationGate } from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type HlsSubscriptionSpec,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\nimport { expandRenditionSpecs } from \"./hls-renditions.js\";\n\nexport type HlsDestination =\n | { type: \"file\"; path: string }\n | {\n type: \"s3\";\n bucket: string;\n region: string;\n /** S3-compatible endpoint URL (e.g. Tigris, MinIO, Cloudflare R2). */\n endpoint?: string;\n /** Key prefix prepended to all uploaded paths. */\n keyPrefix?: string;\n /**\n * When true (default), a per-session timestamp suffix is appended to\n * `keyPrefix` so that browser-cached segments from a previous session are\n * never served for a new one. Set to false when the caller already\n * generates a unique prefix or when sub-paths are not supported.\n */\n makePrefixUnique?: boolean;\n accessKeyId: string;\n secretAccessKey: string;\n /**\n * `Cache-Control` header for `.m3u8` playlist objects.\n * Default: `\"no-cache, no-store, must-revalidate\"` — playlists must\n * never be served stale.\n */\n playlistCacheControl?: string;\n /**\n * `Cache-Control` header for segment objects (`.ts`, `.vtt`).\n * Default: `\"public, max-age=86400\"` — segments are immutable once\n * written, so they can be cached aggressively.\n */\n segmentsCacheControl?: string;\n }\n | {\n type: \"http\";\n /** Base URL that receives PUT/DELETE requests for each segment and playlist. */\n baseUrl: string;\n /** Extra headers sent with every PUT/DELETE request. */\n headers?: Record<string, string>;\n };\n\nexport interface HlsOutputSettings extends NodeCallbacks {\n /** One or more destinations to fan HLS output to. */\n destinations: HlsDestination[];\n /** Target segment duration in seconds. Default: 6 */\n segmentTime?: number;\n /**\n * Number of segments kept in the live playlist window.\n * 0 = keep all (VOD). Default: 5.\n */\n listSize?: number;\n /**\n * Node-level fallback audio codec, applied to audio streams whose\n * subscription does not specify its own `codec`. Omitted (or `copy`)\n * preserves the input audio codec — except when more than one audio source\n * is connected, where the default becomes `aac` so all alternate-audio\n * renditions share one codec (required by hls.js).\n *\n * For a caption-readiness buffer (the old `avDelayMs`), put `delayMs` on\n * each media subscription instead and leave caption subscriptions\n * undelayed.\n */\n audioCodec?: \"copy\" | string | null;\n /**\n * Node-level fallback video codec, applied to video streams whose\n * subscription does not specify its own `codec`. Omitted (or `copy`)\n * preserves the input video codec.\n */\n videoCodec?: \"copy\" | string | null;\n}\n\nexport class HlsOutputNode extends BaseNode {\n readonly settings: HlsOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: HlsOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /**\n * Return the effective base path or URL for each configured destination in\n * the current session. Must be called after `subscribe()` so that the server\n * has wired the destinations. For S3 destinations this includes the\n * per-session subfolder appended by `makePrefixUnique`.\n */\n async effectivePaths(): Promise<string[]> {\n const result = await this.client.trpc.hlsOutputInfo.query({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n });\n return result.effectivePaths;\n }\n\n /**\n * Subscribe this node to one or more source nodes, expanding any\n * `VIDEO`/`ALL` spec's `additionalRenditions` into extra HLS variants\n * first (see `VideoRendition`). Keyframe alignment for generated\n * renditions uses this node's own `segmentTime`.\n */\n override async subscribe(\n specs: HlsSubscriptionSpec[],\n options?: { gate?: ActivationGate },\n ): Promise<void> {\n await super.subscribe(\n expandRenditionSpecs(specs, this.settings.segmentTime ?? 6),\n options,\n );\n }\n}\n\nexport async function createHlsOutputNode(\n workflow: WorkflowHandle,\n settings: HlsOutputSettings,\n nodeId?: string,\n): Promise<HlsOutputNode> {\n const id = nodeId ?? `hls_output_${uid(12)}`;\n const node = new HlsOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"HLS_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n hlsOutput: {\n destinations: settings.destinations,\n segmentTime: settings.segmentTime ?? 6,\n listSize: settings.listSize ?? 5,\n audioCodec: settings.audioCodec ?? undefined,\n videoCodec: settings.videoCodec ?? undefined,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type {\n BaseNode,\n HlsSubscriptionSpec,\n HlsVideoSubscriptionSpec,\n SubscriptionSpec,\n VideoRendition,\n} from \"../node.js\";\n\n/** Ladders beyond this total variant count get a one-time `console.warn`. */\nconst RENDITION_WARN_THRESHOLD = 3;\n\n/**\n * Parse an ffmpeg bitrate value (`\"3M\"`, `\"1500k\"`, or a bare bits/sec\n * number) to bits/sec, for deriving a default `bufsize`.\n */\nfunction bitsPerSecond(v: string | number): number {\n if (typeof v === \"number\") return v;\n const match = /^([\\d.]+)\\s*([kKmM]?)$/.exec(v.trim());\n if (!match) return Number(v) || 0;\n const value = Number(match[1]);\n const suffix = match[2]?.toLowerCase();\n if (suffix === \"k\") return value * 1_000;\n if (suffix === \"m\") return value * 1_000_000;\n return value;\n}\n\n/** Build one generated `HlsVideoSubscriptionSpec` for a single ladder tier. */\nfunction buildRenditionSpec(\n source: BaseNode,\n track: number | undefined,\n r: VideoRendition,\n segmentTime: number,\n): HlsVideoSubscriptionSpec {\n const bitrate = String(r.bitrate);\n const maxrate = r.maxrate !== undefined ? String(r.maxrate) : bitrate;\n const bufsize =\n r.bufsize !== undefined\n ? String(r.bufsize)\n : String(2 * bitsPerSecond(r.bitrate));\n\n return {\n source,\n sourceSelector: \"VIDEO\",\n ...(track !== undefined ? { track } : {}),\n codec: {\n name: r.codecName ?? \"libx264\",\n options: {\n preset: r.preset ?? \"veryfast\",\n b: bitrate,\n maxrate,\n bufsize,\n // fps-agnostic keyframe alignment so every rendition cuts HLS\n // segments at the same wall-clock points — required for clean ABR\n // switching (see VideoRendition docs / SDK docs CPU-cost section).\n force_key_frames: `expr:gte(t,n_forced*${segmentTime})`,\n // Per-stream options always get a `:v:N` specifier appended (see\n // `buildCodecArgs`) — `filter` (not the bare `vf` alias, which does\n // not accept a stream specifier: `-vf:v:N` is invalid FFmpeg syntax\n // and fails with AVERROR(EINVAL)) becomes the valid `-filter:v:N`.\n ...(r.width !== undefined\n ? { s: `${r.width}x${r.height}` }\n : { filter: `scale=-2:${r.height}` }),\n ...r.extraOptions,\n },\n },\n video: { label: r.label ?? `${r.height}p` },\n };\n}\n\n/**\n * Expand every `VIDEO`/`ALL` spec's `additionalRenditions` into extra\n * `VideoSubscriptionSpec` entries, returning the full flat list to actually\n * subscribe. Non-VIDEO/ALL specs (AUDIO, CAPTIONS) and VIDEO/ALL specs with\n * no `additionalRenditions` pass through unchanged. Pure — no network\n * calls.\n *\n * Emits one `console.warn` (advisory, not thrown) when the total number of\n * video variants across all specs exceeds a sane default ladder size —\n * CPU cost scales ~linearly with rendition count, dominated by encode: each\n * rendition is an independent decode+encode pass inside the HLS output's\n * one shared FFmpeg process (no single-decode `filter_complex split+scale`\n * optimization).\n */\nexport function expandRenditionSpecs(\n specs: HlsSubscriptionSpec[],\n segmentTime: number,\n): SubscriptionSpec[] {\n const result: SubscriptionSpec[] = [];\n let videoVariantCount = 0;\n\n for (const spec of specs) {\n if (spec.sourceSelector !== \"VIDEO\" && spec.sourceSelector !== \"ALL\") {\n result.push(spec);\n continue;\n }\n\n const { additionalRenditions, omitOriginalRendition, ...rest } = spec;\n\n if (!additionalRenditions || additionalRenditions.length === 0) {\n result.push(spec);\n videoVariantCount++;\n continue;\n }\n\n if (!omitOriginalRendition) {\n result.push(rest);\n videoVariantCount++;\n }\n\n const track = spec.sourceSelector === \"VIDEO\" ? spec.track : undefined;\n for (const r of additionalRenditions) {\n result.push(buildRenditionSpec(spec.source, track, r, segmentTime));\n videoVariantCount++;\n }\n }\n\n if (videoVariantCount > RENDITION_WARN_THRESHOLD) {\n console.warn(\n `[rema] HLS output: ${videoVariantCount} video renditions means ${videoVariantCount} concurrent decode+encode passes inside one FFmpeg process — CPU cost scales ~linearly with rendition count, dominated by encode. Consider trimming tiers, a faster preset (\"superfast\"/\"ultrafast\"), or a hardware encoder via VideoRendition.codecName (e.g. \"h264_videotoolbox\", \"h264_nvenc\").`,\n );\n }\n\n return result;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface RtmpOutputSettings extends NodeCallbacks {\n /** Full RTMP URL to push to, e.g. \"rtmp://live.twitch.tv/app/stream-key\" */\n url: string;\n /**\n * Called when the output FFmpeg process exits with an error after the node\n * has been created — e.g. the destination server refused the connection,\n * dropped mid-stream, or returned an unexpected error code.\n *\n * Errors that occur during node creation still reject the\n * `workflow.output.rtmp()` promise as usual.\n */\n onError?: (err: Error) => void;\n}\n\nexport class RtmpOutputNode extends BaseNode {\n readonly settings: RtmpOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: RtmpOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createRtmpOutputNode(\n workflow: WorkflowHandle,\n settings: RtmpOutputSettings,\n nodeId?: string,\n): Promise<RtmpOutputNode> {\n const id = nodeId ?? `rtmp_output_${uid(12)}`;\n const node = new RtmpOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpOutput: { url: settings.url },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface SrtOutputSettings extends NodeCallbacks {\n /**\n * Full SRT URL to push to, e.g. `\"srt://host:port\"`.\n * Transport parameters (latency, passphrase, etc.) are passed as query\n * params: `\"srt://host:port?latency=200&passphrase=secret\"`.\n */\n url: string;\n /**\n * Called when the output FFmpeg process exits with an error after the node\n * has been created — e.g. the destination refused the connection or dropped\n * mid-stream.\n *\n * Errors that occur during node creation still reject the\n * `workflow.output.srt()` promise as usual.\n */\n onError?: (err: Error) => void;\n}\n\nexport class SrtOutputNode extends BaseNode {\n readonly settings: SrtOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: SrtOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createSrtOutputNode(\n workflow: WorkflowHandle,\n settings: SrtOutputSettings,\n nodeId?: string,\n): Promise<SrtOutputNode> {\n const id = nodeId ?? `srt_output_${uid(12)}`;\n const node = new SrtOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"SRT_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n srtOutput: { url: settings.url },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type {\n AzureSpeechLocale,\n AzureTargetLanguageCode,\n AzureVoiceName,\n} from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface AzureTranslateSettings extends NodeCallbacks {\n /** Azure Speech resource key. */\n apiKey: string;\n /** Azure Speech resource region, e.g. `\"westeurope\"`. */\n region: string;\n /**\n * Source language code for the input audio, e.g. `\"en-US\"`. Autocompletes\n * common locales; any valid Azure locale works — see\n * https://learn.microsoft.com/azure/ai-services/speech-service/language-support?tabs=stt\n * for the full list.\n */\n sourceLanguageCode: AzureSpeechLocale;\n /**\n * Target language code for the translation output, e.g. `\"de\"`.\n * Autocompletes common codes; any valid Azure target works — see\n * https://learn.microsoft.com/azure/ai-services/speech-service/language-support?tabs=speech-translation\n * for the full list.\n */\n targetLanguageCode: AzureTargetLanguageCode;\n /**\n * Target-language neural voice used to synthesize the translation, e.g.\n * `\"de-DE-KatjaNeural\"`. Must match the target language. Autocompletes\n * common voices; any valid Azure voice works — see\n * https://learn.microsoft.com/azure/ai-services/speech-service/language-support?tabs=tts\n * for the full list.\n */\n voiceName: AzureVoiceName;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via Azure AI Speech's real-time translation + synthesis. */\nexport class AzureTranslateNode extends BaseNode {\n readonly settings: AzureTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: AzureTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createAzureTranslateNode(\n workflow: WorkflowHandle,\n settings: AzureTranslateSettings,\n nodeId?: string,\n): Promise<AzureTranslateNode> {\n const id = nodeId ?? `azure_translate_${uid(12)}`;\n const node = new AzureTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"AZURE_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n azureTranslate: {\n apiKey: settings.apiKey,\n region: settings.region,\n sourceLanguageCode: settings.sourceLanguageCode,\n targetLanguageCode: settings.targetLanguageCode,\n voiceName: settings.voiceName,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface BufferSettings extends NodeCallbacks {\n /** Time (ms) to hold back the input stream before forwarding it to subscribers. */\n bufferMs: number;\n}\n\n/**\n * Time-shift node. Subscribe it to exactly one source (any selector); it\n * re-emits that source delayed by `bufferMs`, shared so every downstream\n * subscriber sees the same delayed feed.\n */\nexport class BufferNode extends BaseNode {\n readonly settings: BufferSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: BufferSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\n/** Create a {@link BufferNode} on the server. */\nexport async function createBufferNode(\n workflow: WorkflowHandle,\n settings: BufferSettings,\n nodeId?: string,\n): Promise<BufferNode> {\n const id = nodeId ?? `buffer_${uid(12)}`;\n const node = new BufferNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"BUFFER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n buffer: { bufferMs: settings.bufferMs },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface CambAiTranslateSettings extends NodeCallbacks {\n /** Camb.ai API key. */\n apiKey: string;\n /** BCP-47 source language code for the input audio (e.g. `\"en-US\"`). */\n sourceLanguageCode: string;\n /** BCP-47 target language code for the translation output (e.g. `\"de-DE\"`). */\n targetLanguageCode: string;\n /** Realtime translation model. Defaults to Camb's server-side default. */\n model?: \"lilac\" | \"violet\" | \"iris\" | \"orchid\";\n /** Cloned voice ID to synthesize the translation with. Defaults to a built-in voice for the target language. */\n voiceId?: number;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via the Camb.ai realtime speech-to-speech API. */\nexport class CambAiTranslateNode extends BaseNode {\n readonly settings: CambAiTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: CambAiTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createCambAiTranslateNode(\n workflow: WorkflowHandle,\n settings: CambAiTranslateSettings,\n nodeId?: string,\n): Promise<CambAiTranslateNode> {\n const id = nodeId ?? `camb_translate_${uid(12)}`;\n const node = new CambAiTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"CAMB_AI_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n cambAiTranslate: {\n apiKey: settings.apiKey,\n sourceLanguageCode: settings.sourceLanguageCode,\n targetLanguageCode: settings.targetLanguageCode,\n model: settings.model,\n voiceId: settings.voiceId,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport type CaptionMode = \"rollUp\" | \"popOn\" | \"paintOn\";\n\nexport interface Cea608EncoderSettings extends NodeCallbacks {\n /** Caption display mode. Default: \"rollUp\" */\n mode?: CaptionMode;\n /**\n * Maximum number of visible rows (2, 3, or 4). Default: 2.\n *\n * - **rollUp**: sets the scroll-window height (RU2/RU3/RU4 command).\n * - **popOn**: captions sent with `\\n`-separated lines are displayed\n * bottom-anchored; this value caps how many lines are shown at once.\n * E.g. `rows: 2` with `\"line one\\nline two\"` fills rows 14 and 15.\n */\n rows?: 2 | 3 | 4;\n /**\n * When `mode` is `\"popOn\"`, automatically word-wrap caption text to\n * CEA-608's 32-column display width before laying it out as a\n * bottom-anchored multi-line caption card (capped at `rows` lines) —\n * instead of requiring you to insert `\\n` yourself. Has no effect in\n * `\"rollUp\"`/`\"paintOn\"` modes, where each `send()` is naturally a single\n * line.\n *\n * Default: false.\n */\n autoWrap?: boolean;\n}\n\nexport class Cea608EncoderNode extends BaseNode {\n readonly settings: Cea608EncoderSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: Cea608EncoderSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createCea608EncoderNode(\n workflow: WorkflowHandle,\n settings: Cea608EncoderSettings = {},\n nodeId?: string,\n): Promise<Cea608EncoderNode> {\n const id = nodeId ?? `cea608_${uid(12)}`;\n const node = new Cea608EncoderNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"CEA608_ENCODER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n cea608Encoder: {\n mode: settings.mode ?? \"rollUp\",\n rows: settings.rows ?? 2,\n autoWrap: settings.autoWrap ?? false,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface DeepgramSttSettings extends NodeCallbacks {\n /** Deepgram API key. */\n apiKey: string;\n /**\n * BCP-47 (or bare) language code hinting at the primary spoken language,\n * e.g. `\"en-US\"` or `\"en\"`. Also used as this node's subtitle track\n * language tag. Omitted lets Deepgram auto-detect (model-dependent) and\n * defaults the subtitle language tag to `\"en\"`.\n */\n language?: string;\n /** Deepgram model name, e.g. `\"nova-3\"`. Defaults to Deepgram's server-side default. */\n model?: string;\n /**\n * Schedule interim (non-final) transcripts as captions too, not just\n * finalized utterances. Interim results lower latency at the cost of\n * captions that may be replaced/overlap shortly after appearing. Default: `false`.\n */\n interimResults?: boolean;\n /** When set, captions longer than this are split into sequential chunks with proportional timing. */\n maxLineLength?: number;\n /** Subtitle track label shown to viewers, e.g. `\"English (auto)\"`. Defaults to `language`, or `\"Auto\"` if `language` is also omitted. */\n subtitleLabel?: string;\n /** Marks this subtitle track as the default track in the HLS master playlist. Default: `false`. */\n subtitleDefault?: boolean;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/**\n * Processor node that transcribes audio in real-time using Deepgram's\n * streaming speech-to-text API and exposes the transcripts as timed\n * captions — subscribe a `Cea608EncoderNode` to it (`sourceSelector:\n * \"CAPTIONS\"`) for burned-in captions, or select it as an HLS subtitle\n * track, exactly like `wf.input.captions()`.\n */\nexport class DeepgramSttNode extends BaseNode {\n readonly settings: DeepgramSttSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: DeepgramSttSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createDeepgramSttNode(\n workflow: WorkflowHandle,\n settings: DeepgramSttSettings,\n nodeId?: string,\n): Promise<DeepgramSttNode> {\n const id = nodeId ?? `deepgram_stt_${uid(12)}`;\n const node = new DeepgramSttNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"DEEPGRAM_STT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n deepgramStt: {\n apiKey: settings.apiKey,\n language: settings.language,\n model: settings.model,\n interimResults: settings.interimResults,\n maxLineLength: settings.maxLineLength,\n subtitleLabel: settings.subtitleLabel,\n subtitleDefault: settings.subtitleDefault,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { MonitorEventMsg } from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport type { MonitorEventMsg };\n\nexport interface FfprobeMonitorSettings extends NodeCallbacks {\n /**\n * How often (ms) to re-run ffprobe on the attached stream.\n * Each cycle spawns a fresh ffprobe process, probes `probeSizeBytes` of data,\n * and publishes the result to the **stream-info** monitor.\n * Default: 30 000 ms.\n */\n intervalMs?: number;\n /**\n * Bytes of stream data to feed into each ffprobe invocation.\n * Larger values give ffprobe more context (e.g. long-GOP streams) at the\n * cost of a slightly longer first-result latency. Default: 1 048 576 (1 MB).\n */\n probeSizeBytes?: number;\n}\n\nexport class FfprobeMonitorNode extends BaseNode {\n readonly settings: FfprobeMonitorSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: FfprobeMonitorSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /**\n * Subscribe to a monitor stream on this node.\n *\n * - `\"stream-info\"` — emits a `status` event (key/value map) every time a\n * probe cycle completes, containing codec names, resolution, frame rate,\n * bit rates, and format.\n * - `\"ffprobe-log\"` — emits `log` events with raw ffprobe stderr output.\n *\n * Returns an object with an `unsubscribe()` method; call it to stop receiving\n * events and release the tRPC subscription.\n */\n observe(\n monitorName: \"stream-info\" | \"ffprobe-log\",\n onData: (event: MonitorEventMsg) => void,\n onError?: (err: unknown) => void,\n ): { unsubscribe(): void } {\n return this.client.trpc.observeNode.subscribe(\n { workflowId: this.workflowId, nodeId: this.nodeId, monitorName },\n { onData, ...(onError !== undefined && { onError }) },\n );\n }\n}\n\nexport async function createFfprobeMonitorNode(\n workflow: WorkflowHandle,\n settings: FfprobeMonitorSettings = {},\n nodeId?: string,\n): Promise<FfprobeMonitorNode> {\n const id = nodeId ?? `ffprobe_${uid(12)}`;\n const node = new FfprobeMonitorNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"FFPROBE_MONITOR\",\n workflowId: workflow.workflowId,\n nodeId: id,\n ffprobeMonitor: {\n intervalMs: settings.intervalMs ?? 30_000,\n probeSizeBytes: settings.probeSizeBytes ?? 1_048_576,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface GateSettings extends NodeCallbacks {\n /**\n * Number of distinct sources that must have emitted at least one item\n * before the gate opens. Default: wait for every subscribed source.\n */\n minReady?: number;\n /**\n * Force the gate open after this many ms even if `minReady` hasn't been\n * reached, using whatever sources are ready by then. Omit to wait\n * indefinitely — a source that never produces data would otherwise stall\n * the gate forever.\n */\n timeoutMs?: number;\n}\n\n/**\n * Synchronization barrier for fan-in from multiple, independently-timed\n * sources (e.g. two different transcription/translation services). Not to\n * be confused with the `gate` option on `.subscribe()` (`ActivationGate`),\n * which defers wiring based on upstream node *lifecycle*, not data.\n *\n * `GateNode` subscribes to every source immediately and queues everything\n * each one emits, then starts forwarding — for every source at once — only\n * once enough of them have produced their first item. It is a one-time\n * rendezvous, not a per-item join.\n *\n * Every source becomes its own keyed pass-through port on the gate — streams\n * are never merged. Name a port with `name` on the gate's subscription spec\n * (default: the source's index as a string) and read it downstream with\n * `port`:\n *\n * ```ts\n * await gate.subscribe([\n * { source: a, sourceSelector: \"ALL\", name: \"main\" },\n * { source: b, sourceSelector: \"ALL\", name: \"backup\" },\n * ]);\n * await out1.subscribe([{ source: gate, sourceSelector: \"ALL\", port: \"main\" }]);\n * await out2.subscribe([{ source: gate, sourceSelector: \"ALL\", port: \"backup\" }]);\n * ```\n *\n * With exactly one source of a kind, plain selector subscriptions (no\n * `port`) still resolve to it; with several same-kind sources the server\n * errors and asks for an explicit `port`.\n */\nexport class GateNode extends BaseNode {\n readonly settings: GateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: GateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\n/** Create a {@link GateNode} on the server. */\nexport async function createGateNode(\n workflow: WorkflowHandle,\n settings: GateSettings = {},\n nodeId?: string,\n): Promise<GateNode> {\n const id = nodeId ?? `gate_${uid(12)}`;\n const node = new GateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"GATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n gate: {\n ...(settings.minReady !== undefined && {\n minReady: settings.minReady,\n }),\n ...(settings.timeoutMs !== undefined && {\n timeoutMs: settings.timeoutMs,\n }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface GeminiTranslateSettings extends NodeCallbacks {\n /** Gemini API key. */\n apiKey: string;\n /**\n * BCP-47 target language code for the translation output\n * (e.g. `\"it\"`, `\"es\"`, `\"fr\"`).\n */\n targetLanguageCode: string;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via the Gemini Live Translate API. */\nexport class GeminiTranslateNode extends BaseNode {\n readonly settings: GeminiTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: GeminiTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createGeminiTranslateNode(\n workflow: WorkflowHandle,\n settings: GeminiTranslateSettings,\n nodeId?: string,\n): Promise<GeminiTranslateNode> {\n const id = nodeId ?? `gemini_translate_${uid(12)}`;\n const node = new GeminiTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"GEMINI_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n geminiTranslate: {\n apiKey: settings.apiKey,\n targetLanguageCode: settings.targetLanguageCode,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { OpenAiTranslateTargetLanguage } from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface OpenAiTranslateSettings extends NodeCallbacks {\n /** OpenAI API key. */\n apiKey: string;\n /**\n * Target language code for the translation output — one of the 13\n * languages `gpt-realtime-translate` currently supports.\n */\n targetLanguageCode: OpenAiTranslateTargetLanguage;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via OpenAI's `gpt-realtime-translate` model. */\nexport class OpenAiTranslateNode extends BaseNode {\n readonly settings: OpenAiTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: OpenAiTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createOpenAiTranslateNode(\n workflow: WorkflowHandle,\n settings: OpenAiTranslateSettings,\n nodeId?: string,\n): Promise<OpenAiTranslateNode> {\n const id = nodeId ?? `openai_translate_${uid(12)}`;\n const node = new OpenAiTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"OPENAI_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n openAiTranslate: {\n apiKey: settings.apiKey,\n targetLanguageCode: settings.targetLanguageCode,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { AppRouter, HealthStatus } from \"@remavideo/server\";\nimport {\n createTRPCClient,\n httpLink,\n httpSubscriptionLink,\n splitLink,\n} from \"@trpc/client\";\nimport { EventSource } from \"eventsource\";\nimport superjson from \"superjson\";\n\nexport interface RemaClientOptions {\n host?: string;\n port?: number;\n onLogEvent?: (log: { level: string; message: string }) => void;\n onReady?: () => void;\n onFailedToConnect?: () => void;\n}\n\nexport class RemaClient {\n readonly trpc: ReturnType<typeof createTRPCClient<AppRouter>>;\n readonly options: Required<Pick<RemaClientOptions, \"host\" | \"port\">>;\n\n private constructor(\n trpc: ReturnType<typeof createTRPCClient<AppRouter>>,\n options: RemaClientOptions,\n ) {\n this.trpc = trpc;\n this.options = {\n host: options.host ?? \"127.0.0.1\",\n port: options.port ?? 8080,\n };\n }\n\n static async connect(opts: RemaClientOptions = {}): Promise<RemaClient> {\n const host = opts.host ?? \"127.0.0.1\";\n const port = opts.port ?? 8080;\n const url = `http://${host}:${port}/trpc`;\n\n const trpc = createTRPCClient<AppRouter>({\n links: [\n splitLink({\n condition: (op) => op.type === \"subscription\",\n true: httpSubscriptionLink({\n url,\n transformer: superjson,\n EventSource,\n }),\n false: httpLink({ url, transformer: superjson }),\n }),\n ],\n });\n\n try {\n await trpc.ping.query();\n opts.onReady?.();\n } catch (err) {\n opts.onFailedToConnect?.();\n throw new Error(\n `Failed to connect to rema server at ${host}:${port}: ${String(err)}`,\n );\n }\n\n return new RemaClient(trpc, opts);\n }\n\n /** No-op kept for API compatibility — SSE has no persistent connection to close. */\n close(): void {}\n\n /** Fetches the server's health status from the plain HTTP `/api/health` endpoint. */\n async health(): Promise<HealthStatus> {\n const url = `http://${this.options.host}:${this.options.port}/api/health`;\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Health check failed: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as HealthStatus;\n }\n}\n","import type { NodeEventMsg, WorkflowInfo } from \"@remavideo/server\";\nimport { TypedEmitter } from \"tiny-typed-emitter\";\nimport type { RemaClient } from \"./client.js\";\nimport {\n type CaptionSourceNode,\n type CaptionSourceSettings,\n createCaptionSourceNode,\n} from \"./input/caption-source.js\";\nimport {\n createFileInputNode,\n type FileInputNode,\n type FileInputSettings,\n} from \"./input/file.js\";\nimport {\n createNodeStreamInputNode,\n type NodeStreamInputNode,\n type NodeStreamInputSettings,\n} from \"./input/node-stream.js\";\nimport {\n createRtmpAnnouncerNode,\n type RtmpAnnouncerNode,\n type RtmpAnnouncerSettings,\n} from \"./input/rtmp-announcer.js\";\nimport {\n createRtmpMultiplexerNode,\n type RtmpMultiplexerNode,\n type RtmpMultiplexerSettings,\n} from \"./input/rtmp-multiplexer.js\";\nimport {\n createRtmpReaderNode,\n type RtmpReaderNode,\n type RtmpReaderSettings,\n} from \"./input/rtmp-reader.js\";\nimport {\n createSrtAnnouncerNode,\n type SrtAnnouncerNode,\n type SrtAnnouncerSettings,\n} from \"./input/srt-announcer.js\";\nimport {\n createSrtReaderNode,\n type SrtReaderNode,\n type SrtReaderSettings,\n} from \"./input/srt-reader.js\";\nimport {\n createFileOutputNode,\n type FileOutputNode,\n type FileOutputSettings,\n} from \"./output/file.js\";\nimport {\n createHlsOutputNode,\n type HlsOutputNode,\n type HlsOutputSettings,\n} from \"./output/hls.js\";\nimport {\n createRtmpOutputNode,\n type RtmpOutputNode,\n type RtmpOutputSettings,\n} from \"./output/rtmp.js\";\nimport {\n createSrtOutputNode,\n type SrtOutputNode,\n type SrtOutputSettings,\n} from \"./output/srt.js\";\nimport {\n type AudioMixerNode,\n type AudioMixerSettings,\n createAudioMixerNode,\n} from \"./processor/audio-mixer.js\";\nimport {\n type AzureTranslateNode,\n type AzureTranslateSettings,\n createAzureTranslateNode,\n} from \"./processor/azure-translate.js\";\nimport {\n type BufferNode,\n type BufferSettings,\n createBufferNode,\n} from \"./processor/buffer.js\";\nimport {\n type CambAiTranslateNode,\n type CambAiTranslateSettings,\n createCambAiTranslateNode,\n} from \"./processor/camb-ai-translate.js\";\nimport {\n type Cea608EncoderNode,\n type Cea608EncoderSettings,\n createCea608EncoderNode,\n} from \"./processor/cea608-encoder.js\";\nimport {\n createDeepgramSttNode,\n type DeepgramSttNode,\n type DeepgramSttSettings,\n} from \"./processor/deepgram-stt.js\";\nimport {\n createFfprobeMonitorNode,\n type FfprobeMonitorNode,\n type FfprobeMonitorSettings,\n} from \"./processor/ffprobe-monitor.js\";\nimport {\n createGateNode,\n type GateNode,\n type GateSettings,\n} from \"./processor/gate.js\";\nimport {\n createGeminiTranslateNode,\n type GeminiTranslateNode,\n type GeminiTranslateSettings,\n} from \"./processor/gemini-translate.js\";\nimport {\n createOpenAiTranslateNode,\n type OpenAiTranslateNode,\n type OpenAiTranslateSettings,\n} from \"./processor/openai-translate.js\";\n\nexport type { WorkflowInfo };\n\n/** Normalize a `catch`-clause/subscription value (typed `unknown`) into an `Error`. */\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err));\n}\n\n/** Lifecycle callbacks available on a workflow. */\nexport interface WorkflowCallbacks {\n /**\n * Called if this workflow's worker process crashes. Workflow errors\n * otherwise only appear in server logs — this is the client-side signal.\n */\n onError?: (err: Error) => void;\n /** Called when the workflow closes, whether via `.close()` or a crash. */\n onClose?: () => void;\n}\n\n/** Events emitted by a workflow, mirroring {@link WorkflowCallbacks} for `.on()`-style consumers. */\nexport interface WorkflowEvents {\n error: (err: Error) => void;\n close: () => void;\n}\n\n/**\n * A workflow is an isolated sub-registry on the server.\n * All nodes created through a workflow handle are scoped to that workflow;\n * they cannot subscribe to nodes in other workflows.\n *\n * Obtain a workflow via `rema.workflow.create()`.\n */\nexport class Workflow extends TypedEmitter<WorkflowEvents> {\n readonly workflowId: string;\n /** Non-private: accessible to factory functions via WorkflowHandle. */\n readonly client: RemaClient;\n\n private readonly _callbacks: WorkflowCallbacks;\n private readonly _eventHandlers = new Map<\n string,\n Set<(evt: NodeEventMsg) => void>\n >();\n private _eventSub: { unsubscribe(): void } | null = null;\n private _stateSub: { unsubscribe(): void } | null = null;\n private _seenInList = false;\n private _erroredFired = false;\n private _closedFired = false;\n\n /** @internal — use `rema.workflow.create()` instead */\n constructor(\n workflowId: string,\n client: RemaClient,\n callbacks: WorkflowCallbacks = {},\n ) {\n super();\n this.workflowId = workflowId;\n this.client = client;\n this._callbacks = callbacks;\n this._startEventSubscription();\n this._startStateSubscription();\n }\n\n /**\n * Register a handler for events emitted by a specific node in this workflow.\n * Returns an unsubscribe function — call it when the node is closed.\n * @internal used by node factory functions\n */\n _onNodeEvent(\n nodeId: string,\n handler: (evt: NodeEventMsg) => void,\n ): () => void {\n let handlers = this._eventHandlers.get(nodeId);\n if (handlers === undefined) {\n handlers = new Set();\n this._eventHandlers.set(nodeId, handlers);\n }\n handlers.add(handler);\n return () => {\n this._eventHandlers.get(nodeId)?.delete(handler);\n };\n }\n\n private _startEventSubscription(): void {\n this._eventSub = this.client.trpc.workflow.events.subscribe(\n { workflowId: this.workflowId },\n {\n onData: (evt) => {\n this._eventHandlers.get(evt.nodeId)?.forEach((h) => {\n h(evt);\n });\n },\n onError: (err) => this._reportError(toError(err)),\n },\n );\n }\n\n /**\n * Watches the server's list of all workflows for this workflow's entry, so\n * `onError`/`onClose` can be derived without a dedicated per-workflow\n * subscription: a transition to `status: \"crashed\"` fires `onError`, and\n * the entry disappearing (graceful close) fires `onClose`.\n */\n private _startStateSubscription(): void {\n this._stateSub = this.client.trpc.workflow.state.subscribe(undefined, {\n onData: (workflows) => {\n const entry = workflows.find((w) => w.id === this.workflowId);\n if (entry !== undefined) {\n this._seenInList = true;\n if (entry.status === \"crashed\" && !this._erroredFired) {\n this._erroredFired = true;\n this._reportError(new Error(\"workflow worker crashed\"));\n }\n return;\n }\n if (this._seenInList) this._fireClose();\n },\n onError: (err) => this._reportError(toError(err)),\n });\n }\n\n /**\n * Report an error to this workflow's `onError` callback and its\n * `WorkflowEvents` emitter. Like `BaseNode`, `Workflow` extends Node's\n * `EventEmitter`, which throws synchronously from `emit()` when an\n * `\"error\"` event has no listener — so this only emits when someone has\n * opted in via `.on(\"error\", ...)`; the `onError` callback covers the\n * common case. The SDK does no logging of its own.\n */\n private _reportError(err: Error): void {\n this._callbacks.onError?.(err);\n if (this.listenerCount(\"error\") > 0) this.emit(\"error\", err);\n }\n\n private _fireClose(): void {\n if (this._closedFired) return;\n this._closedFired = true;\n this._eventSub?.unsubscribe();\n this._stateSub?.unsubscribe();\n this._callbacks.onClose?.();\n this.emit(\"close\");\n }\n\n // ─── Input nodes ─────────────────────────────────────────────────────────\n\n readonly input = {\n file: (\n settings: FileInputSettings,\n nodeId?: string,\n ): Promise<FileInputNode> => createFileInputNode(this, settings, nodeId),\n\n rtmpReader: (\n settings: RtmpReaderSettings,\n nodeId?: string,\n ): Promise<RtmpReaderNode> => createRtmpReaderNode(this, settings, nodeId),\n\n /** Announces RTMP publisher connections on an `app` without creating nodes. */\n rtmpAnnouncer: (\n settings: RtmpAnnouncerSettings,\n nodeId?: string,\n ): Promise<RtmpAnnouncerNode> =>\n createRtmpAnnouncerNode(this, settings, nodeId),\n\n /** Announces SRT publisher connections on an `app` without creating nodes. */\n srtAnnouncer: (\n settings: SrtAnnouncerSettings,\n nodeId?: string,\n ): Promise<SrtAnnouncerNode> =>\n createSrtAnnouncerNode(this, settings, nodeId),\n\n /** Reads an SRT stream ingested by MediaMTX, pulling it back via RTSP internally. */\n srtReader: (\n settings: SrtReaderSettings,\n nodeId?: string,\n ): Promise<SrtReaderNode> => createSrtReaderNode(this, settings, nodeId),\n\n /**\n * An RTMP multiplexer: accepts any number of simultaneous publishers under\n * the given `app`. For each connection `onStream` is called with a fresh\n * `RtmpStreamNode` that can be wired into its own encoder/output pipeline.\n */\n rtmpMultiplexer: (\n settings: RtmpMultiplexerSettings,\n nodeId?: string,\n ): Promise<RtmpMultiplexerNode> =>\n createRtmpMultiplexerNode(this, settings, nodeId),\n\n /** Format-agnostic caption schedule node. Send captions to it; wire it to a cea608() encoder. */\n captions: (\n settings: CaptionSourceSettings = {},\n nodeId?: string,\n ): Promise<CaptionSourceNode> =>\n createCaptionSourceNode(this, settings, nodeId),\n\n /**\n * Audio stream input node. Push a Node.js `Readable` (or any\n * `AsyncIterable<Buffer>`) directly into the pipeline by calling\n * `.stream(source)` on the returned node.\n */\n streamInput: (\n settings: NodeStreamInputSettings,\n nodeId?: string,\n ): Promise<NodeStreamInputNode> =>\n createNodeStreamInputNode(this, settings, nodeId),\n };\n\n // ─── Output nodes ─────────────────────────────────────────────────────────\n\n readonly output = {\n rtmp: (\n settings: RtmpOutputSettings,\n nodeId?: string,\n ): Promise<RtmpOutputNode> => createRtmpOutputNode(this, settings, nodeId),\n\n file: (\n settings: FileOutputSettings,\n nodeId?: string,\n ): Promise<FileOutputNode> => createFileOutputNode(this, settings, nodeId),\n\n /** HLS output node. Subscribe it to one or more media/subtitle sources. */\n hls: (\n settings: HlsOutputSettings,\n nodeId?: string,\n ): Promise<HlsOutputNode> => createHlsOutputNode(this, settings, nodeId),\n\n /** SRT output node. Pushes MPEG-TS over SRT to the given URL. */\n srt: (\n settings: SrtOutputSettings,\n nodeId?: string,\n ): Promise<SrtOutputNode> => createSrtOutputNode(this, settings, nodeId),\n };\n\n // ─── Processor nodes ──────────────────────────────────────────────────────\n\n readonly processor = {\n /** CEA-608/708 encoder node. Subscribe it to video + a captions() node. */\n cea608: (\n settings: Cea608EncoderSettings = {},\n nodeId?: string,\n ): Promise<Cea608EncoderNode> =>\n createCea608EncoderNode(this, settings, nodeId),\n\n /**\n * ffprobe monitor node.\n * Subscribe it to any media source; it periodically probes the live stream\n * and exposes codec/resolution/bitrate info via the `stream-info` monitor.\n */\n ffprobeMonitor: (\n settings: FfprobeMonitorSettings = {},\n nodeId?: string,\n ): Promise<FfprobeMonitorNode> =>\n createFfprobeMonitorNode(this, settings, nodeId),\n\n /**\n * Gemini Live Translate node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using the Gemini Live Translate API.\n */\n geminiTranslate: (\n settings: GeminiTranslateSettings,\n nodeId?: string,\n ): Promise<GeminiTranslateNode> =>\n createGeminiTranslateNode(this, settings, nodeId),\n\n /**\n * Camb.ai realtime speech-to-speech translation node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using the Camb.ai realtime translation API.\n */\n cambAiTranslate: (\n settings: CambAiTranslateSettings,\n nodeId?: string,\n ): Promise<CambAiTranslateNode> =>\n createCambAiTranslateNode(this, settings, nodeId),\n\n /**\n * OpenAI `gpt-realtime-translate` node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using OpenAI's realtime translation API.\n */\n openAiTranslate: (\n settings: OpenAiTranslateSettings,\n nodeId?: string,\n ): Promise<OpenAiTranslateNode> =>\n createOpenAiTranslateNode(this, settings, nodeId),\n\n /**\n * Azure AI Speech real-time translation node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using Azure Speech's real-time translation + synthesis API.\n */\n azureTranslate: (\n settings: AzureTranslateSettings,\n nodeId?: string,\n ): Promise<AzureTranslateNode> =>\n createAzureTranslateNode(this, settings, nodeId),\n\n /**\n * Deepgram real-time speech-to-text node. Subscribe it to an\n * audio/media source; it exposes the resulting transcripts as timed\n * captions — feed that into `processor.cea608()` (`sourceSelector:\n * \"CAPTIONS\"`) for burned-in captions, or select it as an HLS subtitle\n * track, exactly like `input.captions()`.\n */\n deepgramStt: (\n settings: DeepgramSttSettings,\n nodeId?: string,\n ): Promise<DeepgramSttNode> =>\n createDeepgramSttNode(this, settings, nodeId),\n\n /**\n * Time-shift node. Subscribe it to exactly one source; it re-emits that\n * source delayed by `bufferMs`, shared across every downstream\n * subscriber.\n */\n buffer: (settings: BufferSettings, nodeId?: string): Promise<BufferNode> =>\n createBufferNode(this, settings, nodeId),\n\n /**\n * Synchronization barrier for fan-in from multiple, independently-timed\n * sources. Subscribe it to two or more sources; it withholds all of them\n * until enough (`minReady`, default: all) have produced their first\n * item, then forwards everything live.\n */\n gate: (settings: GateSettings = {}, nodeId?: string): Promise<GateNode> =>\n createGateNode(this, settings, nodeId),\n\n /**\n * Audio mixer node. Subscribe it to exactly `settings.audio.length`\n * AUDIO/ALL sources, in order — the Nth `audio[]` entry configures the\n * Nth subscribed source. Outputs a single mixed audio feed.\n */\n audioMixer: (\n settings: AudioMixerSettings,\n nodeId?: string,\n ): Promise<AudioMixerNode> => createAudioMixerNode(this, settings, nodeId),\n };\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n /**\n * Close this workflow and deactivate all its nodes on the server.\n * After calling this the workflow handle is no longer usable.\n *\n * The event/state subscriptions are kept open until the close mutation\n * resolves so that the server's per-node `CLOSED` events (dispatched to\n * each node's `onClose`/`onStatusChange` callbacks) are still received,\n * rather than tearing the subscriptions down before those events arrive.\n */\n async close(): Promise<void> {\n await this.client.trpc.workflow.close.mutate({\n workflowId: this.workflowId,\n });\n this._fireClose();\n }\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Per-source settings for one audio input to an `AudioMixerNode`. */\nexport interface AudioMixerInputSettings {\n /** Linear gain multiplier (1 = unity, 0.5 = -6dB). Default: 1. */\n volume?: number;\n /** Silences this input entirely, regardless of `volume`. Default: false. */\n muted?: boolean;\n /** Stereo balance: -1 (full left) .. 1 (full right), 0 = center. Default: 0. */\n pan?: number;\n /** High-pass filter cutoff (Hz), applied before mixing. Omit for no high-pass. */\n highpassHz?: number;\n /** Low-pass filter cutoff (Hz), applied before mixing. Omit for no low-pass. */\n lowpassHz?: number;\n}\n\nexport interface AudioMixerSettings extends NodeCallbacks {\n /**\n * One entry per audio source this node will be subscribed to, in the same\n * order — the Nth entry configures the Nth subscribed AUDIO/ALL source.\n * Subscribe with exactly `audio.length` sources.\n */\n audio: AudioMixerInputSettings[];\n}\n\nexport class AudioMixerNode extends BaseNode {\n readonly settings: AudioMixerSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: AudioMixerSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createAudioMixerNode(\n workflow: WorkflowHandle,\n settings: AudioMixerSettings,\n nodeId?: string,\n): Promise<AudioMixerNode> {\n const id = nodeId ?? `audio_mixer_${uid(12)}`;\n const node = new AudioMixerNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"AUDIO_MIXER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n audioMixer: {\n audio: settings.audio.map((a) => ({\n volume: a.volume ?? 1,\n muted: a.muted ?? false,\n pan: a.pan ?? 0,\n ...(a.highpassHz !== undefined ? { highpassHz: a.highpassHz } : {}),\n ...(a.lowpassHz !== undefined ? { lowpassHz: a.lowpassHz } : {}),\n })),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { HealthStatus, WorkflowInfo } from \"@remavideo/server\";\nimport { RemaClient, type RemaClientOptions } from \"./client.js\";\nimport { Workflow, type WorkflowCallbacks } from \"./workflow.js\";\n\nexport class Rema {\n private readonly client: RemaClient;\n private readonly _autoCloseWorkflows = new Set<Workflow>();\n\n private constructor(client: RemaClient) {\n this.client = client;\n }\n\n static async connect(opts: RemaClientOptions = {}): Promise<Rema> {\n const client = await RemaClient.connect(opts);\n return new Rema(client);\n }\n\n // ─── Workflow management ──────────────────────────────────────────────────\n\n readonly workflow = {\n /**\n * Create a new isolated workflow on the server.\n *\n * All nodes created through the returned `Workflow` handle are scoped to\n * this workflow; they cannot subscribe to nodes in other workflows.\n *\n * @param opts.name Human-readable label shown in the web UI.\n * @param opts.workflowId Optional stable ID. Server generates one if omitted.\n * @param opts.closeOnDisconnect When true, this workflow is closed automatically\n * when `rema.close()`/`wf.close()` is called, or\n * when the connection to the server is lost (e.g.\n * the process crashes) for more than a few seconds.\n * @param opts.parentId ID of an existing workflow to attach this one to as\n * a child (shown nested in the web UI).\n * @param opts.closeWithParent When true, this workflow is closed automatically\n * when its parent workflow is closed.\n * @param opts.onError Called if this workflow's worker process crashes.\n * @param opts.onClose Called when the workflow closes, whether via\n * `.close()` or a crash.\n */\n create: async (\n opts: {\n name?: string;\n workflowId?: string;\n closeOnDisconnect?: boolean;\n parentId?: string;\n closeWithParent?: boolean;\n } & WorkflowCallbacks = {},\n ): Promise<Workflow> => {\n const info = await this.client.trpc.workflow.create.mutate({\n workflowId: opts.workflowId,\n name: opts.name,\n parentId: opts.parentId,\n closeWithParent: opts.closeWithParent,\n closeOnDisconnect: opts.closeOnDisconnect,\n });\n const wf = new Workflow(info.id, this.client, {\n ...(opts.onError !== undefined && { onError: opts.onError }),\n ...(opts.onClose !== undefined && { onClose: opts.onClose }),\n });\n if (opts.closeOnDisconnect === true) {\n this._autoCloseWorkflows.add(wf);\n }\n return wf;\n },\n\n /** List all active workflows on the server. */\n list: (): Promise<WorkflowInfo[]> => this.client.trpc.workflow.list.query(),\n };\n\n /** Fetches the server's current health status (uptime, workflow counts, MediaMTX reachability). */\n health(): Promise<HealthStatus> {\n return this.client.health();\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n /**\n * Close all `closeOnDisconnect` workflows, then disconnect from the server.\n */\n async close(): Promise<void> {\n await Promise.allSettled(\n [...this._autoCloseWorkflows].map((wf) => wf.close()),\n );\n this.client.close();\n }\n}\n","/**\n * Stream selectors — used when calling node.subscribe() to pick which\n * streams flow from source to subscriber.\n */\n\nexport type StreamSelector = \"VIDEO\" | \"AUDIO\" | \"CAPTIONS\" | \"ALL\";\n\n// Narrowly typed (`as const`) so a spec literal like\n// `{ sourceSelector: selectAudio, audio: {...} }` discriminates the\n// SubscriptionSpec union correctly.\nexport const selectVideo = \"VIDEO\" as const;\nexport const selectAudio = \"AUDIO\" as const;\nexport const selectCaptions = \"CAPTIONS\" as const;\nexport const selectAll = \"ALL\" as const;\n"],"mappings":"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","names":["uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","TypedEmitter","uid","uid","toError","TypedEmitter"]}
|
|
1
|
+
{"version":3,"sources":["../src/input/caption-source.ts","../src/node.ts","../src/input/file.ts","../src/input/node-stream.ts","../src/input/rtmp-announcer.ts","../src/input/rtmp-multiplexer.ts","../src/input/rtmp-reader.ts","../src/input/srt-announcer.ts","../src/input/srt-reader.ts","../src/output/file.ts","../src/output/hls.ts","../src/output/hls-renditions.ts","../src/output/rtmp.ts","../src/output/srt.ts","../src/processor/azure-translate.ts","../src/processor/buffer.ts","../src/processor/camb-ai-translate.ts","../src/processor/cea608-encoder.ts","../src/processor/deepgram-stt.ts","../src/processor/ffprobe-monitor.ts","../src/processor/gate.ts","../src/processor/gemini-translate.ts","../src/processor/openai-translate.ts","../src/client.ts","../src/workflow.ts","../src/processor/audio-mixer.ts","../src/rema.ts","../src/selectors.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface CaptionSourceSettings extends NodeCallbacks {\n /**\n * How many past caption entries to replay to an encoder that subscribes\n * after some captions have already been sent.\n *\n * - `0` (default) — no replay; each encoder only receives captions sent\n * after it connects. Correct for live captioning where old text is stale.\n * - `Infinity` — replay the full history. Use this when pre-scheduling\n * all captions upfront (e.g. from an SRT file) before the pipeline starts,\n * so encoders that activate later still see every entry.\n * - `N` — replay the last N entries only.\n */\n replayCount?: number;\n /**\n * BCP-47 language tag written into the HLS master playlist when this caption\n * source is wired as a subtitle track. Default: \"und\".\n */\n language?: string;\n /** Human-readable track label shown in player subtitle menus. Default: \"Subtitles\". */\n label?: string;\n /** Whether this track is pre-selected when the player first loads. Default: true. */\n defaultTrack?: boolean;\n /**\n * Maximum character length per caption line. When set, captions whose text\n * exceeds this length are automatically split into sequential chunks on the\n * server. Each chunk's display duration is proportional to its character\n * count relative to the original caption's total duration.\n */\n maxLineLength?: number;\n}\n\nexport class CaptionSourceNode extends BaseNode {\n readonly settings: CaptionSourceSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: CaptionSourceSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /**\n * Queue caption text for injection into the video stream.\n *\n * @param options.startAt Stream-elapsed milliseconds (relative to the\n * first video frame received by the server) at which\n * to show the caption. The server holds the text and\n * injects it at the matching PTS, so you can schedule\n * captions before that point in the stream is reached.\n * Omit (or 0) to inject immediately at the next frame.\n * @param options.duration How long (ms) the caption stays visible before\n * being automatically erased. Required.\n */\n async send(\n text: string,\n options: { duration: number; startAt?: number },\n ): Promise<void> {\n await this.client.trpc.sendCaption.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n text,\n durationMs: options.duration,\n startAtMs: options.startAt ?? 0,\n });\n }\n\n /**\n * Parse raw SRT or WebVTT content and schedule all entries on the server.\n *\n * Use `replayCount: Infinity` when creating this node if the pipeline may\n * connect encoders after the captions are loaded.\n *\n * @returns The number of caption entries loaded.\n */\n async sendFile(content: string): Promise<{ count: number }> {\n return this.client.trpc.loadCaptionFile.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n content,\n });\n }\n\n /**\n * Read a local SRT or WebVTT file and schedule all its entries on the server.\n * The file is read client-side; only the content string is sent over the wire.\n *\n * @param filePath Absolute or relative path to the subtitle file.\n * @returns The number of caption entries loaded.\n */\n async sendFileByPath(filePath: string): Promise<{ count: number }> {\n const content = await readFile(filePath, \"utf-8\");\n return this.sendFile(content);\n }\n}\n\nexport async function createCaptionSourceNode(\n workflow: WorkflowHandle,\n settings: CaptionSourceSettings = {},\n nodeId?: string,\n): Promise<CaptionSourceNode> {\n const id = nodeId ?? `caption_source_${Date.now()}`;\n const node = new CaptionSourceNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"CAPTION_SOURCE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n captionSource: {\n replayCount: settings.replayCount ?? 0,\n language: settings.language ?? \"und\",\n label: settings.label ?? \"Subtitles\",\n defaultTrack: settings.defaultTrack ?? true,\n ...(settings.maxLineLength !== undefined && {\n maxLineLength: settings.maxLineLength,\n }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type {\n ActivationGate,\n NodeEventMsg,\n NodeState,\n TrackInfo,\n} from \"@remavideo/server\";\nimport { TypedEmitter } from \"tiny-typed-emitter\";\nimport type { RemaClient } from \"./client.js\";\n\n/** Lifecycle callbacks available on every node's settings object. */\nexport interface NodeCallbacks {\n /** Called when the node transitions into the `ERROR` state. */\n onError?: (err: Error) => void;\n /** Called on every lifecycle state transition (`READY`, `ACTIVE`, `ENDED`, `ERROR`, `CLOSED`). */\n onStatusChange?: (state: NodeState, message?: string) => void;\n /** Called when the node closes — locally via `.close()`, or remotely (workflow close, crash). */\n onClose?: () => void;\n}\n\n/** Events emitted by every node, mirroring {@link NodeCallbacks} for `.on()`-style consumers. */\nexport interface NodeEvents {\n error: (err: Error) => void;\n statusChange: (state: NodeState, message?: string) => void;\n close: () => void;\n}\n\nconst NODE_STATES = new Set<NodeState>([\n \"READY\",\n \"ACTIVE\",\n \"ENDED\",\n \"ERROR\",\n \"CLOSED\",\n]);\n\n/**\n * Report an error to a node's `onError` callback and its `NodeEvents`\n * emitter. The SDK does no logging of its own — the caller decides whether\n * and how an unhandled error surfaces, via `onError` or `.on(\"error\", ...)`.\n *\n * `BaseNode` extends Node's `EventEmitter` (via `tiny-typed-emitter`), which\n * throws synchronously from `emit()` when an `\"error\"` event has no\n * listener. Since callers typically only set the `onError` callback (not\n * `.on(\"error\", ...)`), emitting unconditionally would throw back into\n * whatever triggered this call — e.g. the `workflow.events` subscription's\n * `onData` handler, tearing down event delivery for the entire workflow.\n * Only emit when someone has opted in.\n */\nexport function reportNodeError(\n node: BaseNode,\n callbacks: NodeCallbacks,\n err: Error,\n): void {\n callbacks.onError?.(err);\n if (node.listenerCount(\"error\") > 0) node.emit(\"error\", err);\n}\n\n/**\n * Dispatch a raw node event to the generic lifecycle callbacks on a node's\n * settings object, and to the node's `NodeEvents` emitter. Call this from a\n * node factory's event handler alongside any node-type-specific event handling.\n */\nexport function dispatchNodeCallbacks(\n node: BaseNode,\n evt: NodeEventMsg,\n callbacks: NodeCallbacks,\n): void {\n if (NODE_STATES.has(evt.kind as NodeState)) {\n const state = evt.kind as NodeState;\n callbacks.onStatusChange?.(state, evt.message);\n node.emit(\"statusChange\", state, evt.message);\n }\n if (evt.kind === \"ERROR\") {\n reportNodeError(node, callbacks, new Error(evt.message ?? \"node error\"));\n }\n if (evt.kind === \"CLOSED\") {\n callbacks.onClose?.();\n node.emit(\"close\");\n }\n}\n\n/**\n * FFmpeg codec specification for one stream of a subscription.\n *\n * The receiving output node applies it where appropriate (e.g. per-rendition\n * `-c:v:N`/`-c:a:N` for HLS, `-c:v`/`-c:a` for RTMP/File outputs). Omitted =\n * stream copy.\n */\nexport interface CodecSpec {\n /** FFmpeg encoder name: `\"copy\"`, `\"aac\"`, `\"libx264\"`, … */\n name: string;\n /**\n * Extra encoder options keyed by bare FFmpeg option name (no stream\n * specifier), e.g. `{ preset: \"veryfast\", b: \"3M\" }`.\n */\n options?: Record<string, string | number>;\n}\n\n/**\n * Audio rendition metadata for HLS output nodes. Sets the language tag and\n * label that appear in the player's track menu. Ignored by non-HLS nodes.\n */\nexport interface AudioRenditionOptions {\n /** BCP-47 language tag, e.g. `\"en\"`, `\"fr\"`. Default: `\"und\"`. */\n language?: string;\n /** Label shown in player track menus. Default: `\"Audio\"`. */\n label?: string;\n /** Pre-select this track on first play. Default: `false`. */\n defaultTrack?: boolean;\n}\n\n/**\n * Video rendition metadata for HLS output nodes. Ignored by non-HLS nodes.\n */\nexport interface VideoRenditionOptions {\n /**\n * Label used to name the HLS variant playlist/segments (sanitized to a\n * URL-safe variant name).\n */\n label?: string;\n}\n\n/**\n * One rung of an HLS adaptive-bitrate (ABR) ladder — an independent\n * resolution/bitrate tier transcoded from the same video source, wired via\n * `VideoSubscriptionSpec.additionalRenditions` / `AllSubscriptionSpec.additionalRenditions`.\n * HLS-only; ignored by non-HLS output nodes.\n */\nexport interface VideoRendition {\n /**\n * Output height in pixels. Also derives the default label (`\"{height}p\"`)\n * and, when `width` is omitted, an aspect-preserving `scale=-2:height`\n * filter.\n */\n height: number;\n /** Output width in pixels. Omit to preserve the source aspect ratio. */\n width?: number;\n /** Target video bitrate — ffmpeg bitrate syntax (`\"3M\"`) or bits/sec. */\n bitrate: string | number;\n /** VBV maxrate. Default: same as `bitrate`. */\n maxrate?: string | number;\n /** VBV bufsize. Default: 2x `bitrate`. */\n bufsize?: string | number;\n /** libx264 preset. Default: `\"veryfast\"`. */\n preset?: string;\n /**\n * Encoder name override — escape hatch for hardware encoders (e.g.\n * `\"h264_videotoolbox\"`, `\"h264_nvenc\"`). Default: `\"libx264\"`. No\n * server-side hardware-encoder plumbing exists; passed through as-is.\n */\n codecName?: string;\n /**\n * Variant label. Default: `\"{height}p\"`. Must be unique across the\n * ladder — colliding labels are auto-suffixed server-side.\n */\n label?: string;\n /**\n * Extra raw codec options, merged in last — overrides any default above\n * (including the keyframe-alignment `force_key_frames` default).\n */\n extraOptions?: Record<string, string | number>;\n}\n\n/** Fields shared by every subscription variant. */\ninterface SubscriptionSpecBase {\n source: BaseNode;\n /**\n * Delay this stream by N milliseconds before it reaches the subscribing\n * node (applied server-side). Typical use: delay the A/V subscriptions so\n * a slower caption source has lead time.\n */\n delayMs?: number;\n /**\n * Read from a keyed output port of a multi-output source node (Gate).\n * Ports are named by the `name` field of the specs the gate itself was\n * subscribed with. Mutually exclusive with `track`.\n */\n port?: string;\n /**\n * When the *subscribing* node is a multi-output node (Gate), names the\n * pass-through port it creates for this source (default: the source's\n * index as a string). Ignored by other node types.\n */\n name?: string;\n}\n\n/** Subscribe to a video-only stream of a source node. */\nexport interface VideoSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"VIDEO\";\n /**\n * Per-type video track index within the source container (0 = first video\n * track). Discover available indices with `await source.tracks()`.\n * Default: 0.\n */\n track?: number;\n /** Codec applied to the video stream by the receiving output node. */\n codec?: CodecSpec;\n}\n\n/** Subscribe to an audio-only stream of a source node. */\nexport interface AudioSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"AUDIO\";\n /**\n * Per-type audio track index within the source container (0 = first audio\n * track). Discover available indices with `await source.tracks()`.\n * Default: 0.\n */\n track?: number;\n /** Codec applied to the audio stream by the receiving output node. */\n codec?: CodecSpec;\n}\n\n/** Subscribe to the combined A/V stream of a source node. */\nexport interface AllSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"ALL\";\n /** Codec applied to the video track by the receiving output node. */\n videoCodec?: CodecSpec;\n /** Codec applied to the audio track by the receiving output node. */\n audioCodec?: CodecSpec;\n}\n\n/** Subscribe to the caption stream of a source node. */\nexport interface CaptionsSubscriptionSpec extends SubscriptionSpecBase {\n sourceSelector: \"CAPTIONS\";\n}\n\n/**\n * One source wired into a subscribing node, discriminated by\n * `sourceSelector` so per-stream options (codec) are only available where\n * they apply. Rendition-metadata fields (`video`/`audio`/\n * `additionalRenditions`/`omitOriginalRendition`) are HLS-only and live on\n * {@link HlsSubscriptionSpec} instead, since only `HlsOutputNode.subscribe()`\n * consumes them.\n */\nexport type SubscriptionSpec =\n | VideoSubscriptionSpec\n | AudioSubscriptionSpec\n | AllSubscriptionSpec\n | CaptionsSubscriptionSpec;\n\n/** Subscribe to a video-only stream of a source node, for an HLS output. */\nexport interface HlsVideoSubscriptionSpec extends VideoSubscriptionSpec {\n /** Video rendition metadata (HLS). */\n video?: VideoRenditionOptions;\n /**\n * Additional ABR ladder tiers transcoded from this same source, each\n * becoming its own HLS variant alongside this subscription's own\n * rendition.\n */\n additionalRenditions?: VideoRendition[];\n /**\n * When true, this subscription's own rendition (this `codec`/`video`) is\n * NOT subscribed as a variant — only `additionalRenditions` are.\n */\n omitOriginalRendition?: boolean;\n}\n\n/** Subscribe to an audio-only stream of a source node, for an HLS output. */\nexport interface HlsAudioSubscriptionSpec extends AudioSubscriptionSpec {\n /** Audio rendition metadata (HLS). */\n audio?: AudioRenditionOptions;\n}\n\n/** Subscribe to the combined A/V stream of a source node, for an HLS output. */\nexport interface HlsAllSubscriptionSpec extends AllSubscriptionSpec {\n /** Video rendition metadata (HLS). */\n video?: VideoRenditionOptions;\n /** Audio rendition metadata (HLS). */\n audio?: AudioRenditionOptions;\n /**\n * Additional ABR ladder tiers transcoded from this same source, each\n * becoming its own video-only HLS variant alongside this subscription's\n * own combined A/V rendition.\n *\n * `omitOriginalRendition: true` on an `ALL` spec also drops this\n * subscription's own audio (since `ALL` bundles audio+video) — subscribe\n * audio separately (`sourceSelector: \"AUDIO\"`) if you still need it.\n */\n additionalRenditions?: VideoRendition[];\n /**\n * When true, this subscription's own combined rendition is NOT\n * subscribed — only `additionalRenditions` are. See the audio caveat\n * above.\n */\n omitOriginalRendition?: boolean;\n}\n\n/**\n * One source wired into an {@link HlsOutputNode}, adding rendition-metadata\n * fields (`video`/`audio`/`additionalRenditions`/`omitOriginalRendition`) on\n * top of the common {@link SubscriptionSpec} — these only type-check here\n * since only `HlsOutputNode.subscribe()` consumes them; every other node's\n * `subscribe()` uses the plain `SubscriptionSpec` and rejects them at\n * compile time.\n */\nexport type HlsSubscriptionSpec =\n | HlsVideoSubscriptionSpec\n | HlsAudioSubscriptionSpec\n | HlsAllSubscriptionSpec\n | CaptionsSubscriptionSpec;\n\n/**\n * Minimal interface passed to factory functions. Defined here (not in workflow.ts)\n * to avoid a circular import: workflow.ts imports factory files, factory files\n * import this interface.\n */\nexport interface WorkflowHandle {\n readonly workflowId: string;\n readonly client: RemaClient;\n _onNodeEvent(\n nodeId: string,\n handler: (evt: NodeEventMsg) => void,\n ): () => void;\n}\n\n/**\n * Base class for all rema nodes.\n * Provides the .subscribe() method that wires nodes together on the server,\n * and a `NodeEvents` emitter (`.on(\"error\"/\"statusChange\"/\"close\", ...)`) as\n * an alternative to the `onError`/`onStatusChange`/`onClose` settings callbacks.\n */\nexport abstract class BaseNode extends TypedEmitter<NodeEvents> {\n readonly nodeId: string;\n readonly workflowId: string;\n protected readonly client: RemaClient;\n private _eventCleanup: (() => void) | null = null;\n private _callbacks: NodeCallbacks = {};\n\n constructor(nodeId: string, client: RemaClient, workflowId: string) {\n super();\n this.nodeId = nodeId;\n this.workflowId = workflowId;\n this.client = client;\n }\n\n /** Called by create functions to register the workflow event handler cleanup. */\n _setEventCleanup(fn: () => void): void {\n this._eventCleanup = fn;\n }\n\n /**\n * Called by create functions to register the node's lifecycle callbacks\n * (`onError`/`onStatusChange`/`onClose`), so `.close()` can invoke `onClose`\n * directly on a locally-initiated close.\n */\n _bindCallbacks(callbacks: NodeCallbacks): void {\n this._callbacks = callbacks;\n }\n\n /**\n * Subscribe this node to one or more source nodes.\n * This tells the server to route streams from the sources into this node.\n */\n async subscribe(\n specs: SubscriptionSpec[],\n options?: { gate?: ActivationGate },\n ): Promise<void> {\n await this.client.trpc.subscribeNode.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n sources: specs.map((s) => BaseNode._toSourceInput(s)),\n gate: options?.gate,\n });\n }\n\n /**\n * Map one source spec to the wire shape expected by the server. Typed to\n * accept {@link HlsSubscriptionSpec} (a superset of {@link SubscriptionSpec})\n * since it serves both `BaseNode.subscribe` and `HlsOutputNode.subscribe`.\n */\n private static _toSourceInput(spec: HlsSubscriptionSpec) {\n const base = {\n sourceNodeId: spec.source.nodeId,\n ...(spec.delayMs !== undefined ? { delayMs: spec.delayMs } : {}),\n ...(spec.port !== undefined ? { port: spec.port } : {}),\n ...(spec.name !== undefined ? { name: spec.name } : {}),\n };\n const audio = (a: AudioRenditionOptions) => ({\n language: a.language ?? \"und\",\n label: a.label ?? \"Audio\",\n defaultTrack: a.defaultTrack ?? false,\n });\n // `video` is only sent when a label is provided — it is the only field\n // and is required on the wire.\n const video = (v: VideoRenditionOptions) =>\n v.label !== undefined ? { video: { label: v.label } } : {};\n\n switch (spec.sourceSelector) {\n case \"CAPTIONS\":\n return { ...base, selector: \"CAPTIONS\" as const };\n case \"VIDEO\":\n return {\n ...base,\n selector: \"VIDEO\" as const,\n ...(spec.track !== undefined ? { track: spec.track } : {}),\n ...(spec.codec !== undefined ? { codec: spec.codec } : {}),\n ...(spec.video !== undefined ? video(spec.video) : {}),\n };\n case \"AUDIO\":\n return {\n ...base,\n selector: \"AUDIO\" as const,\n ...(spec.track !== undefined ? { track: spec.track } : {}),\n ...(spec.codec !== undefined ? { codec: spec.codec } : {}),\n ...(spec.audio !== undefined ? { audio: audio(spec.audio) } : {}),\n };\n case \"ALL\":\n return {\n ...base,\n selector: \"ALL\" as const,\n ...(spec.videoCodec !== undefined\n ? { videoCodec: spec.videoCodec }\n : {}),\n ...(spec.audioCodec !== undefined\n ? { audioCodec: spec.audioCodec }\n : {}),\n ...(spec.video !== undefined ? video(spec.video) : {}),\n ...(spec.audio !== undefined ? { audio: audio(spec.audio) } : {}),\n };\n }\n }\n\n /**\n * Probe this node's media stream (ffprobe, server-side) and return its\n * track list. Each entry's `track` is the per-type index to pass back as\n * `track` in a VIDEO/AUDIO subscription:\n *\n * ```ts\n * const tracks = await input.tracks();\n * const ita = tracks.find((t) => t.type === \"audio\" && t.language === \"ita\");\n * await output.subscribe([\n * { source: input, sourceSelector: \"VIDEO\" },\n * { source: input, sourceSelector: \"AUDIO\", track: ita!.track },\n * ]);\n * ```\n *\n * Probing consumes real-time-paced stream data, so it can take several\n * seconds; `probeSizeBytes` (default 1 MB) trades discovery depth for\n * latency. The server errors after ~15 s if the source produces no data\n * (e.g. no publisher connected yet, or a disabled file input).\n */\n async tracks(options?: { probeSizeBytes?: number }): Promise<TrackInfo[]> {\n return this.client.trpc.probeTracks.query({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n ...(options?.probeSizeBytes !== undefined\n ? { probeSizeBytes: options.probeSizeBytes }\n : {}),\n });\n }\n\n /** Tear down this node on the server. */\n async close(): Promise<void> {\n this._eventCleanup?.();\n await this.client.trpc.closeNode.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n });\n this._callbacks.onClose?.();\n this.emit(\"close\");\n }\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface FileInputSettings extends NodeCallbacks {\n /** Absolute path to the media file (MP4, TS, MKV, …) */\n fileName: string;\n /** Loop the file indefinitely. Default: false */\n loop?: boolean;\n /** Source name used to identify streams from this node */\n sourceName?: string;\n /**\n * Start in a disabled state — the file is not read until `.enable()` is called\n * (or the node is enabled from the web UI). Default: false.\n */\n disabled?: boolean;\n /** Called when the node transitions to the enabled / ready state */\n onReady?: () => void;\n /**\n * Called once when the first data packet from this input arrives at the server.\n * Use this to convert absolute timestamps to the stream-relative `startAt`\n * expected by `CaptionSourceNode.send()`:\n *\n * ```ts\n * onStreamStart(wallClockMs) {\n * const startAt = myAbsoluteCaptionMs - wallClockMs;\n * await captions.send(text, { startAt });\n * }\n * ```\n *\n * @param wallClockMs `Date.now()` on the server at the moment the first\n * MPEG-TS data packet arrived from this input.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called once the file has finished playing (reaches EOF). Never fires when `loop` is true. */\n onEnded?: () => void;\n}\n\nexport class FileInputNode extends BaseNode {\n readonly settings: FileInputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: FileInputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /** Enable this node — starts reading the file and streaming to downstream nodes. */\n async enable(): Promise<void> {\n await this.client.trpc.enableNode.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n });\n }\n}\n\nexport async function createFileInputNode(\n workflow: WorkflowHandle,\n settings: FileInputSettings,\n nodeId?: string,\n): Promise<FileInputNode> {\n const id = nodeId ?? `file_input_${uid(12)}`;\n const node = new FileInputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") settings.onReady?.();\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined)\n settings.onStreamStart?.(Number(evt.message));\n if (evt.kind === \"ENDED\") settings.onEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"FILE_INPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n fileInput: {\n fileName: settings.fileName,\n loop: settings.loop ?? false,\n sourceName: settings.sourceName,\n disabled: settings.disabled ?? false,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface NodeStreamInputSettings extends NodeCallbacks {\n /**\n * FFmpeg format identifier for the incoming audio data.\n *\n * Raw PCM examples: `\"s16le\"` (16-bit signed LE), `\"f32le\"` (32-bit float LE).\n * Encoded examples: `\"wav\"`, `\"mp3\"`, `\"aac\"`, `\"flac\"`.\n */\n format: string;\n /** Sample rate in Hz. Required when `format` is a raw PCM format. */\n sampleRate?: number;\n /** Number of audio channels. Required when `format` is a raw PCM format. */\n channels?: number;\n /**\n * FFmpeg codec for the MPEG-TS output. Defaults to `\"aac\"`, which is safe\n * for any input format. Use `\"copy\"` only when the input is already\n * MPEG-TS-compatible AAC.\n */\n audioCodec?: string;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n /** Called when the node transitions to the ready state on the server. */\n onReady?: () => void;\n /**\n * Called once when the first audio packet is written to the server.\n * The argument is `Date.now()` on the server at that moment.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called once the underlying FFmpeg process exits cleanly after `end()`. */\n onEnded?: () => void;\n}\n\nexport class NodeStreamInputNode extends BaseNode {\n readonly settings: NodeStreamInputSettings;\n private readonly _apiBase: string;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: NodeStreamInputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n const { host, port } = workflow.client.options;\n this._apiBase = `http://${host}:${port}`;\n }\n\n /**\n * Push an audio stream to the server.\n *\n * Accepts any `AsyncIterable<Buffer | Uint8Array>`, which includes Node.js\n * `Readable` streams (async-iterable since Node 10) and async generators.\n * The call resolves when the source iterable is exhausted or the server\n * closes the connection.\n *\n * @example\n * ```ts\n * import { createReadStream } from \"node:fs\";\n * await audioInput.stream(createReadStream(\"audio.mp3\"));\n * ```\n *\n * @example\n * ```ts\n * // Raw PCM from a microphone via node-record-lpcm16\n * const mic = recorder.start({ sampleRate: 16000, channels: 1 });\n * await audioInput.stream(mic);\n * ```\n */\n async stream(source: AsyncIterable<Buffer | Uint8Array>): Promise<void> {\n const url = `${this._apiBase}/api/node-stream/${this.workflowId}/${this.nodeId}`;\n\n const body = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n for await (const chunk of source) {\n controller.enqueue(\n chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk),\n );\n }\n controller.close();\n } catch (err) {\n controller.error(err);\n }\n },\n });\n\n await fetch(url, {\n method: \"POST\",\n body,\n // Required for streaming request bodies in Node.js 18+ native fetch.\n duplex: \"half\",\n } as RequestInit);\n }\n}\n\nexport async function createNodeStreamInputNode(\n workflow: WorkflowHandle,\n settings: NodeStreamInputSettings,\n nodeId?: string,\n): Promise<NodeStreamInputNode> {\n const id = nodeId ?? `node_stream_${uid(12)}`;\n const node = new NodeStreamInputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") settings.onReady?.();\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined)\n settings.onStreamStart?.(Number(evt.message));\n if (evt.kind === \"ENDED\") settings.onEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"NODE_STREAM_INPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n nodeStreamInput: {\n format: settings.format,\n ...(settings.sampleRate !== undefined && {\n sampleRate: settings.sampleRate,\n }),\n ...(settings.channels !== undefined && {\n channels: settings.channels,\n }),\n audioCodec: settings.audioCodec ?? \"aac\",\n ...(settings.label !== undefined && { label: settings.label }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Connection info passed to `onConnect` and `onDisconnect`. */\nexport interface RtmpConnectionInfo {\n app: string;\n sourceName: string;\n /**\n * MediaMTX connection id for this publisher, if it could be resolved at\n * connect time. Pass directly to `operations.disconnectSource`.\n */\n connectionId?: string;\n}\n\n/** Operations available on a live RTMP publisher connection. */\nexport interface RtmpAnnouncerOperations {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Safe to\n * call even if it already disconnected (resolves as a no-op).\n */\n disconnectSource(connectionId: string): Promise<void>;\n}\n\nexport interface RtmpAnnouncerSettings extends NodeCallbacks {\n /** RTMP app name to watch (default: \"live\"). */\n app?: string;\n /**\n * Silently kick the first N connections for each `sourceName` — never\n * calling `onConnect`/`onDisconnect` for them — before letting the\n * (N+1)th through. Useful for publishers whose stream is incomplete for\n * the first few seconds of a connection. Resets once a connection is let\n * through, so the next new session for this `sourceName` gets the same\n * warm-up. Default: 0 (disabled).\n */\n forceReconnectAttempts?: number;\n /** Delay before kicking each of the `forceReconnectAttempts` connections. Default: 5000. */\n forceReconnectDelayMs?: number;\n /**\n * Called when a publisher connects. Create nodes in any workflow from\n * here, or call `operations.disconnectSource(info.connectionId)` to kick\n * the publisher synchronously (e.g. to reject an invalid `sourceName`).\n */\n onConnect: (\n info: RtmpConnectionInfo,\n operations: RtmpAnnouncerOperations,\n ) => void;\n /** Called when a publisher disconnects. */\n onDisconnect?: (info: RtmpConnectionInfo) => void;\n}\n\n/** SDK handle for an RTMP announcer node. */\nexport class RtmpAnnouncerNode extends BaseNode {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Can be\n * called at any time after the node is created — e.g. from an async\n * moderation decision made after `onConnect` already returned.\n */\n async disconnectSource(connectionId: string): Promise<void> {\n await this.client.trpc.disconnectSource.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n connectionId,\n });\n }\n}\n\nexport async function createRtmpAnnouncerNode(\n workflow: WorkflowHandle,\n settings: RtmpAnnouncerSettings,\n nodeId?: string,\n): Promise<RtmpAnnouncerNode> {\n const id = nodeId ?? `rtmp_announcer_${uid(12)}`;\n const app = settings.app ?? \"live\";\n const node = new RtmpAnnouncerNode(id, workflow.client, workflow.workflowId);\n const operations: RtmpAnnouncerOperations = {\n disconnectSource: (connectionId) => node.disconnectSource(connectionId),\n };\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"STREAM_CONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as RtmpConnectionInfo;\n settings.onConnect(info, operations);\n }\n if (evt.kind === \"STREAM_DISCONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as RtmpConnectionInfo;\n settings.onDisconnect?.(info);\n }\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_ANNOUNCER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpAnnouncer: {\n app,\n ...(settings.forceReconnectAttempts !== undefined && {\n forceReconnectAttempts: settings.forceReconnectAttempts,\n }),\n ...(settings.forceReconnectDelayMs !== undefined && {\n forceReconnectDelayMs: settings.forceReconnectDelayMs,\n }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport type { RemaClient } from \"../client.js\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n reportNodeError,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Normalize a `catch`-clause value (which TypeScript types as `unknown`) into an `Error`. */\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err));\n}\n\n// ── RtmpStreamNode ─────────────────────────────────────────────────────────────\n\n/**\n * Represents a single active RTMP stream created by an `RtmpMultiplexerNode`.\n *\n * The server creates this node automatically when a publisher connects. The SDK\n * wraps the server-side node ID so it can be passed as a source to downstream\n * nodes (encoders, outputs) via `subscribe()`.\n */\nexport class RtmpStreamNode extends BaseNode {\n readonly app: string;\n readonly sourceName: string;\n\n constructor(\n nodeId: string,\n client: RemaClient,\n app: string,\n sourceName: string,\n workflowId: string,\n ) {\n super(nodeId, client, workflowId);\n this.app = app;\n this.sourceName = sourceName;\n }\n}\n\n// ── RtmpMultiplexerNode ────────────────────────────────────────────────────────\n\n/**\n * SDK handle for an RTMP multiplexer node.\n *\n * Calling `close()` deactivates the multiplexer and all of its active stream\n * nodes on the server.\n */\nexport class RtmpMultiplexerNode extends BaseNode {}\n\n// ── Settings ──────────────────────────────────────────────────────────────────\n\nexport interface RtmpMultiplexerSettings extends NodeCallbacks {\n /** RTMP app name to listen on (default: \"live\"). */\n app?: string;\n\n /**\n * Called when a publisher connects. Return `{ accept: false }` to kick the\n * connection immediately; return `{ accept: true }` or omit the return value\n * to accept and trigger `onStream`.\n */\n onConnect?: (info: {\n app: string;\n sourceName: string;\n }) =>\n | Promise<{ accept: boolean } | undefined>\n | { accept: boolean }\n | undefined;\n\n /**\n * Called with the per-publisher `RtmpStreamNode` when a new RTMP stream is\n * accepted. Create your downstream pipeline (encoders, outputs) from this node.\n */\n onStream: (node: RtmpStreamNode) => Promise<void> | void;\n\n /**\n * Called when a publisher disconnects or is rejected. Use this to close any\n * downstream nodes you created in `onStream`.\n */\n onStreamEnd?: (node: RtmpStreamNode) => Promise<void> | void;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport async function createRtmpMultiplexerNode(\n workflow: WorkflowHandle,\n settings: RtmpMultiplexerSettings,\n nodeId?: string,\n): Promise<RtmpMultiplexerNode> {\n const id = nodeId ?? `rtmp_mux_${uid(12)}`;\n const app = settings.app ?? \"live\";\n const node = new RtmpMultiplexerNode(\n id,\n workflow.client,\n workflow.workflowId,\n );\n\n // Track active stream nodes by sourceName for onStreamEnd lookup.\n const activeStreams = new Map<string, RtmpStreamNode>();\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n if (evt.kind === \"CHILD_CREATED\" && evt.message !== undefined) {\n const { childNodeId, sourceName } = JSON.parse(evt.message) as {\n childNodeId: string;\n sourceName: string;\n };\n\n Promise.resolve(settings.onConnect?.({ app, sourceName }))\n .then(async (result) => {\n if (result?.accept === false) {\n await workflow.client.trpc.rejectPublisher\n .mutate({\n workflowId: workflow.workflowId,\n nodeId: id,\n sourceName,\n })\n .catch((err: unknown) => {\n reportNodeError(node, settings, toError(err));\n });\n return;\n }\n const streamNode = new RtmpStreamNode(\n childNodeId,\n workflow.client,\n app,\n sourceName,\n workflow.workflowId,\n );\n activeStreams.set(sourceName, streamNode);\n await settings.onStream(streamNode);\n })\n .catch((err: unknown) => {\n reportNodeError(node, settings, toError(err));\n });\n return;\n }\n\n if (evt.kind === \"CHILD_CLOSED\" && evt.message !== undefined) {\n const { sourceName } = JSON.parse(evt.message) as { sourceName: string };\n const streamNode = activeStreams.get(sourceName);\n activeStreams.delete(sourceName);\n if (streamNode) {\n Promise.resolve(settings.onStreamEnd?.(streamNode)).catch(\n (err: unknown) => {\n reportNodeError(node, settings, toError(err));\n },\n );\n }\n }\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_MULTIPLEXER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpMultiplexer: { app, port: 1935 },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface RtmpReaderSettings extends NodeCallbacks {\n /** RTMP app name. */\n app: string;\n /** Source name (stream key). */\n sourceName: string;\n /**\n * Retry policy for a failed FFmpeg pull from the internal MediaMTX relay\n * (e.g. a transient network blip). All fields optional; defaults to 5\n * attempts with 1s→10s exponential backoff before the node errors out.\n */\n retry?: {\n /** Max retries before giving up and marking the node ERROR. Default: 5. */\n maxAttempts?: number;\n /** Initial backoff delay in ms before the first retry. Default: 1000. */\n baseDelayMs?: number;\n /** Backoff delay ceiling in ms. Default: 10000. */\n maxDelayMs?: number;\n };\n /** Called when an RTMP publisher connects. */\n onPublish?: (info: { app: string; internalUrl: string }) => void;\n /**\n * Called once when the first data packet from this input arrives at the server.\n * @param wallClockMs `Date.now()` on the server at the moment the first MPEG-TS packet arrived.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called when the publisher disconnects (the underlying FFmpeg pull exits). */\n onStreamEnded?: () => void;\n}\n\nexport class RtmpReaderNode extends BaseNode {\n readonly settings: RtmpReaderSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: RtmpReaderSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createRtmpReaderNode(\n workflow: WorkflowHandle,\n settings: RtmpReaderSettings,\n nodeId?: string,\n): Promise<RtmpReaderNode> {\n const id = nodeId ?? `rtmp_reader_${uid(12)}`;\n const node = new RtmpReaderNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") {\n settings.onPublish?.({\n app: settings.app,\n internalUrl: evt.message ?? \"\",\n });\n }\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined) {\n settings.onStreamStart?.(Number(evt.message));\n }\n if (evt.kind === \"ENDED\") settings.onStreamEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_READER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpReader: {\n app: settings.app,\n sourceName: settings.sourceName,\n ...(settings.retry !== undefined && { retry: settings.retry }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Connection info passed to `onConnect` and `onDisconnect`. */\nexport interface SrtConnectionInfo {\n app: string;\n sourceName: string;\n /** Passphrase configured on the announcer, if any. */\n passphrase?: string;\n /** Negotiated SRT latency in milliseconds, if configured. */\n latencyMs?: number;\n /**\n * MediaMTX connection id for this publisher, if it could be resolved at\n * connect time. Pass directly to `operations.disconnectSource`.\n */\n connectionId?: string;\n}\n\n/** Operations available on a live SRT publisher connection. */\nexport interface SrtAnnouncerOperations {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Safe to\n * call even if it already disconnected (resolves as a no-op).\n */\n disconnectSource(connectionId: string): Promise<void>;\n}\n\nexport interface SrtAnnouncerSettings extends NodeCallbacks {\n /** SRT app name to watch (default: \"live\"). */\n app?: string;\n /** Passphrase to include in connection events (mirrors MediaMTX path config). */\n passphrase?: string;\n /** SRT latency in milliseconds to include in connection events. */\n latencyMs?: number;\n /**\n * Silently kick the first N connections for each `sourceName` — never\n * calling `onConnect`/`onDisconnect` for them — before letting the\n * (N+1)th through. Useful for publishers whose stream is incomplete for\n * the first few seconds of a connection (e.g. some CDN restream outputs\n * emit an MPEG-TS PMT that doesn't declare every elementary stream until\n * slightly after the connection starts). Resets once a connection is let\n * through, so the next new session for this `sourceName` gets the same\n * warm-up. Default: 0 (disabled).\n */\n forceReconnectAttempts?: number;\n /** Delay before kicking each of the `forceReconnectAttempts` connections. Default: 5000. */\n forceReconnectDelayMs?: number;\n /**\n * Called when a publisher connects. Create nodes in any workflow from\n * here, or call `operations.disconnectSource(info.connectionId)` to kick\n * the publisher synchronously (e.g. to reject an invalid `sourceName`).\n */\n onConnect: (\n info: SrtConnectionInfo,\n operations: SrtAnnouncerOperations,\n ) => void;\n /** Called when a publisher disconnects. */\n onDisconnect?: (info: Pick<SrtConnectionInfo, \"app\" | \"sourceName\">) => void;\n}\n\n/** SDK handle for an SRT announcer node. */\nexport class SrtAnnouncerNode extends BaseNode {\n /**\n * Forcibly disconnect the connection identified by `connectionId`. Can be\n * called at any time after the node is created — e.g. from an async\n * moderation decision made after `onConnect` already returned.\n */\n async disconnectSource(connectionId: string): Promise<void> {\n await this.client.trpc.disconnectSource.mutate({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n connectionId,\n });\n }\n}\n\nexport async function createSrtAnnouncerNode(\n workflow: WorkflowHandle,\n settings: SrtAnnouncerSettings,\n nodeId?: string,\n): Promise<SrtAnnouncerNode> {\n const id = nodeId ?? `srt_announcer_${uid(12)}`;\n const app = settings.app ?? \"live\";\n const node = new SrtAnnouncerNode(id, workflow.client, workflow.workflowId);\n const operations: SrtAnnouncerOperations = {\n disconnectSource: (connectionId) => node.disconnectSource(connectionId),\n };\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"STREAM_CONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as SrtConnectionInfo;\n settings.onConnect(info, operations);\n }\n if (evt.kind === \"STREAM_DISCONNECTED\" && evt.message !== undefined) {\n const info = JSON.parse(evt.message) as Pick<\n SrtConnectionInfo,\n \"app\" | \"sourceName\"\n >;\n settings.onDisconnect?.(info);\n }\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"SRT_ANNOUNCER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n srtAnnouncer: {\n app,\n ...(settings.passphrase !== undefined && {\n passphrase: settings.passphrase,\n }),\n ...(settings.latencyMs !== undefined && {\n latencyMs: settings.latencyMs,\n }),\n ...(settings.forceReconnectAttempts !== undefined && {\n forceReconnectAttempts: settings.forceReconnectAttempts,\n }),\n ...(settings.forceReconnectDelayMs !== undefined && {\n forceReconnectDelayMs: settings.forceReconnectDelayMs,\n }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface SrtReaderSettings extends NodeCallbacks {\n /** SRT app name. */\n app: string;\n /** Source name (stream key / stream ID suffix). */\n sourceName: string;\n /**\n * Retry policy for a failed FFmpeg pull from the internal MediaMTX relay\n * (e.g. a transient network blip). All fields optional; defaults to 5\n * attempts with 1s→10s exponential backoff before the node errors out.\n */\n retry?: {\n /** Max retries before giving up and marking the node ERROR. Default: 5. */\n maxAttempts?: number;\n /** Initial backoff delay in ms before the first retry. Default: 1000. */\n baseDelayMs?: number;\n /** Backoff delay ceiling in ms. Default: 10000. */\n maxDelayMs?: number;\n };\n /** Called when an SRT publisher connects and the stream is ready to read. */\n onPublish?: (info: { app: string; internalUrl: string }) => void;\n /**\n * Called once when the first data packet from this input arrives at the server.\n * @param wallClockMs `Date.now()` on the server at the moment the first MPEG-TS packet arrived.\n */\n onStreamStart?: (wallClockMs: number) => void;\n /** Called when the publisher disconnects (the underlying FFmpeg pull exits). */\n onStreamEnded?: () => void;\n}\n\n/** SDK handle for an SRT reader node. */\nexport class SrtReaderNode extends BaseNode {\n readonly settings: SrtReaderSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: SrtReaderSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createSrtReaderNode(\n workflow: WorkflowHandle,\n settings: SrtReaderSettings,\n nodeId?: string,\n): Promise<SrtReaderNode> {\n const id = nodeId ?? `srt_reader_${uid(12)}`;\n const node = new SrtReaderNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n if (evt.kind === \"READY\") {\n settings.onPublish?.({\n app: settings.app,\n internalUrl: evt.message ?? \"\",\n });\n }\n if (evt.kind === \"ACTIVE\" && evt.message !== undefined) {\n settings.onStreamStart?.(Number(evt.message));\n }\n if (evt.kind === \"ENDED\") settings.onStreamEnded?.();\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"SRT_READER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n srtReader: {\n app: settings.app,\n sourceName: settings.sourceName,\n ...(settings.retry !== undefined && { retry: settings.retry }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface FileOutputSettings extends NodeCallbacks {\n /** Absolute path for the output file */\n fileName: string;\n /** Container format: \"mp4\" | \"ts\" | \"mkv\". Default: inferred from extension */\n format?: string;\n /** Overwrite `fileName` if it already exists. Default: `true`. */\n overwrite?: boolean;\n}\n\nexport class FileOutputNode extends BaseNode {\n readonly settings: FileOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: FileOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createFileOutputNode(\n workflow: WorkflowHandle,\n settings: FileOutputSettings,\n nodeId?: string,\n): Promise<FileOutputNode> {\n const id = nodeId ?? `file_output_${uid(12)}`;\n const node = new FileOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"FILE_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n fileOutput: {\n fileName: settings.fileName,\n format: settings.format ?? \"\",\n overwrite: settings.overwrite ?? true,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { ActivationGate } from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type HlsSubscriptionSpec,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\nimport { expandRenditionSpecs } from \"./hls-renditions.js\";\n\nexport type HlsDestination =\n | { type: \"file\"; path: string }\n | {\n type: \"s3\";\n bucket: string;\n region: string;\n /** S3-compatible endpoint URL (e.g. Tigris, MinIO, Cloudflare R2). */\n endpoint?: string;\n /** Key prefix prepended to all uploaded paths. */\n keyPrefix?: string;\n /**\n * When true (default), a per-session timestamp suffix is appended to\n * `keyPrefix` so that browser-cached segments from a previous session are\n * never served for a new one. Set to false when the caller already\n * generates a unique prefix or when sub-paths are not supported.\n */\n makePrefixUnique?: boolean;\n accessKeyId: string;\n secretAccessKey: string;\n /**\n * `Cache-Control` header for `.m3u8` playlist objects.\n * Default: `\"no-cache, no-store, must-revalidate\"` — playlists must\n * never be served stale.\n */\n playlistCacheControl?: string;\n /**\n * `Cache-Control` header for segment objects (`.ts`, `.vtt`).\n * Default: `\"public, max-age=86400\"` — segments are immutable once\n * written, so they can be cached aggressively.\n */\n segmentsCacheControl?: string;\n }\n | {\n type: \"http\";\n /** Base URL that receives PUT/DELETE requests for each segment and playlist. */\n baseUrl: string;\n /** Extra headers sent with every PUT/DELETE request. */\n headers?: Record<string, string>;\n };\n\nexport interface HlsOutputSettings extends NodeCallbacks {\n /** One or more destinations to fan HLS output to. */\n destinations: HlsDestination[];\n /** Target segment duration in seconds. Default: 6 */\n segmentTime?: number;\n /**\n * Number of segments kept in the live playlist window.\n * 0 = keep all (VOD). Default: 5.\n */\n listSize?: number;\n /**\n * Node-level fallback audio codec, applied to audio streams whose\n * subscription does not specify its own `codec`. Omitted (or `copy`)\n * preserves the input audio codec — except when more than one audio source\n * is connected, where the default becomes `aac` so all alternate-audio\n * renditions share one codec (required by hls.js).\n *\n * For a caption-readiness buffer (the old `avDelayMs`), put `delayMs` on\n * each media subscription instead and leave caption subscriptions\n * undelayed.\n */\n audioCodec?: \"copy\" | string | null;\n /**\n * Node-level fallback video codec, applied to video streams whose\n * subscription does not specify its own `codec`. Omitted (or `copy`)\n * preserves the input video codec.\n */\n videoCodec?: \"copy\" | string | null;\n}\n\nexport class HlsOutputNode extends BaseNode {\n readonly settings: HlsOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: HlsOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /**\n * Return the effective base path or URL for each configured destination in\n * the current session. Must be called after `subscribe()` so that the server\n * has wired the destinations. For S3 destinations this includes the\n * per-session subfolder appended by `makePrefixUnique`.\n */\n async effectivePaths(): Promise<string[]> {\n const result = await this.client.trpc.hlsOutputInfo.query({\n workflowId: this.workflowId,\n nodeId: this.nodeId,\n });\n return result.effectivePaths;\n }\n\n /**\n * Subscribe this node to one or more source nodes, expanding any\n * `VIDEO`/`ALL` spec's `additionalRenditions` into extra HLS variants\n * first (see `VideoRendition`). Keyframe alignment for generated\n * renditions uses this node's own `segmentTime`.\n */\n override async subscribe(\n specs: HlsSubscriptionSpec[],\n options?: { gate?: ActivationGate },\n ): Promise<void> {\n await super.subscribe(\n expandRenditionSpecs(specs, this.settings.segmentTime ?? 6),\n options,\n );\n }\n}\n\nexport async function createHlsOutputNode(\n workflow: WorkflowHandle,\n settings: HlsOutputSettings,\n nodeId?: string,\n): Promise<HlsOutputNode> {\n const id = nodeId ?? `hls_output_${uid(12)}`;\n const node = new HlsOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"HLS_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n hlsOutput: {\n destinations: settings.destinations,\n segmentTime: settings.segmentTime ?? 6,\n listSize: settings.listSize ?? 5,\n audioCodec: settings.audioCodec ?? undefined,\n videoCodec: settings.videoCodec ?? undefined,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type {\n BaseNode,\n HlsSubscriptionSpec,\n HlsVideoSubscriptionSpec,\n SubscriptionSpec,\n VideoRendition,\n} from \"../node.js\";\n\n/** Ladders beyond this total variant count get a one-time `console.warn`. */\nconst RENDITION_WARN_THRESHOLD = 3;\n\n/**\n * Parse an ffmpeg bitrate value (`\"3M\"`, `\"1500k\"`, or a bare bits/sec\n * number) to bits/sec, for deriving a default `bufsize`.\n */\nfunction bitsPerSecond(v: string | number): number {\n if (typeof v === \"number\") return v;\n const match = /^([\\d.]+)\\s*([kKmM]?)$/.exec(v.trim());\n if (!match) return Number(v) || 0;\n const value = Number(match[1]);\n const suffix = match[2]?.toLowerCase();\n if (suffix === \"k\") return value * 1_000;\n if (suffix === \"m\") return value * 1_000_000;\n return value;\n}\n\n/** Build one generated `HlsVideoSubscriptionSpec` for a single ladder tier. */\nfunction buildRenditionSpec(\n source: BaseNode,\n track: number | undefined,\n r: VideoRendition,\n segmentTime: number,\n): HlsVideoSubscriptionSpec {\n const bitrate = String(r.bitrate);\n const maxrate = r.maxrate !== undefined ? String(r.maxrate) : bitrate;\n const bufsize =\n r.bufsize !== undefined\n ? String(r.bufsize)\n : String(2 * bitsPerSecond(r.bitrate));\n\n return {\n source,\n sourceSelector: \"VIDEO\",\n ...(track !== undefined ? { track } : {}),\n codec: {\n name: r.codecName ?? \"libx264\",\n options: {\n preset: r.preset ?? \"veryfast\",\n b: bitrate,\n maxrate,\n bufsize,\n // fps-agnostic keyframe alignment so every rendition cuts HLS\n // segments at the same wall-clock points — required for clean ABR\n // switching (see VideoRendition docs / SDK docs CPU-cost section).\n force_key_frames: `expr:gte(t,n_forced*${segmentTime})`,\n // Per-stream options always get a `:v:N` specifier appended (see\n // `buildCodecArgs`) — `filter` (not the bare `vf` alias, which does\n // not accept a stream specifier: `-vf:v:N` is invalid FFmpeg syntax\n // and fails with AVERROR(EINVAL)) becomes the valid `-filter:v:N`.\n ...(r.width !== undefined\n ? { s: `${r.width}x${r.height}` }\n : { filter: `scale=-2:${r.height}` }),\n ...r.extraOptions,\n },\n },\n video: { label: r.label ?? `${r.height}p` },\n };\n}\n\n/**\n * Expand every `VIDEO`/`ALL` spec's `additionalRenditions` into extra\n * `VideoSubscriptionSpec` entries, returning the full flat list to actually\n * subscribe. Non-VIDEO/ALL specs (AUDIO, CAPTIONS) and VIDEO/ALL specs with\n * no `additionalRenditions` pass through unchanged. Pure — no network\n * calls.\n *\n * Emits one `console.warn` (advisory, not thrown) when the total number of\n * video variants across all specs exceeds a sane default ladder size —\n * CPU cost scales ~linearly with rendition count, dominated by encode: each\n * rendition is an independent decode+encode pass inside the HLS output's\n * one shared FFmpeg process (no single-decode `filter_complex split+scale`\n * optimization).\n */\nexport function expandRenditionSpecs(\n specs: HlsSubscriptionSpec[],\n segmentTime: number,\n): SubscriptionSpec[] {\n const result: SubscriptionSpec[] = [];\n let videoVariantCount = 0;\n\n for (const spec of specs) {\n if (spec.sourceSelector !== \"VIDEO\" && spec.sourceSelector !== \"ALL\") {\n result.push(spec);\n continue;\n }\n\n const { additionalRenditions, omitOriginalRendition, ...rest } = spec;\n\n if (!additionalRenditions || additionalRenditions.length === 0) {\n result.push(spec);\n videoVariantCount++;\n continue;\n }\n\n if (!omitOriginalRendition) {\n result.push(rest);\n videoVariantCount++;\n }\n\n const track = spec.sourceSelector === \"VIDEO\" ? spec.track : undefined;\n for (const r of additionalRenditions) {\n result.push(buildRenditionSpec(spec.source, track, r, segmentTime));\n videoVariantCount++;\n }\n }\n\n if (videoVariantCount > RENDITION_WARN_THRESHOLD) {\n console.warn(\n `[rema] HLS output: ${videoVariantCount} video renditions means ${videoVariantCount} concurrent decode+encode passes inside one FFmpeg process — CPU cost scales ~linearly with rendition count, dominated by encode. Consider trimming tiers, a faster preset (\"superfast\"/\"ultrafast\"), or a hardware encoder via VideoRendition.codecName (e.g. \"h264_videotoolbox\", \"h264_nvenc\").`,\n );\n }\n\n return result;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface RtmpOutputSettings extends NodeCallbacks {\n /** Full RTMP URL to push to, e.g. \"rtmp://live.twitch.tv/app/stream-key\" */\n url: string;\n /**\n * Called when the output FFmpeg process exits with an error after the node\n * has been created — e.g. the destination server refused the connection,\n * dropped mid-stream, or returned an unexpected error code.\n *\n * Errors that occur during node creation still reject the\n * `workflow.output.rtmp()` promise as usual.\n */\n onError?: (err: Error) => void;\n}\n\nexport class RtmpOutputNode extends BaseNode {\n readonly settings: RtmpOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: RtmpOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createRtmpOutputNode(\n workflow: WorkflowHandle,\n settings: RtmpOutputSettings,\n nodeId?: string,\n): Promise<RtmpOutputNode> {\n const id = nodeId ?? `rtmp_output_${uid(12)}`;\n const node = new RtmpOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"RTMP_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n rtmpOutput: { url: settings.url },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface SrtOutputSettings extends NodeCallbacks {\n /**\n * Full SRT URL to push to, e.g. `\"srt://host:port\"`.\n * Transport parameters (latency, passphrase, etc.) are passed as query\n * params: `\"srt://host:port?latency=200&passphrase=secret\"`.\n */\n url: string;\n /**\n * Called when the output FFmpeg process exits with an error after the node\n * has been created — e.g. the destination refused the connection or dropped\n * mid-stream.\n *\n * Errors that occur during node creation still reject the\n * `workflow.output.srt()` promise as usual.\n */\n onError?: (err: Error) => void;\n}\n\nexport class SrtOutputNode extends BaseNode {\n readonly settings: SrtOutputSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: SrtOutputSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createSrtOutputNode(\n workflow: WorkflowHandle,\n settings: SrtOutputSettings,\n nodeId?: string,\n): Promise<SrtOutputNode> {\n const id = nodeId ?? `srt_output_${uid(12)}`;\n const node = new SrtOutputNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"SRT_OUTPUT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n srtOutput: { url: settings.url },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type {\n AzureSpeechLocale,\n AzureTargetLanguageCode,\n AzureVoiceName,\n} from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface AzureTranslateSettings extends NodeCallbacks {\n /** Azure Speech resource key. */\n apiKey: string;\n /** Azure Speech resource region, e.g. `\"westeurope\"`. */\n region: string;\n /**\n * Source language code for the input audio, e.g. `\"en-US\"`. Autocompletes\n * common locales; any valid Azure locale works — see\n * https://learn.microsoft.com/azure/ai-services/speech-service/language-support?tabs=stt\n * for the full list.\n */\n sourceLanguageCode: AzureSpeechLocale;\n /**\n * Target language code for the translation output, e.g. `\"de\"`.\n * Autocompletes common codes; any valid Azure target works — see\n * https://learn.microsoft.com/azure/ai-services/speech-service/language-support?tabs=speech-translation\n * for the full list.\n */\n targetLanguageCode: AzureTargetLanguageCode;\n /**\n * Target-language neural voice used to synthesize the translation, e.g.\n * `\"de-DE-KatjaNeural\"`. Must match the target language. Autocompletes\n * common voices; any valid Azure voice works — see\n * https://learn.microsoft.com/azure/ai-services/speech-service/language-support?tabs=tts\n * for the full list.\n */\n voiceName: AzureVoiceName;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via Azure AI Speech's real-time translation + synthesis. */\nexport class AzureTranslateNode extends BaseNode {\n readonly settings: AzureTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: AzureTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createAzureTranslateNode(\n workflow: WorkflowHandle,\n settings: AzureTranslateSettings,\n nodeId?: string,\n): Promise<AzureTranslateNode> {\n const id = nodeId ?? `azure_translate_${uid(12)}`;\n const node = new AzureTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"AZURE_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n azureTranslate: {\n apiKey: settings.apiKey,\n region: settings.region,\n sourceLanguageCode: settings.sourceLanguageCode,\n targetLanguageCode: settings.targetLanguageCode,\n voiceName: settings.voiceName,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface BufferSettings extends NodeCallbacks {\n /** Time (ms) to hold back the input stream before forwarding it to subscribers. */\n bufferMs: number;\n}\n\n/**\n * Time-shift node. Subscribe it to exactly one source (any selector); it\n * re-emits that source delayed by `bufferMs`, shared so every downstream\n * subscriber sees the same delayed feed.\n */\nexport class BufferNode extends BaseNode {\n readonly settings: BufferSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: BufferSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\n/** Create a {@link BufferNode} on the server. */\nexport async function createBufferNode(\n workflow: WorkflowHandle,\n settings: BufferSettings,\n nodeId?: string,\n): Promise<BufferNode> {\n const id = nodeId ?? `buffer_${uid(12)}`;\n const node = new BufferNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"BUFFER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n buffer: { bufferMs: settings.bufferMs },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface CambAiTranslateSettings extends NodeCallbacks {\n /** Camb.ai API key. */\n apiKey: string;\n /** BCP-47 source language code for the input audio (e.g. `\"en-US\"`). */\n sourceLanguageCode: string;\n /** BCP-47 target language code for the translation output (e.g. `\"de-DE\"`). */\n targetLanguageCode: string;\n /** Realtime translation model. Defaults to Camb's server-side default. */\n model?: \"lilac\" | \"violet\" | \"iris\" | \"orchid\";\n /** Cloned voice ID to synthesize the translation with. Defaults to a built-in voice for the target language. */\n voiceId?: number;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via the Camb.ai realtime speech-to-speech API. */\nexport class CambAiTranslateNode extends BaseNode {\n readonly settings: CambAiTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: CambAiTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createCambAiTranslateNode(\n workflow: WorkflowHandle,\n settings: CambAiTranslateSettings,\n nodeId?: string,\n): Promise<CambAiTranslateNode> {\n const id = nodeId ?? `camb_translate_${uid(12)}`;\n const node = new CambAiTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"CAMB_AI_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n cambAiTranslate: {\n apiKey: settings.apiKey,\n sourceLanguageCode: settings.sourceLanguageCode,\n targetLanguageCode: settings.targetLanguageCode,\n model: settings.model,\n voiceId: settings.voiceId,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport type CaptionMode = \"rollUp\" | \"popOn\" | \"paintOn\";\n\nexport interface Cea608EncoderSettings extends NodeCallbacks {\n /** Caption display mode. Default: \"rollUp\" */\n mode?: CaptionMode;\n /**\n * Maximum number of visible rows (2, 3, or 4). Default: 2.\n *\n * - **rollUp**: sets the scroll-window height (RU2/RU3/RU4 command).\n * - **popOn**: captions sent with `\\n`-separated lines are displayed\n * bottom-anchored; this value caps how many lines are shown at once.\n * E.g. `rows: 2` with `\"line one\\nline two\"` fills rows 14 and 15.\n */\n rows?: 2 | 3 | 4;\n /**\n * When `mode` is `\"popOn\"`, automatically word-wrap caption text to\n * CEA-608's 32-column display width before laying it out as a\n * bottom-anchored multi-line caption card (capped at `rows` lines) —\n * instead of requiring you to insert `\\n` yourself. Has no effect in\n * `\"rollUp\"`/`\"paintOn\"` modes, where each `send()` is naturally a single\n * line.\n *\n * Default: false.\n */\n autoWrap?: boolean;\n}\n\nexport class Cea608EncoderNode extends BaseNode {\n readonly settings: Cea608EncoderSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: Cea608EncoderSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createCea608EncoderNode(\n workflow: WorkflowHandle,\n settings: Cea608EncoderSettings = {},\n nodeId?: string,\n): Promise<Cea608EncoderNode> {\n const id = nodeId ?? `cea608_${uid(12)}`;\n const node = new Cea608EncoderNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"CEA608_ENCODER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n cea608Encoder: {\n mode: settings.mode ?? \"rollUp\",\n rows: settings.rows ?? 2,\n autoWrap: settings.autoWrap ?? false,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface DeepgramSttSettings extends NodeCallbacks {\n /** Deepgram API key. */\n apiKey: string;\n /**\n * BCP-47 (or bare) language code hinting at the primary spoken language,\n * e.g. `\"en-US\"` or `\"en\"`. Also used as this node's subtitle track\n * language tag. Omitted lets Deepgram auto-detect (model-dependent) and\n * defaults the subtitle language tag to `\"en\"`.\n */\n language?: string;\n /** Deepgram model name, e.g. `\"nova-3\"`. Defaults to Deepgram's server-side default. */\n model?: string;\n /**\n * Schedule interim (non-final) transcripts as captions too, not just\n * finalized utterances. Interim results lower latency at the cost of\n * captions that may be replaced/overlap shortly after appearing. Default: `false`.\n */\n interimResults?: boolean;\n /** When set, captions longer than this are split into sequential chunks with proportional timing. */\n maxLineLength?: number;\n /** Subtitle track label shown to viewers, e.g. `\"English (auto)\"`. Defaults to `language`, or `\"Auto\"` if `language` is also omitted. */\n subtitleLabel?: string;\n /** Marks this subtitle track as the default track in the HLS master playlist. Default: `false`. */\n subtitleDefault?: boolean;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/**\n * Processor node that transcribes audio in real-time using Deepgram's\n * streaming speech-to-text API and exposes the transcripts as timed\n * captions — subscribe a `Cea608EncoderNode` to it (`sourceSelector:\n * \"CAPTIONS\"`) for burned-in captions, or select it as an HLS subtitle\n * track, exactly like `wf.input.captions()`.\n */\nexport class DeepgramSttNode extends BaseNode {\n readonly settings: DeepgramSttSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: DeepgramSttSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createDeepgramSttNode(\n workflow: WorkflowHandle,\n settings: DeepgramSttSettings,\n nodeId?: string,\n): Promise<DeepgramSttNode> {\n const id = nodeId ?? `deepgram_stt_${uid(12)}`;\n const node = new DeepgramSttNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"DEEPGRAM_STT\",\n workflowId: workflow.workflowId,\n nodeId: id,\n deepgramStt: {\n apiKey: settings.apiKey,\n language: settings.language,\n model: settings.model,\n interimResults: settings.interimResults,\n maxLineLength: settings.maxLineLength,\n subtitleLabel: settings.subtitleLabel,\n subtitleDefault: settings.subtitleDefault,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { MonitorEventMsg } from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport type { MonitorEventMsg };\n\nexport interface FfprobeMonitorSettings extends NodeCallbacks {\n /**\n * How often (ms) to re-run ffprobe on the attached stream.\n * Each cycle spawns a fresh ffprobe process, probes `probeSizeBytes` of data,\n * and publishes the result to the **stream-info** monitor.\n * Default: 30 000 ms.\n */\n intervalMs?: number;\n /**\n * Bytes of stream data to feed into each ffprobe invocation.\n * Larger values give ffprobe more context (e.g. long-GOP streams) at the\n * cost of a slightly longer first-result latency. Default: 1 048 576 (1 MB).\n */\n probeSizeBytes?: number;\n}\n\nexport class FfprobeMonitorNode extends BaseNode {\n readonly settings: FfprobeMonitorSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: FfprobeMonitorSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n\n /**\n * Subscribe to a monitor stream on this node.\n *\n * - `\"stream-info\"` — emits a `status` event (key/value map) every time a\n * probe cycle completes, containing codec names, resolution, frame rate,\n * bit rates, and format.\n * - `\"ffprobe-log\"` — emits `log` events with raw ffprobe stderr output.\n *\n * Returns an object with an `unsubscribe()` method; call it to stop receiving\n * events and release the tRPC subscription.\n */\n observe(\n monitorName: \"stream-info\" | \"ffprobe-log\",\n onData: (event: MonitorEventMsg) => void,\n onError?: (err: unknown) => void,\n ): { unsubscribe(): void } {\n return this.client.trpc.observeNode.subscribe(\n { workflowId: this.workflowId, nodeId: this.nodeId, monitorName },\n { onData, ...(onError !== undefined && { onError }) },\n );\n }\n}\n\nexport async function createFfprobeMonitorNode(\n workflow: WorkflowHandle,\n settings: FfprobeMonitorSettings = {},\n nodeId?: string,\n): Promise<FfprobeMonitorNode> {\n const id = nodeId ?? `ffprobe_${uid(12)}`;\n const node = new FfprobeMonitorNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"FFPROBE_MONITOR\",\n workflowId: workflow.workflowId,\n nodeId: id,\n ffprobeMonitor: {\n intervalMs: settings.intervalMs ?? 30_000,\n probeSizeBytes: settings.probeSizeBytes ?? 1_048_576,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface GateSettings extends NodeCallbacks {\n /**\n * Number of distinct sources that must have emitted at least one item\n * before the gate opens. Default: wait for every subscribed source.\n */\n minReady?: number;\n /**\n * Force the gate open after this many ms even if `minReady` hasn't been\n * reached, using whatever sources are ready by then. Omit to wait\n * indefinitely — a source that never produces data would otherwise stall\n * the gate forever.\n */\n timeoutMs?: number;\n}\n\n/**\n * Synchronization barrier for fan-in from multiple, independently-timed\n * sources (e.g. two different transcription/translation services). Not to\n * be confused with the `gate` option on `.subscribe()` (`ActivationGate`),\n * which defers wiring based on upstream node *lifecycle*, not data.\n *\n * `GateNode` subscribes to every source immediately and queues everything\n * each one emits, then starts forwarding — for every source at once — only\n * once enough of them have produced their first item. It is a one-time\n * rendezvous, not a per-item join.\n *\n * Every source becomes its own keyed pass-through port on the gate — streams\n * are never merged. Name a port with `name` on the gate's subscription spec\n * (default: the source's index as a string) and read it downstream with\n * `port`:\n *\n * ```ts\n * await gate.subscribe([\n * { source: a, sourceSelector: \"ALL\", name: \"main\" },\n * { source: b, sourceSelector: \"ALL\", name: \"backup\" },\n * ]);\n * await out1.subscribe([{ source: gate, sourceSelector: \"ALL\", port: \"main\" }]);\n * await out2.subscribe([{ source: gate, sourceSelector: \"ALL\", port: \"backup\" }]);\n * ```\n *\n * With exactly one source of a kind, plain selector subscriptions (no\n * `port`) still resolve to it; with several same-kind sources the server\n * errors and asks for an explicit `port`.\n */\nexport class GateNode extends BaseNode {\n readonly settings: GateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: GateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\n/** Create a {@link GateNode} on the server. */\nexport async function createGateNode(\n workflow: WorkflowHandle,\n settings: GateSettings = {},\n nodeId?: string,\n): Promise<GateNode> {\n const id = nodeId ?? `gate_${uid(12)}`;\n const node = new GateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"GATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n gate: {\n ...(settings.minReady !== undefined && {\n minReady: settings.minReady,\n }),\n ...(settings.timeoutMs !== undefined && {\n timeoutMs: settings.timeoutMs,\n }),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface GeminiTranslateSettings extends NodeCallbacks {\n /** Gemini API key. */\n apiKey: string;\n /**\n * BCP-47 target language code for the translation output\n * (e.g. `\"it\"`, `\"es\"`, `\"fr\"`).\n */\n targetLanguageCode: string;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via the Gemini Live Translate API. */\nexport class GeminiTranslateNode extends BaseNode {\n readonly settings: GeminiTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: GeminiTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createGeminiTranslateNode(\n workflow: WorkflowHandle,\n settings: GeminiTranslateSettings,\n nodeId?: string,\n): Promise<GeminiTranslateNode> {\n const id = nodeId ?? `gemini_translate_${uid(12)}`;\n const node = new GeminiTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"GEMINI_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n geminiTranslate: {\n apiKey: settings.apiKey,\n targetLanguageCode: settings.targetLanguageCode,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { OpenAiTranslateTargetLanguage } from \"@remavideo/server\";\nimport { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\nexport interface OpenAiTranslateSettings extends NodeCallbacks {\n /** OpenAI API key. */\n apiKey: string;\n /**\n * Target language code for the translation output — one of the 13\n * languages `gpt-realtime-translate` currently supports.\n */\n targetLanguageCode: OpenAiTranslateTargetLanguage;\n /** Human-readable label shown in the dashboard. */\n label?: string;\n}\n\n/** Processor node that translates audio via OpenAI's `gpt-realtime-translate` model. */\nexport class OpenAiTranslateNode extends BaseNode {\n readonly settings: OpenAiTranslateSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: OpenAiTranslateSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createOpenAiTranslateNode(\n workflow: WorkflowHandle,\n settings: OpenAiTranslateSettings,\n nodeId?: string,\n): Promise<OpenAiTranslateNode> {\n const id = nodeId ?? `openai_translate_${uid(12)}`;\n const node = new OpenAiTranslateNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"OPENAI_TRANSLATE\",\n workflowId: workflow.workflowId,\n nodeId: id,\n openAiTranslate: {\n apiKey: settings.apiKey,\n targetLanguageCode: settings.targetLanguageCode,\n label: settings.label,\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { AppRouter, HealthStatus } from \"@remavideo/server\";\nimport {\n createTRPCClient,\n httpLink,\n httpSubscriptionLink,\n splitLink,\n} from \"@trpc/client\";\nimport { EventSource } from \"eventsource\";\nimport superjson from \"superjson\";\n\nexport interface RemaClientOptions {\n host?: string;\n port?: number;\n onLogEvent?: (log: { level: string; message: string }) => void;\n onReady?: () => void;\n onFailedToConnect?: () => void;\n}\n\nexport class RemaClient {\n readonly trpc: ReturnType<typeof createTRPCClient<AppRouter>>;\n readonly options: Required<Pick<RemaClientOptions, \"host\" | \"port\">>;\n\n private constructor(\n trpc: ReturnType<typeof createTRPCClient<AppRouter>>,\n options: RemaClientOptions,\n ) {\n this.trpc = trpc;\n this.options = {\n host: options.host ?? \"127.0.0.1\",\n port: options.port ?? 8080,\n };\n }\n\n static async connect(opts: RemaClientOptions = {}): Promise<RemaClient> {\n const host = opts.host ?? \"127.0.0.1\";\n const port = opts.port ?? 8080;\n const url = `http://${host}:${port}/trpc`;\n\n const trpc = createTRPCClient<AppRouter>({\n links: [\n splitLink({\n condition: (op) => op.type === \"subscription\",\n true: httpSubscriptionLink({\n url,\n transformer: superjson,\n EventSource,\n }),\n false: httpLink({ url, transformer: superjson }),\n }),\n ],\n });\n\n try {\n await trpc.ping.query();\n opts.onReady?.();\n } catch (err) {\n opts.onFailedToConnect?.();\n throw new Error(\n `Failed to connect to rema server at ${host}:${port}: ${String(err)}`,\n );\n }\n\n return new RemaClient(trpc, opts);\n }\n\n /** No-op kept for API compatibility — SSE has no persistent connection to close. */\n close(): void {}\n\n /** Fetches the server's health status from the plain HTTP `/api/health` endpoint. */\n async health(): Promise<HealthStatus> {\n const url = `http://${this.options.host}:${this.options.port}/api/health`;\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Health check failed: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as HealthStatus;\n }\n}\n","import type { NodeEventMsg, WorkflowInfo } from \"@remavideo/server\";\nimport { TypedEmitter } from \"tiny-typed-emitter\";\nimport type { RemaClient } from \"./client.js\";\nimport {\n type CaptionSourceNode,\n type CaptionSourceSettings,\n createCaptionSourceNode,\n} from \"./input/caption-source.js\";\nimport {\n createFileInputNode,\n type FileInputNode,\n type FileInputSettings,\n} from \"./input/file.js\";\nimport {\n createNodeStreamInputNode,\n type NodeStreamInputNode,\n type NodeStreamInputSettings,\n} from \"./input/node-stream.js\";\nimport {\n createRtmpAnnouncerNode,\n type RtmpAnnouncerNode,\n type RtmpAnnouncerSettings,\n} from \"./input/rtmp-announcer.js\";\nimport {\n createRtmpMultiplexerNode,\n type RtmpMultiplexerNode,\n type RtmpMultiplexerSettings,\n} from \"./input/rtmp-multiplexer.js\";\nimport {\n createRtmpReaderNode,\n type RtmpReaderNode,\n type RtmpReaderSettings,\n} from \"./input/rtmp-reader.js\";\nimport {\n createSrtAnnouncerNode,\n type SrtAnnouncerNode,\n type SrtAnnouncerSettings,\n} from \"./input/srt-announcer.js\";\nimport {\n createSrtReaderNode,\n type SrtReaderNode,\n type SrtReaderSettings,\n} from \"./input/srt-reader.js\";\nimport {\n createFileOutputNode,\n type FileOutputNode,\n type FileOutputSettings,\n} from \"./output/file.js\";\nimport {\n createHlsOutputNode,\n type HlsOutputNode,\n type HlsOutputSettings,\n} from \"./output/hls.js\";\nimport {\n createRtmpOutputNode,\n type RtmpOutputNode,\n type RtmpOutputSettings,\n} from \"./output/rtmp.js\";\nimport {\n createSrtOutputNode,\n type SrtOutputNode,\n type SrtOutputSettings,\n} from \"./output/srt.js\";\nimport {\n type AudioMixerNode,\n type AudioMixerSettings,\n createAudioMixerNode,\n} from \"./processor/audio-mixer.js\";\nimport {\n type AzureTranslateNode,\n type AzureTranslateSettings,\n createAzureTranslateNode,\n} from \"./processor/azure-translate.js\";\nimport {\n type BufferNode,\n type BufferSettings,\n createBufferNode,\n} from \"./processor/buffer.js\";\nimport {\n type CambAiTranslateNode,\n type CambAiTranslateSettings,\n createCambAiTranslateNode,\n} from \"./processor/camb-ai-translate.js\";\nimport {\n type Cea608EncoderNode,\n type Cea608EncoderSettings,\n createCea608EncoderNode,\n} from \"./processor/cea608-encoder.js\";\nimport {\n createDeepgramSttNode,\n type DeepgramSttNode,\n type DeepgramSttSettings,\n} from \"./processor/deepgram-stt.js\";\nimport {\n createFfprobeMonitorNode,\n type FfprobeMonitorNode,\n type FfprobeMonitorSettings,\n} from \"./processor/ffprobe-monitor.js\";\nimport {\n createGateNode,\n type GateNode,\n type GateSettings,\n} from \"./processor/gate.js\";\nimport {\n createGeminiTranslateNode,\n type GeminiTranslateNode,\n type GeminiTranslateSettings,\n} from \"./processor/gemini-translate.js\";\nimport {\n createOpenAiTranslateNode,\n type OpenAiTranslateNode,\n type OpenAiTranslateSettings,\n} from \"./processor/openai-translate.js\";\n\nexport type { WorkflowInfo };\n\n/** Normalize a `catch`-clause/subscription value (typed `unknown`) into an `Error`. */\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err));\n}\n\n/** Lifecycle callbacks available on a workflow. */\nexport interface WorkflowCallbacks {\n /**\n * Called if this workflow's worker process crashes. Workflow errors\n * otherwise only appear in server logs — this is the client-side signal.\n */\n onError?: (err: Error) => void;\n /** Called when the workflow closes, whether via `.close()` or a crash. */\n onClose?: () => void;\n}\n\n/** Events emitted by a workflow, mirroring {@link WorkflowCallbacks} for `.on()`-style consumers. */\nexport interface WorkflowEvents {\n error: (err: Error) => void;\n close: () => void;\n}\n\n/**\n * A workflow is an isolated sub-registry on the server.\n * All nodes created through a workflow handle are scoped to that workflow;\n * they cannot subscribe to nodes in other workflows.\n *\n * Obtain a workflow via `rema.workflow.create()`.\n */\nexport class Workflow extends TypedEmitter<WorkflowEvents> {\n readonly workflowId: string;\n /** Non-private: accessible to factory functions via WorkflowHandle. */\n readonly client: RemaClient;\n\n private readonly _callbacks: WorkflowCallbacks;\n private readonly _eventHandlers = new Map<\n string,\n Set<(evt: NodeEventMsg) => void>\n >();\n private _eventSub: { unsubscribe(): void } | null = null;\n private _stateSub: { unsubscribe(): void } | null = null;\n private _seenInList = false;\n private _erroredFired = false;\n private _closedFired = false;\n\n /** @internal — use `rema.workflow.create()` instead */\n constructor(\n workflowId: string,\n client: RemaClient,\n callbacks: WorkflowCallbacks = {},\n ) {\n super();\n this.workflowId = workflowId;\n this.client = client;\n this._callbacks = callbacks;\n this._startEventSubscription();\n this._startStateSubscription();\n }\n\n /**\n * Register a handler for events emitted by a specific node in this workflow.\n * Returns an unsubscribe function — call it when the node is closed.\n * @internal used by node factory functions\n */\n _onNodeEvent(\n nodeId: string,\n handler: (evt: NodeEventMsg) => void,\n ): () => void {\n let handlers = this._eventHandlers.get(nodeId);\n if (handlers === undefined) {\n handlers = new Set();\n this._eventHandlers.set(nodeId, handlers);\n }\n handlers.add(handler);\n return () => {\n this._eventHandlers.get(nodeId)?.delete(handler);\n };\n }\n\n private _startEventSubscription(): void {\n this._eventSub = this.client.trpc.workflow.events.subscribe(\n { workflowId: this.workflowId },\n {\n onData: (evt) => {\n this._eventHandlers.get(evt.nodeId)?.forEach((h) => {\n h(evt);\n });\n },\n onError: (err) => this._reportError(toError(err)),\n },\n );\n }\n\n /**\n * Watches the server's list of all workflows for this workflow's entry, so\n * `onError`/`onClose` can be derived without a dedicated per-workflow\n * subscription: a transition to `status: \"crashed\"` fires `onError`, and\n * the entry disappearing (graceful close) fires `onClose`.\n */\n private _startStateSubscription(): void {\n this._stateSub = this.client.trpc.workflow.state.subscribe(undefined, {\n onData: (workflows) => {\n const entry = workflows.find((w) => w.id === this.workflowId);\n if (entry !== undefined) {\n this._seenInList = true;\n if (entry.status === \"crashed\" && !this._erroredFired) {\n this._erroredFired = true;\n this._reportError(new Error(\"workflow worker crashed\"));\n }\n return;\n }\n if (this._seenInList) this._fireClose();\n },\n onError: (err) => this._reportError(toError(err)),\n });\n }\n\n /**\n * Report an error to this workflow's `onError` callback and its\n * `WorkflowEvents` emitter. Like `BaseNode`, `Workflow` extends Node's\n * `EventEmitter`, which throws synchronously from `emit()` when an\n * `\"error\"` event has no listener — so this only emits when someone has\n * opted in via `.on(\"error\", ...)`; the `onError` callback covers the\n * common case. The SDK does no logging of its own.\n */\n private _reportError(err: Error): void {\n this._callbacks.onError?.(err);\n if (this.listenerCount(\"error\") > 0) this.emit(\"error\", err);\n }\n\n private _fireClose(): void {\n if (this._closedFired) return;\n this._closedFired = true;\n this._eventSub?.unsubscribe();\n this._stateSub?.unsubscribe();\n this._callbacks.onClose?.();\n this.emit(\"close\");\n }\n\n // ─── Input nodes ─────────────────────────────────────────────────────────\n\n readonly input = {\n file: (\n settings: FileInputSettings,\n nodeId?: string,\n ): Promise<FileInputNode> => createFileInputNode(this, settings, nodeId),\n\n rtmpReader: (\n settings: RtmpReaderSettings,\n nodeId?: string,\n ): Promise<RtmpReaderNode> => createRtmpReaderNode(this, settings, nodeId),\n\n /** Announces RTMP publisher connections on an `app` without creating nodes. */\n rtmpAnnouncer: (\n settings: RtmpAnnouncerSettings,\n nodeId?: string,\n ): Promise<RtmpAnnouncerNode> =>\n createRtmpAnnouncerNode(this, settings, nodeId),\n\n /** Announces SRT publisher connections on an `app` without creating nodes. */\n srtAnnouncer: (\n settings: SrtAnnouncerSettings,\n nodeId?: string,\n ): Promise<SrtAnnouncerNode> =>\n createSrtAnnouncerNode(this, settings, nodeId),\n\n /** Reads an SRT stream ingested by MediaMTX, pulling it back via RTSP internally. */\n srtReader: (\n settings: SrtReaderSettings,\n nodeId?: string,\n ): Promise<SrtReaderNode> => createSrtReaderNode(this, settings, nodeId),\n\n /**\n * An RTMP multiplexer: accepts any number of simultaneous publishers under\n * the given `app`. For each connection `onStream` is called with a fresh\n * `RtmpStreamNode` that can be wired into its own encoder/output pipeline.\n */\n rtmpMultiplexer: (\n settings: RtmpMultiplexerSettings,\n nodeId?: string,\n ): Promise<RtmpMultiplexerNode> =>\n createRtmpMultiplexerNode(this, settings, nodeId),\n\n /** Format-agnostic caption schedule node. Send captions to it; wire it to a cea608() encoder. */\n captions: (\n settings: CaptionSourceSettings = {},\n nodeId?: string,\n ): Promise<CaptionSourceNode> =>\n createCaptionSourceNode(this, settings, nodeId),\n\n /**\n * Audio stream input node. Push a Node.js `Readable` (or any\n * `AsyncIterable<Buffer>`) directly into the pipeline by calling\n * `.stream(source)` on the returned node.\n */\n streamInput: (\n settings: NodeStreamInputSettings,\n nodeId?: string,\n ): Promise<NodeStreamInputNode> =>\n createNodeStreamInputNode(this, settings, nodeId),\n };\n\n // ─── Output nodes ─────────────────────────────────────────────────────────\n\n readonly output = {\n rtmp: (\n settings: RtmpOutputSettings,\n nodeId?: string,\n ): Promise<RtmpOutputNode> => createRtmpOutputNode(this, settings, nodeId),\n\n file: (\n settings: FileOutputSettings,\n nodeId?: string,\n ): Promise<FileOutputNode> => createFileOutputNode(this, settings, nodeId),\n\n /** HLS output node. Subscribe it to one or more media/subtitle sources. */\n hls: (\n settings: HlsOutputSettings,\n nodeId?: string,\n ): Promise<HlsOutputNode> => createHlsOutputNode(this, settings, nodeId),\n\n /** SRT output node. Pushes MPEG-TS over SRT to the given URL. */\n srt: (\n settings: SrtOutputSettings,\n nodeId?: string,\n ): Promise<SrtOutputNode> => createSrtOutputNode(this, settings, nodeId),\n };\n\n // ─── Processor nodes ──────────────────────────────────────────────────────\n\n readonly processor = {\n /** CEA-608/708 encoder node. Subscribe it to video + a captions() node. */\n cea608: (\n settings: Cea608EncoderSettings = {},\n nodeId?: string,\n ): Promise<Cea608EncoderNode> =>\n createCea608EncoderNode(this, settings, nodeId),\n\n /**\n * ffprobe monitor node.\n * Subscribe it to any media source; it periodically probes the live stream\n * and exposes codec/resolution/bitrate info via the `stream-info` monitor.\n */\n ffprobeMonitor: (\n settings: FfprobeMonitorSettings = {},\n nodeId?: string,\n ): Promise<FfprobeMonitorNode> =>\n createFfprobeMonitorNode(this, settings, nodeId),\n\n /**\n * Gemini Live Translate node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using the Gemini Live Translate API.\n */\n geminiTranslate: (\n settings: GeminiTranslateSettings,\n nodeId?: string,\n ): Promise<GeminiTranslateNode> =>\n createGeminiTranslateNode(this, settings, nodeId),\n\n /**\n * Camb.ai realtime speech-to-speech translation node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using the Camb.ai realtime translation API.\n */\n cambAiTranslate: (\n settings: CambAiTranslateSettings,\n nodeId?: string,\n ): Promise<CambAiTranslateNode> =>\n createCambAiTranslateNode(this, settings, nodeId),\n\n /**\n * OpenAI `gpt-realtime-translate` node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using OpenAI's realtime translation API.\n */\n openAiTranslate: (\n settings: OpenAiTranslateSettings,\n nodeId?: string,\n ): Promise<OpenAiTranslateNode> =>\n createOpenAiTranslateNode(this, settings, nodeId),\n\n /**\n * Azure AI Speech real-time translation node.\n * Subscribe it to an audio/media source; it outputs translated audio-only\n * MPEG-TS using Azure Speech's real-time translation + synthesis API.\n */\n azureTranslate: (\n settings: AzureTranslateSettings,\n nodeId?: string,\n ): Promise<AzureTranslateNode> =>\n createAzureTranslateNode(this, settings, nodeId),\n\n /**\n * Deepgram real-time speech-to-text node. Subscribe it to an\n * audio/media source; it exposes the resulting transcripts as timed\n * captions — feed that into `processor.cea608()` (`sourceSelector:\n * \"CAPTIONS\"`) for burned-in captions, or select it as an HLS subtitle\n * track, exactly like `input.captions()`.\n */\n deepgramStt: (\n settings: DeepgramSttSettings,\n nodeId?: string,\n ): Promise<DeepgramSttNode> =>\n createDeepgramSttNode(this, settings, nodeId),\n\n /**\n * Time-shift node. Subscribe it to exactly one source; it re-emits that\n * source delayed by `bufferMs`, shared across every downstream\n * subscriber.\n */\n buffer: (settings: BufferSettings, nodeId?: string): Promise<BufferNode> =>\n createBufferNode(this, settings, nodeId),\n\n /**\n * Synchronization barrier for fan-in from multiple, independently-timed\n * sources. Subscribe it to two or more sources; it withholds all of them\n * until enough (`minReady`, default: all) have produced their first\n * item, then forwards everything live.\n */\n gate: (settings: GateSettings = {}, nodeId?: string): Promise<GateNode> =>\n createGateNode(this, settings, nodeId),\n\n /**\n * Audio mixer node. Subscribe it to exactly `settings.audio.length`\n * AUDIO/ALL sources, in order — the Nth `audio[]` entry configures the\n * Nth subscribed source. Outputs a single mixed audio feed.\n */\n audioMixer: (\n settings: AudioMixerSettings,\n nodeId?: string,\n ): Promise<AudioMixerNode> => createAudioMixerNode(this, settings, nodeId),\n };\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n /**\n * Close this workflow and deactivate all its nodes on the server.\n * After calling this the workflow handle is no longer usable.\n *\n * The event/state subscriptions are kept open until the close mutation\n * resolves so that the server's per-node `CLOSED` events (dispatched to\n * each node's `onClose`/`onStatusChange` callbacks) are still received,\n * rather than tearing the subscriptions down before those events arrive.\n */\n async close(): Promise<void> {\n await this.client.trpc.workflow.close.mutate({\n workflowId: this.workflowId,\n });\n this._fireClose();\n }\n}\n","import { uid } from \"radash\";\nimport {\n BaseNode,\n dispatchNodeCallbacks,\n type NodeCallbacks,\n type WorkflowHandle,\n} from \"../node.js\";\n\n/** Per-source settings for one audio input to an `AudioMixerNode`. */\nexport interface AudioMixerInputSettings {\n /** Linear gain multiplier (1 = unity, 0.5 = -6dB). Default: 1. */\n volume?: number;\n /** Silences this input entirely, regardless of `volume`. Default: false. */\n muted?: boolean;\n /** Stereo balance: -1 (full left) .. 1 (full right), 0 = center. Default: 0. */\n pan?: number;\n /** High-pass filter cutoff (Hz), applied before mixing. Omit for no high-pass. */\n highpassHz?: number;\n /** Low-pass filter cutoff (Hz), applied before mixing. Omit for no low-pass. */\n lowpassHz?: number;\n}\n\nexport interface AudioMixerSettings extends NodeCallbacks {\n /**\n * One entry per audio source this node will be subscribed to, in the same\n * order — the Nth entry configures the Nth subscribed AUDIO/ALL source.\n * Subscribe with exactly `audio.length` sources.\n */\n audio: AudioMixerInputSettings[];\n}\n\nexport class AudioMixerNode extends BaseNode {\n readonly settings: AudioMixerSettings;\n\n constructor(\n nodeId: string,\n workflow: WorkflowHandle,\n settings: AudioMixerSettings,\n ) {\n super(nodeId, workflow.client, workflow.workflowId);\n this.settings = settings;\n }\n}\n\nexport async function createAudioMixerNode(\n workflow: WorkflowHandle,\n settings: AudioMixerSettings,\n nodeId?: string,\n): Promise<AudioMixerNode> {\n const id = nodeId ?? `audio_mixer_${uid(12)}`;\n const node = new AudioMixerNode(id, workflow, settings);\n\n node._bindCallbacks(settings);\n const removeHandler = workflow._onNodeEvent(id, (evt) => {\n dispatchNodeCallbacks(node, evt, settings);\n });\n node._setEventCleanup(removeHandler);\n\n await workflow.client.trpc.createNode\n .mutate({\n kind: \"AUDIO_MIXER\",\n workflowId: workflow.workflowId,\n nodeId: id,\n audioMixer: {\n audio: settings.audio.map((a) => ({\n volume: a.volume ?? 1,\n muted: a.muted ?? false,\n pan: a.pan ?? 0,\n ...(a.highpassHz !== undefined ? { highpassHz: a.highpassHz } : {}),\n ...(a.lowpassHz !== undefined ? { lowpassHz: a.lowpassHz } : {}),\n })),\n },\n })\n .catch((err: unknown) => {\n removeHandler();\n throw err;\n });\n\n return node;\n}\n","import type { HealthStatus, WorkflowInfo } from \"@remavideo/server\";\nimport { RemaClient, type RemaClientOptions } from \"./client.js\";\nimport { Workflow, type WorkflowCallbacks } from \"./workflow.js\";\n\nexport class Rema {\n private readonly client: RemaClient;\n private readonly _autoCloseWorkflows = new Set<Workflow>();\n\n private constructor(client: RemaClient) {\n this.client = client;\n }\n\n static async connect(opts: RemaClientOptions = {}): Promise<Rema> {\n const client = await RemaClient.connect(opts);\n return new Rema(client);\n }\n\n // ─── Workflow management ──────────────────────────────────────────────────\n\n readonly workflow = {\n /**\n * Create a new isolated workflow on the server.\n *\n * All nodes created through the returned `Workflow` handle are scoped to\n * this workflow; they cannot subscribe to nodes in other workflows.\n *\n * @param opts.name Human-readable label shown in the web UI.\n * @param opts.workflowId Optional stable ID. Server generates one if omitted.\n * @param opts.closeOnDisconnect When true, this workflow is closed automatically\n * when `rema.close()`/`wf.close()` is called, or\n * when the connection to the server is lost (e.g.\n * the process crashes) for more than a few seconds.\n * @param opts.parentId ID of an existing workflow to attach this one to as\n * a child (shown nested in the web UI).\n * @param opts.closeWithParent When true, this workflow is closed automatically\n * when its parent workflow is closed.\n * @param opts.onError Called if this workflow's worker process crashes.\n * @param opts.onClose Called when the workflow closes, whether via\n * `.close()` or a crash.\n */\n create: async (\n opts: {\n name?: string;\n workflowId?: string;\n closeOnDisconnect?: boolean;\n parentId?: string;\n closeWithParent?: boolean;\n } & WorkflowCallbacks = {},\n ): Promise<Workflow> => {\n const info = await this.client.trpc.workflow.create.mutate({\n workflowId: opts.workflowId,\n name: opts.name,\n parentId: opts.parentId,\n closeWithParent: opts.closeWithParent,\n closeOnDisconnect: opts.closeOnDisconnect,\n });\n const wf = new Workflow(info.id, this.client, {\n ...(opts.onError !== undefined && { onError: opts.onError }),\n ...(opts.onClose !== undefined && { onClose: opts.onClose }),\n });\n if (opts.closeOnDisconnect === true) {\n this._autoCloseWorkflows.add(wf);\n }\n return wf;\n },\n\n /** List all active workflows on the server. */\n list: (): Promise<WorkflowInfo[]> => this.client.trpc.workflow.list.query(),\n };\n\n /** Fetches the server's current health status (uptime, workflow counts, MediaMTX reachability). */\n health(): Promise<HealthStatus> {\n return this.client.health();\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n /**\n * Close all `closeOnDisconnect` workflows, then disconnect from the server.\n */\n async close(): Promise<void> {\n await Promise.allSettled(\n [...this._autoCloseWorkflows].map((wf) => wf.close()),\n );\n this.client.close();\n }\n}\n","/**\n * Stream selectors — used when calling node.subscribe() to pick which\n * streams flow from source to subscriber.\n */\n\nexport type StreamSelector = \"VIDEO\" | \"AUDIO\" | \"CAPTIONS\" | \"ALL\";\n\n// Narrowly typed (`as const`) so a spec literal like\n// `{ sourceSelector: selectAudio, audio: {...} }` discriminates the\n// SubscriptionSpec union correctly.\nexport const selectVideo = \"VIDEO\" as const;\nexport const selectAudio = \"AUDIO\" as const;\nexport const selectCaptions = \"CAPTIONS\" as const;\nexport const selectAll = \"ALL\" as const;\n"],"mappings":"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","names":["uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","uid","TypedEmitter","uid","uid","toError","TypedEmitter"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remavideo/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"tsx": "^4.21.0",
|
|
33
33
|
"typescript": "^5.8.3",
|
|
34
34
|
"vitest": "^3.0.0",
|
|
35
|
-
"@remavideo/server": "0.
|
|
35
|
+
"@remavideo/server": "0.19.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup",
|