@nodetool-ai/runtime 0.8.7 → 0.8.9-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/.nodetool-build-stamp +1 -1
  2. package/dist/context.d.ts +6 -2
  3. package/dist/context.d.ts.map +1 -1
  4. package/dist/context.js +13 -24
  5. package/dist/context.js.map +1 -1
  6. package/dist/host-binaries.d.ts +5 -5
  7. package/dist/host-binaries.js +5 -5
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/invocation-account.d.ts +1 -42
  13. package/dist/invocation-account.d.ts.map +1 -1
  14. package/dist/invocation-account.js +1 -59
  15. package/dist/invocation-account.js.map +1 -1
  16. package/dist/media/abort.d.ts +8 -0
  17. package/dist/media/abort.d.ts.map +1 -0
  18. package/dist/media/abort.js +18 -0
  19. package/dist/media/abort.js.map +1 -0
  20. package/dist/media/audio-encoder.d.ts +37 -0
  21. package/dist/media/audio-encoder.d.ts.map +1 -0
  22. package/dist/media/audio-encoder.js +118 -0
  23. package/dist/media/audio-encoder.js.map +1 -0
  24. package/dist/media/audio.d.ts +36 -0
  25. package/dist/media/audio.d.ts.map +1 -0
  26. package/dist/media/audio.js +84 -0
  27. package/dist/media/audio.js.map +1 -0
  28. package/dist/media/av.d.ts +51 -0
  29. package/dist/media/av.d.ts.map +1 -0
  30. package/dist/media/av.js +146 -0
  31. package/dist/media/av.js.map +1 -0
  32. package/dist/media/codecs.d.ts +9 -0
  33. package/dist/media/codecs.d.ts.map +1 -0
  34. package/dist/media/codecs.js +17 -0
  35. package/dist/media/codecs.js.map +1 -0
  36. package/dist/media/concat.d.ts +56 -0
  37. package/dist/media/concat.d.ts.map +1 -0
  38. package/dist/media/concat.js +319 -0
  39. package/dist/media/concat.js.map +1 -0
  40. package/dist/media/convert.d.ts +31 -0
  41. package/dist/media/convert.d.ts.map +1 -0
  42. package/dist/media/convert.js +114 -0
  43. package/dist/media/convert.js.map +1 -0
  44. package/dist/media/decode.d.ts +31 -0
  45. package/dist/media/decode.d.ts.map +1 -0
  46. package/dist/media/decode.js +85 -0
  47. package/dist/media/decode.js.map +1 -0
  48. package/dist/media/filter-graph.d.ts +53 -0
  49. package/dist/media/filter-graph.d.ts.map +1 -0
  50. package/dist/media/filter-graph.js +260 -0
  51. package/dist/media/filter-graph.js.map +1 -0
  52. package/dist/media/frames.d.ts +31 -0
  53. package/dist/media/frames.d.ts.map +1 -0
  54. package/dist/media/frames.js +112 -0
  55. package/dist/media/frames.js.map +1 -0
  56. package/dist/media/index.d.ts +26 -0
  57. package/dist/media/index.d.ts.map +1 -0
  58. package/dist/media/index.js +26 -0
  59. package/dist/media/index.js.map +1 -0
  60. package/dist/media/mux.d.ts +58 -0
  61. package/dist/media/mux.d.ts.map +1 -0
  62. package/dist/media/mux.js +234 -0
  63. package/dist/media/mux.js.map +1 -0
  64. package/dist/media/probe.d.ts +84 -0
  65. package/dist/media/probe.d.ts.map +1 -0
  66. package/dist/media/probe.js +133 -0
  67. package/dist/media/probe.js.map +1 -0
  68. package/dist/media/rgba.d.ts +78 -0
  69. package/dist/media/rgba.d.ts.map +1 -0
  70. package/dist/media/rgba.js +285 -0
  71. package/dist/media/rgba.js.map +1 -0
  72. package/dist/media/testing.d.ts +119 -0
  73. package/dist/media/testing.d.ts.map +1 -0
  74. package/dist/media/testing.js +343 -0
  75. package/dist/media/testing.js.map +1 -0
  76. package/dist/media/transcode.d.ts +17 -0
  77. package/dist/media/transcode.d.ts.map +1 -0
  78. package/dist/media/transcode.js +19 -0
  79. package/dist/media/transcode.js.map +1 -0
  80. package/dist/media/transform.d.ts +61 -0
  81. package/dist/media/transform.d.ts.map +1 -0
  82. package/dist/media/transform.js +371 -0
  83. package/dist/media/transform.js.map +1 -0
  84. package/dist/media/verify.d.ts +17 -0
  85. package/dist/media/verify.d.ts.map +1 -0
  86. package/dist/media/verify.js +65 -0
  87. package/dist/media/verify.js.map +1 -0
  88. package/dist/media-loader.d.ts +8 -0
  89. package/dist/media-loader.d.ts.map +1 -0
  90. package/dist/media-loader.js +10 -0
  91. package/dist/media-loader.js.map +1 -0
  92. package/dist/prefixed-storage-adapter.d.ts.map +1 -1
  93. package/dist/prefixed-storage-adapter.js +1 -1
  94. package/dist/prefixed-storage-adapter.js.map +1 -1
  95. package/dist/providers/anthropic-provider.d.ts +21 -0
  96. package/dist/providers/anthropic-provider.d.ts.map +1 -1
  97. package/dist/providers/anthropic-provider.js +82 -17
  98. package/dist/providers/anthropic-provider.js.map +1 -1
  99. package/dist/providers/claude-agent-provider.js +14 -2
  100. package/dist/providers/claude-agent-provider.js.map +1 -1
  101. package/dist/providers/codex-provider.d.ts +10 -0
  102. package/dist/providers/codex-provider.d.ts.map +1 -1
  103. package/dist/providers/codex-provider.js +29 -3
  104. package/dist/providers/codex-provider.js.map +1 -1
  105. package/dist/providers/data-uri.d.ts +6 -0
  106. package/dist/providers/data-uri.d.ts.map +1 -0
  107. package/dist/providers/data-uri.js +19 -0
  108. package/dist/providers/data-uri.js.map +1 -0
  109. package/dist/providers/dreamina-provider.d.ts +1 -1
  110. package/dist/providers/dreamina-provider.d.ts.map +1 -1
  111. package/dist/providers/dreamina-provider.js +54 -72
  112. package/dist/providers/dreamina-provider.js.map +1 -1
  113. package/dist/providers/fake-provider.js +1 -1
  114. package/dist/providers/gemini-provider.d.ts.map +1 -1
  115. package/dist/providers/gemini-provider.js +23 -0
  116. package/dist/providers/gemini-provider.js.map +1 -1
  117. package/dist/providers/meta-transcription.d.ts +1 -5
  118. package/dist/providers/meta-transcription.d.ts.map +1 -1
  119. package/dist/providers/meta-transcription.js +9 -55
  120. package/dist/providers/meta-transcription.js.map +1 -1
  121. package/dist/providers/node-llama-cpp-trending.generated.js +121 -121
  122. package/dist/providers/node-llama-cpp-trending.generated.js.map +1 -1
  123. package/dist/providers/ollama-provider.d.ts.map +1 -1
  124. package/dist/providers/ollama-provider.js +1 -17
  125. package/dist/providers/ollama-provider.js.map +1 -1
  126. package/dist/providers/openai-provider.d.ts +12 -0
  127. package/dist/providers/openai-provider.d.ts.map +1 -1
  128. package/dist/providers/openai-provider.js +27 -2
  129. package/dist/providers/openai-provider.js.map +1 -1
  130. package/dist/providers/openrouter-provider.js +1 -1
  131. package/dist/providers/openrouter-provider.js.map +1 -1
  132. package/dist/providers/requesty-provider.js +1 -1
  133. package/dist/providers/requesty-provider.js.map +1 -1
  134. package/dist/providers/safe-url.d.ts +1 -69
  135. package/dist/providers/safe-url.d.ts.map +1 -1
  136. package/dist/providers/safe-url.js +1 -284
  137. package/dist/providers/safe-url.js.map +1 -1
  138. package/dist/providers/topaz-provider.d.ts +1 -1
  139. package/dist/providers/topaz-provider.js +1 -1
  140. package/dist/providers/video-frame-fallback.d.ts +1 -1
  141. package/dist/providers/video-frame-fallback.js +4 -4
  142. package/dist/providers/video-frame-fallback.js.map +1 -1
  143. package/dist/providers/video-frames.d.ts +6 -16
  144. package/dist/providers/video-frames.d.ts.map +1 -1
  145. package/dist/providers/video-frames.js +48 -116
  146. package/dist/providers/video-frames.js.map +1 -1
  147. package/dist/python-bridge-base.d.ts +2 -0
  148. package/dist/python-bridge-base.d.ts.map +1 -1
  149. package/dist/python-bridge-base.js +9 -26
  150. package/dist/python-bridge-base.js.map +1 -1
  151. package/dist/storage-workspace.d.ts +1 -61
  152. package/dist/storage-workspace.d.ts.map +1 -1
  153. package/dist/storage-workspace.js +2 -430
  154. package/dist/storage-workspace.js.map +1 -1
  155. package/dist/testing.d.ts.map +1 -1
  156. package/dist/testing.js +57 -1
  157. package/dist/testing.js.map +1 -1
  158. package/dist/trim-video-window.d.ts.map +1 -1
  159. package/dist/trim-video-window.js +2 -32
  160. package/dist/trim-video-window.js.map +1 -1
  161. package/dist/workspace.d.ts +1 -118
  162. package/dist/workspace.d.ts.map +1 -1
  163. package/dist/workspace.js +1 -111
  164. package/dist/workspace.js.map +1 -1
  165. package/dist/yt-dlp-format.d.ts +22 -0
  166. package/dist/yt-dlp-format.d.ts.map +1 -0
  167. package/dist/yt-dlp-format.js +90 -0
  168. package/dist/yt-dlp-format.js.map +1 -0
  169. package/package.json +23 -6
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Whole-file conversions on Mediabunny: trim with re-encode, and audio to WAV.
3
+ * Mediabunny encodes through `@mediabunny/server`, which runs node-av's codecs.
4
+ */
5
+ import { ALL_FORMATS, BufferSource, BufferTarget, Conversion, FilePathSource, FilePathTarget, Input, Mp4OutputFormat, Output, WavOutputFormat } from "mediabunny";
6
+ import { registerMediabunnyServer } from "@mediabunny/server";
7
+ import { abortError, throwIfAborted } from "./av.js";
8
+ let serverRegistered = false;
9
+ /**
10
+ * Register Mediabunny's Node codecs once per process. Hardware acceleration
11
+ * stays off: a server render must not depend on which GPU the host has, and
12
+ * probing every hardware API on each codec open costs time and log noise.
13
+ */
14
+ export function ensureMediabunnyServer() {
15
+ if (serverRegistered)
16
+ return;
17
+ serverRegistered = true;
18
+ registerMediabunnyServer({ hardwareContext: null });
19
+ }
20
+ function inputFrom(source) {
21
+ return new Input({
22
+ source: typeof source === "string" ? new FilePathSource(source) : new BufferSource(source),
23
+ formats: ALL_FORMATS
24
+ });
25
+ }
26
+ async function runConversion(conversion, signal) {
27
+ if (!conversion.isValid) {
28
+ const reasons = conversion.discardedTracks.map((track) => track.reason).join(", ");
29
+ throw new Error(`The media cannot be converted${reasons ? `: ${reasons}` : ""}`);
30
+ }
31
+ const cancel = () => {
32
+ void conversion.cancel();
33
+ };
34
+ signal?.addEventListener("abort", cancel, { once: true });
35
+ try {
36
+ await conversion.execute();
37
+ }
38
+ catch (error) {
39
+ if (signal?.aborted)
40
+ throw abortError(signal);
41
+ throw error;
42
+ }
43
+ finally {
44
+ signal?.removeEventListener("abort", cancel);
45
+ }
46
+ }
47
+ /**
48
+ * Re-encode `[startSec, endSec)` of `source` as an MP4 that starts at zero,
49
+ * H.264 and AAC with the index at the front: the in-process form of
50
+ * `ffmpeg -i in -ss S -t D -c:v libx264 -c:a aac -movflags +faststart`.
51
+ */
52
+ export async function trimVideo(source, startSec, endSec, options = {}) {
53
+ throwIfAborted(options.signal);
54
+ ensureMediabunnyServer();
55
+ const input = inputFrom(source);
56
+ try {
57
+ const target = new BufferTarget();
58
+ const output = new Output({
59
+ format: new Mp4OutputFormat({ fastStart: "in-memory" }),
60
+ target
61
+ });
62
+ const conversion = await Conversion.init({
63
+ input,
64
+ output,
65
+ trim: { start: startSec, end: endSec },
66
+ video: { codec: "avc", forceTranscode: true },
67
+ audio: { codec: "aac", forceTranscode: true },
68
+ showWarnings: false
69
+ });
70
+ await runConversion(conversion, options.signal);
71
+ if (!target.buffer)
72
+ throw new Error("The trim produced no file");
73
+ return new Uint8Array(target.buffer);
74
+ }
75
+ finally {
76
+ input.dispose();
77
+ }
78
+ }
79
+ /**
80
+ * Write `source`'s first audio track to `outPath` as 16-bit PCM WAV, at its own
81
+ * rate and channel count unless `sampleRate` or `channels` say otherwise, as
82
+ * `ffmpeg -i in -vn [-ar R] [-ac C] -acodec pcm_s16le out.wav` did. Throws when
83
+ * the source has no audio track.
84
+ */
85
+ export async function convertAudioToWav(source, outPath, options = {}) {
86
+ throwIfAborted(options.signal);
87
+ ensureMediabunnyServer();
88
+ const input = inputFrom(source);
89
+ try {
90
+ if (!(await input.getPrimaryAudioTrack())) {
91
+ throw new Error("The media has no audio track");
92
+ }
93
+ const output = new Output({
94
+ format: new WavOutputFormat(),
95
+ target: new FilePathTarget(outPath)
96
+ });
97
+ const conversion = await Conversion.init({
98
+ input,
99
+ output,
100
+ video: { discard: true },
101
+ audio: {
102
+ codec: "pcm-s16",
103
+ sampleRate: options.sampleRate,
104
+ numberOfChannels: options.channels
105
+ },
106
+ showWarnings: false
107
+ });
108
+ await runConversion(conversion, options.signal);
109
+ }
110
+ finally {
111
+ input.dispose();
112
+ }
113
+ }
114
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/media/convert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Demux and decode one stream of a file with node-av, with the timestamp
3
+ * handling the ffmpeg CLI applies to an input: the container's start time is
4
+ * subtracted, so the first frame of a file sits near zero.
5
+ */
6
+ import { type Frame, type Stream } from "node-av";
7
+ export type MediaKind = "audio" | "video";
8
+ /** One decoded stream of an open file. */
9
+ export interface DecodedStream {
10
+ readonly stream: Stream;
11
+ /** Seconds subtracted from every frame: the container start time plus the seek. */
12
+ readonly offsetSec: number;
13
+ /**
14
+ * Decoded frames in presentation order, timestamps already shifted by
15
+ * {@link offsetSec}. The caller owns each frame and frees it.
16
+ */
17
+ frames(): AsyncGenerator<Frame>;
18
+ /** Free the decoder and the demuxer. Waits for an iteration in progress. */
19
+ close(): Promise<void>;
20
+ }
21
+ /**
22
+ * Open `filePath` and decode its first `kind` stream. `seekSec` seeks to the
23
+ * keyframe at or before that source time; the frames before it are still
24
+ * decoded, as the CLI's `-ss` before `-i` decodes them, and the caller trims.
25
+ * Returns `null` when the file has no stream of that kind.
26
+ */
27
+ export declare function openDecodedStream(filePath: string, kind: MediaKind, options?: {
28
+ seekSec?: number;
29
+ signal?: AbortSignal;
30
+ }): Promise<DecodedStream | null>;
31
+ //# sourceMappingURL=decode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../../src/media/decode.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAKL,KAAK,KAAK,EACV,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAIjB,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1C,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC;IAChC,4EAA4E;IAC5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAID;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,SAAS,EACf,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GACvD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAiE/B"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Demux and decode one stream of a file with node-av, with the timestamp
3
+ * handling the ffmpeg CLI applies to an input: the container's start time is
4
+ * subtracted, so the first frame of a file sits near zero.
5
+ */
6
+ import { AV_NOPTS_VALUE, AVSEEK_FLAG_BACKWARD, Decoder, Demuxer } from "node-av";
7
+ import { abortError, quietLibav, startTimeSeconds, throwIfAborted } from "./av.js";
8
+ const AV_TIME_BASE_Q = { num: 1, den: 1_000_000 };
9
+ /**
10
+ * Open `filePath` and decode its first `kind` stream. `seekSec` seeks to the
11
+ * keyframe at or before that source time; the frames before it are still
12
+ * decoded, as the CLI's `-ss` before `-i` decodes them, and the caller trims.
13
+ * Returns `null` when the file has no stream of that kind.
14
+ */
15
+ export async function openDecodedStream(filePath, kind, options = {}) {
16
+ const { signal } = options;
17
+ quietLibav();
18
+ throwIfAborted(signal);
19
+ const demuxer = await Demuxer.open(filePath, { signal });
20
+ let decoder = null;
21
+ try {
22
+ const found = kind === "audio" ? demuxer.audio() : demuxer.video();
23
+ if (!found) {
24
+ await demuxer.close();
25
+ return null;
26
+ }
27
+ const stream = found;
28
+ const formatStartSec = startTimeSeconds(demuxer.getFormatContext().startTime, AV_TIME_BASE_Q);
29
+ const seekSec = options.seekSec && options.seekSec > 0 ? options.seekSec : 0;
30
+ if (seekSec > 0) {
31
+ // A failed seek leaves the demuxer at the start, which is still correct:
32
+ // the caller trims everything before the requested time.
33
+ await demuxer.seek(formatStartSec + seekSec, -1, AVSEEK_FLAG_BACKWARD);
34
+ }
35
+ decoder = await Decoder.create(stream, { signal });
36
+ const activeDecoder = decoder;
37
+ const offsetSec = formatStartSec + seekSec;
38
+ let active = null;
39
+ let closed = false;
40
+ async function* decodeFrames() {
41
+ for await (const frame of activeDecoder.frames(demuxer.packets(stream.index))) {
42
+ if (!frame)
43
+ continue;
44
+ if (closed || signal?.aborted) {
45
+ frame.free();
46
+ if (signal?.aborted)
47
+ throw abortError(signal);
48
+ return;
49
+ }
50
+ if (offsetSec !== 0 && frame.pts !== AV_NOPTS_VALUE) {
51
+ const timeBase = frame.timeBase.den > 0 ? frame.timeBase : stream.timeBase;
52
+ frame.pts -= BigInt(Math.round((offsetSec * timeBase.den) / timeBase.num));
53
+ }
54
+ yield frame;
55
+ }
56
+ }
57
+ return {
58
+ stream,
59
+ offsetSec,
60
+ frames() {
61
+ if (active)
62
+ throw new Error("A decoded stream can be iterated once");
63
+ active = decodeFrames();
64
+ return active;
65
+ },
66
+ async close() {
67
+ if (closed)
68
+ return;
69
+ closed = true;
70
+ // `return()` waits for a pull in progress, so native calls finish
71
+ // before the decoder and demuxer they use are freed.
72
+ if (active)
73
+ await active.return(undefined);
74
+ activeDecoder.close();
75
+ await demuxer.close();
76
+ }
77
+ };
78
+ }
79
+ catch (error) {
80
+ decoder?.close();
81
+ await demuxer.close();
82
+ throw error;
83
+ }
84
+ }
85
+ //# sourceMappingURL=decode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode.js","sourceRoot":"","sources":["../../src/media/decode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * libavfilter graphs over decoded files, on node-av. The graph strings are the
3
+ * ones the ffmpeg CLI took in `-vf`, `-af` and `-filter_complex`, unchanged.
4
+ */
5
+ import { Frame, type IRational, type Stream } from "node-av";
6
+ import { type MediaKind } from "./decode.js";
7
+ /** Filtered frames from an open graph. Each frame is the caller's to free. */
8
+ export interface FilterRun {
9
+ frames(): AsyncGenerator<Frame>;
10
+ /** The output's frame rate once a frame has come out, or null when it has none. */
11
+ frameRate(): IRational | null;
12
+ /** Stop the run and free the graph and every decoder. */
13
+ close(): Promise<void>;
14
+ }
15
+ /**
16
+ * A video stream's frame rate, the hint the CLI gave its buffer source so
17
+ * the encoder learns the output rate. Null for audio or an unknown rate.
18
+ */
19
+ export declare function streamFrameRate(stream: Stream): IRational | null;
20
+ /**
21
+ * Run `filter` (a `-vf` or `-af` chain) over the first `kind` stream of
22
+ * `filePath`. Returns `null` when the file has no such stream.
23
+ */
24
+ export declare function openFilterGraph(opts: {
25
+ filePath: string;
26
+ kind: MediaKind;
27
+ filter: string;
28
+ seekSec?: number;
29
+ signal?: AbortSignal;
30
+ }): Promise<FilterRun | null>;
31
+ /** One input of a filter_complex graph: a file stream bound to a link label. */
32
+ export interface FilterComplexInput {
33
+ filePath: string;
34
+ kind: MediaKind;
35
+ /** The graph's link label without brackets, e.g. `1:a`. */
36
+ label: string;
37
+ }
38
+ /**
39
+ * Run a `-filter_complex` graph with one output. Every input is a file stream
40
+ * the graph names by label, as `[1:a]` named the CLI's second input; a file
41
+ * may appear more than once under different labels. Throws when an input has
42
+ * no stream of its kind, as the CLI did for a stream specifier that matched
43
+ * nothing.
44
+ */
45
+ export declare function openFilterComplex(opts: {
46
+ inputs: FilterComplexInput[];
47
+ graph: string;
48
+ /** The output link label without brackets, e.g. `aout`. */
49
+ output: string;
50
+ outputKind: MediaKind;
51
+ signal?: AbortSignal;
52
+ }): Promise<FilterRun>;
53
+ //# sourceMappingURL=filter-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-graph.d.ts","sourceRoot":"","sources":["../../src/media/filter-graph.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAQL,KAAK,EACL,KAAK,SAAS,EACd,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAEpF,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC;IAChC,mFAAmF;IACnF,SAAS,IAAI,SAAS,GAAG,IAAI,CAAC;IAC9B,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAKhE;AAoDD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAyB5B;AA4HD,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,SAAS,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,SAAS,CAAC,CAuCrB"}
@@ -0,0 +1,260 @@
1
+ /**
2
+ * libavfilter graphs over decoded files, on node-av. The graph strings are the
3
+ * ones the ffmpeg CLI took in `-vf`, `-af` and `-filter_complex`, unchanged.
4
+ */
5
+ import { AV_NOPTS_VALUE, AVERROR_EOF, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO, FFmpegError, FilterAPI, FilterComplexAPI, Frame } from "node-av";
6
+ import { abortError, throwIfAborted } from "./av.js";
7
+ import { openDecodedStream } from "./decode.js";
8
+ /**
9
+ * A video stream's frame rate, the hint the CLI gave its buffer source so
10
+ * the encoder learns the output rate. Null for audio or an unknown rate.
11
+ */
12
+ export function streamFrameRate(stream) {
13
+ for (const rate of [stream.avgFrameRate, stream.rFrameRate]) {
14
+ if (rate.num > 0 && rate.den > 0)
15
+ return { num: rate.num, den: rate.den };
16
+ }
17
+ return null;
18
+ }
19
+ /** Decoded frames followed by the end-of-stream marker a filter needs to flush. */
20
+ async function* withEndOfStream(frames) {
21
+ yield* frames;
22
+ yield null;
23
+ }
24
+ function filterRun(produce, release, frameRate, signal) {
25
+ let active = null;
26
+ let closed = false;
27
+ async function* frames() {
28
+ try {
29
+ for await (const frame of produce()) {
30
+ if (!frame)
31
+ continue;
32
+ if (signal?.aborted) {
33
+ frame.free();
34
+ throw abortError(signal);
35
+ }
36
+ yield frame;
37
+ }
38
+ }
39
+ catch (error) {
40
+ // A graph that ends itself (`atrim=end=`, `trim`) refuses the next
41
+ // input with EOF. Everything it produced was already received.
42
+ if (error instanceof FFmpegError && error.code === AVERROR_EOF)
43
+ return;
44
+ throw error;
45
+ }
46
+ }
47
+ return {
48
+ frames() {
49
+ if (active)
50
+ throw new Error("A filter run can be iterated once");
51
+ active = frames();
52
+ return active;
53
+ },
54
+ frameRate() {
55
+ const rate = frameRate();
56
+ return rate && rate.num > 0 && rate.den > 0 ? rate : null;
57
+ },
58
+ async close() {
59
+ if (closed)
60
+ return;
61
+ closed = true;
62
+ if (active)
63
+ await active.return(undefined);
64
+ await release();
65
+ }
66
+ };
67
+ }
68
+ /**
69
+ * Run `filter` (a `-vf` or `-af` chain) over the first `kind` stream of
70
+ * `filePath`. Returns `null` when the file has no such stream.
71
+ */
72
+ export async function openFilterGraph(opts) {
73
+ const decoded = await openDecodedStream(opts.filePath, opts.kind, {
74
+ seekSec: opts.seekSec,
75
+ signal: opts.signal
76
+ });
77
+ if (!decoded)
78
+ return null;
79
+ let graph;
80
+ try {
81
+ graph = FilterAPI.create(opts.filter, {
82
+ framerate: opts.kind === "video" ? (streamFrameRate(decoded.stream) ?? undefined) : undefined,
83
+ signal: opts.signal
84
+ });
85
+ }
86
+ catch (error) {
87
+ await decoded.close();
88
+ throw error;
89
+ }
90
+ return filterRun(() => graph.frames(withEndOfStream(decoded.frames())), async () => {
91
+ await decoded.close();
92
+ graph.close();
93
+ }, () => graph.frameRate, opts.signal);
94
+ }
95
+ function frameSeconds(frame) {
96
+ const { timeBase } = frame;
97
+ if (frame.pts === AV_NOPTS_VALUE || !(timeBase.den > 0))
98
+ return Number.NEGATIVE_INFINITY;
99
+ return (Number(frame.pts) * timeBase.num) / timeBase.den;
100
+ }
101
+ /**
102
+ * Feed a filter_complex graph the way the ffmpeg CLI does: always the input
103
+ * whose next frame is earliest, each input ended on its own when it runs out,
104
+ * output collected after every frame.
105
+ *
106
+ * node-av's `FilterComplexAPI.frames()` reads one frame from every input in
107
+ * lockstep and finalizes the whole graph as soon as any one input ends, so a
108
+ * mix of a short clip and a long one stops at the short one (and an `apad`
109
+ * after it never ends). Every input gets its first frame before any is
110
+ * ended, because the graph only configures once each input has a format.
111
+ */
112
+ async function* driveFilterComplex(graph, output, inputs, decoded) {
113
+ const pending = inputs.map((input, index) => ({
114
+ label: input.label,
115
+ frames: decoded[index].frames()[Symbol.asyncIterator](),
116
+ next: null,
117
+ done: false
118
+ }));
119
+ let ended = false;
120
+ async function* drain() {
121
+ for (;;) {
122
+ const out = await graph.receive(output);
123
+ if (out === null)
124
+ return;
125
+ if (!(out instanceof Frame)) {
126
+ ended = true;
127
+ return;
128
+ }
129
+ yield out;
130
+ }
131
+ }
132
+ /**
133
+ * Send `input`'s pending frame. A branch that ends itself (`atrim=end=`)
134
+ * refuses further frames with EOF: that input is finished, the graph is not.
135
+ */
136
+ const send = async (input) => {
137
+ const frame = input.next;
138
+ input.next = null;
139
+ try {
140
+ await graph.process(input.label, frame);
141
+ }
142
+ catch (error) {
143
+ if (!(error instanceof FFmpegError && error.code === AVERROR_EOF))
144
+ throw error;
145
+ input.done = true;
146
+ }
147
+ finally {
148
+ frame.free();
149
+ }
150
+ };
151
+ const advance = async (input) => {
152
+ if (input.done) {
153
+ // Its branch ended; release what is left of the decode.
154
+ await input.frames.return?.(undefined);
155
+ return;
156
+ }
157
+ const result = await input.frames.next();
158
+ if (result.done) {
159
+ input.done = true;
160
+ input.next = null;
161
+ }
162
+ else {
163
+ input.next = result.value;
164
+ }
165
+ };
166
+ for (const input of pending) {
167
+ await advance(input);
168
+ if (input.done)
169
+ throw new Error(`[${input.label}] decoded to no frames`);
170
+ }
171
+ for (const input of pending) {
172
+ await send(input);
173
+ const ended = input.done;
174
+ await advance(input);
175
+ // A one-frame input runs out here; the graph only learns that from a flush.
176
+ if (input.done && !ended)
177
+ await graph.flush(input.label);
178
+ }
179
+ yield* drain();
180
+ for (;;) {
181
+ let earliest = null;
182
+ for (const input of pending) {
183
+ if (input.done || !input.next)
184
+ continue;
185
+ if (!earliest || frameSeconds(input.next) < frameSeconds(earliest.next))
186
+ earliest = input;
187
+ }
188
+ if (!earliest)
189
+ break;
190
+ await send(earliest);
191
+ yield* drain();
192
+ if (ended)
193
+ return;
194
+ const wasDone = earliest.done;
195
+ await advance(earliest);
196
+ if (earliest.done && !wasDone) {
197
+ await graph.flush(earliest.label);
198
+ yield* drain();
199
+ if (ended)
200
+ return;
201
+ }
202
+ }
203
+ // Every input has ended; the graph runs out (or pads forever, for the
204
+ // caller to stop).
205
+ while (!ended) {
206
+ let produced = false;
207
+ for await (const frame of drain()) {
208
+ produced = true;
209
+ yield frame;
210
+ }
211
+ if (!produced)
212
+ return;
213
+ }
214
+ }
215
+ /**
216
+ * Run a `-filter_complex` graph with one output. Every input is a file stream
217
+ * the graph names by label, as `[1:a]` named the CLI's second input; a file
218
+ * may appear more than once under different labels. Throws when an input has
219
+ * no stream of its kind, as the CLI did for a stream specifier that matched
220
+ * nothing.
221
+ */
222
+ export async function openFilterComplex(opts) {
223
+ throwIfAborted(opts.signal);
224
+ const decoded = [];
225
+ try {
226
+ for (const input of opts.inputs) {
227
+ const stream = await openDecodedStream(input.filePath, input.kind, {
228
+ signal: opts.signal
229
+ });
230
+ if (!stream) {
231
+ throw new Error(`${input.filePath} has no ${input.kind} stream for [${input.label}]`);
232
+ }
233
+ decoded.push(stream);
234
+ }
235
+ const firstVideo = opts.inputs.findIndex((input) => input.kind === "video");
236
+ const graph = FilterComplexAPI.create(opts.graph, {
237
+ framerate: firstVideo >= 0 ? (streamFrameRate(decoded[firstVideo].stream) ?? undefined) : undefined,
238
+ inputs: opts.inputs.map((input) => ({ label: input.label })),
239
+ outputs: [
240
+ {
241
+ label: opts.output,
242
+ mediaType: opts.outputKind === "audio" ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO
243
+ }
244
+ ],
245
+ signal: opts.signal
246
+ });
247
+ const output = opts.output;
248
+ return filterRun(() => driveFilterComplex(graph, output, opts.inputs, decoded), async () => {
249
+ for (const stream of decoded)
250
+ await stream.close();
251
+ graph.close();
252
+ }, () => graph.frameRate, opts.signal);
253
+ }
254
+ catch (error) {
255
+ for (const stream of decoded)
256
+ await stream.close();
257
+ throw error;
258
+ }
259
+ }
260
+ //# sourceMappingURL=filter-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-graph.js","sourceRoot":"","sources":["../../src/media/filter-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Video frames as PNG files, the in-process form of `ffmpeg -i in -vf <filter>
3
+ * frame_%d.png`, `-ss T -i in -frames:v 1 frame.png` and `-sseof`.
4
+ */
5
+ /**
6
+ * PNG bytes of every frame `filter` passes, in order. Frames are RGB, or RGBA
7
+ * with `alpha`, as the CLI's PNG encoder chose from the source. `seekSec`
8
+ * starts at the frame shown at that time; `limit` stops after that many.
9
+ * Yields nothing when the file has no video stream.
10
+ */
11
+ export declare function pngFrames(opts: {
12
+ filePath: string;
13
+ filter?: string;
14
+ seekSec?: number;
15
+ limit?: number;
16
+ alpha?: boolean;
17
+ signal?: AbortSignal;
18
+ }): AsyncGenerator<Uint8Array>;
19
+ /**
20
+ * The last frame of `filePath` as PNG, or null when it has no picture: the
21
+ * in-process form of `ffmpeg -sseof -1 -i in -update 1 -frames:v 1 out.png`.
22
+ * Decodes only the last `tailSec` seconds.
23
+ */
24
+ export declare function lastFramePng(opts: {
25
+ filePath: string;
26
+ durationSec: number | null;
27
+ alpha?: boolean;
28
+ tailSec?: number;
29
+ signal?: AbortSignal;
30
+ }): Promise<Uint8Array | null>;
31
+ //# sourceMappingURL=frames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/media/frames.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6BH;;;;;GAKG;AACH,wBAAuB,SAAS,CAAC,IAAI,EAAE;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,cAAc,CAAC,UAAU,CAAC,CAkC7B;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAyB7B"}