@editframe/assets 0.37.3-beta → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/Probe.cjs +494 -0
  2. package/dist/Probe.cjs.map +1 -0
  3. package/dist/Probe.d.cts +1135 -0
  4. package/dist/Probe.d.ts +26 -26
  5. package/dist/Probe.js +2 -0
  6. package/dist/Probe.js.map +1 -1
  7. package/dist/VideoRenderOptions.cjs +42 -0
  8. package/dist/VideoRenderOptions.cjs.map +1 -0
  9. package/dist/VideoRenderOptions.d.cts +198 -0
  10. package/dist/VideoRenderOptions.d.ts +48 -48
  11. package/dist/_virtual/rolldown_runtime.cjs +25 -0
  12. package/dist/generateFragmentIndex.cjs +420 -0
  13. package/dist/generateFragmentIndex.cjs.map +1 -0
  14. package/dist/generateFragmentIndex.d.cts +8 -0
  15. package/dist/generateFragmentIndex.js.map +1 -1
  16. package/dist/generateSingleTrack.cjs +82 -0
  17. package/dist/generateSingleTrack.cjs.map +1 -0
  18. package/dist/idempotentTask.cjs +152 -0
  19. package/dist/idempotentTask.cjs.map +1 -0
  20. package/dist/idempotentTask.d.cts +11 -0
  21. package/dist/idempotentTask.js.map +1 -1
  22. package/dist/index.cjs +27 -0
  23. package/dist/index.d.cts +11 -0
  24. package/dist/md5.cjs +64 -0
  25. package/dist/md5.cjs.map +1 -0
  26. package/dist/md5.d.cts +11 -0
  27. package/dist/md5.js +4 -2
  28. package/dist/md5.js.map +1 -1
  29. package/dist/tasks/cacheImage.cjs +28 -0
  30. package/dist/tasks/cacheImage.cjs.map +1 -0
  31. package/dist/tasks/cacheImage.d.cts +7 -0
  32. package/dist/tasks/findOrCreateCaptions.cjs +58 -0
  33. package/dist/tasks/findOrCreateCaptions.cjs.map +1 -0
  34. package/dist/tasks/findOrCreateCaptions.d.cts +8 -0
  35. package/dist/tasks/findOrCreateCaptions.js.map +1 -1
  36. package/dist/tasks/generateScrubTrack.cjs +107 -0
  37. package/dist/tasks/generateScrubTrack.cjs.map +1 -0
  38. package/dist/tasks/generateScrubTrack.d.cts +12 -0
  39. package/dist/tasks/generateScrubTrack.js.map +1 -1
  40. package/dist/tasks/generateTrack.cjs +34 -0
  41. package/dist/tasks/generateTrack.cjs.map +1 -0
  42. package/dist/tasks/generateTrack.d.cts +8 -0
  43. package/dist/tasks/generateTrackFragmentIndex.cjs +69 -0
  44. package/dist/tasks/generateTrackFragmentIndex.cjs.map +1 -0
  45. package/dist/tasks/generateTrackFragmentIndex.d.cts +9 -0
  46. package/dist/truncateDecimal.cjs +10 -0
  47. package/dist/truncateDecimal.cjs.map +1 -0
  48. package/package.json +19 -6
  49. package/tsdown.config.ts +1 -0
  50. package/types.json +1 -1
package/dist/Probe.cjs ADDED
@@ -0,0 +1,494 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ const require_truncateDecimal = require('./truncateDecimal.cjs');
3
+ let node_child_process = require("node:child_process");
4
+ node_child_process = require_rolldown_runtime.__toESM(node_child_process);
5
+ let node_util = require("node:util");
6
+ node_util = require_rolldown_runtime.__toESM(node_util);
7
+ let node_fs = require("node:fs");
8
+ node_fs = require_rolldown_runtime.__toESM(node_fs);
9
+ let zod = require("zod");
10
+ zod = require_rolldown_runtime.__toESM(zod);
11
+ let debug = require("debug");
12
+ debug = require_rolldown_runtime.__toESM(debug);
13
+
14
+ //#region src/Probe.ts
15
+ const execPromise = (0, node_util.promisify)(node_child_process.exec);
16
+ const log = (0, debug.default)("ef:assets:probe");
17
+ const AudioStreamSchema = zod.object({
18
+ index: zod.number(),
19
+ codec_name: zod.string(),
20
+ codec_long_name: zod.string(),
21
+ codec_type: zod.literal("audio"),
22
+ codec_tag_string: zod.string(),
23
+ codec_tag: zod.string(),
24
+ sample_fmt: zod.string(),
25
+ sample_rate: zod.string(),
26
+ channels: zod.number(),
27
+ channel_layout: zod.string().optional(),
28
+ bits_per_sample: zod.number(),
29
+ initial_padding: zod.number().optional(),
30
+ r_frame_rate: zod.string(),
31
+ avg_frame_rate: zod.string(),
32
+ time_base: zod.string(),
33
+ start_pts: zod.number().optional(),
34
+ start_time: zod.coerce.number().optional(),
35
+ duration_ts: zod.number(),
36
+ duration: zod.coerce.number(),
37
+ bit_rate: zod.string(),
38
+ disposition: zod.record(zod.unknown())
39
+ });
40
+ const VideoStreamSchema = zod.object({
41
+ index: zod.number(),
42
+ codec_name: zod.string(),
43
+ codec_long_name: zod.string(),
44
+ codec_type: zod.literal("video"),
45
+ codec_tag_string: zod.string(),
46
+ codec_tag: zod.string(),
47
+ profile: zod.string().optional(),
48
+ level: zod.number().optional(),
49
+ width: zod.number(),
50
+ height: zod.number(),
51
+ coded_width: zod.number(),
52
+ coded_height: zod.number(),
53
+ r_frame_rate: zod.string(),
54
+ avg_frame_rate: zod.string(),
55
+ time_base: zod.string(),
56
+ start_pts: zod.number().optional(),
57
+ start_time: zod.coerce.number().optional(),
58
+ duration_ts: zod.number().optional(),
59
+ duration: zod.coerce.number().optional(),
60
+ bit_rate: zod.string().optional(),
61
+ disposition: zod.record(zod.unknown())
62
+ });
63
+ const ProbeFormatSchema = zod.object({
64
+ filename: zod.string(),
65
+ nb_streams: zod.number(),
66
+ nb_programs: zod.number(),
67
+ format_name: zod.string(),
68
+ format_long_name: zod.string(),
69
+ start_time: zod.string().optional(),
70
+ duration: zod.string().optional(),
71
+ size: zod.string().optional(),
72
+ bit_rate: zod.string().optional(),
73
+ probe_score: zod.number()
74
+ });
75
+ const DataStreamSchema = zod.object({
76
+ index: zod.number(),
77
+ codec_type: zod.literal("data"),
78
+ duration: zod.string().optional(),
79
+ duration_ts: zod.number().optional(),
80
+ start_pts: zod.number().optional()
81
+ });
82
+ const StreamSchema = zod.discriminatedUnion("codec_type", [
83
+ AudioStreamSchema,
84
+ VideoStreamSchema,
85
+ DataStreamSchema
86
+ ]);
87
+ const PacketSchema = zod.object({
88
+ stream_index: zod.number(),
89
+ pts: zod.number(),
90
+ pts_time: zod.coerce.number(),
91
+ dts: zod.number(),
92
+ dts_time: zod.coerce.number(),
93
+ duration: zod.coerce.number().optional(),
94
+ pos: zod.coerce.number().optional(),
95
+ flags: zod.string().optional()
96
+ });
97
+ const ProbeSchema = zod.object({
98
+ streams: zod.array(StreamSchema),
99
+ format: ProbeFormatSchema
100
+ });
101
+ const PacketProbeSchema = zod.object({
102
+ packets: zod.array(PacketSchema),
103
+ format: ProbeFormatSchema,
104
+ streams: zod.array(StreamSchema)
105
+ });
106
+ const buildProbeArgs = (options) => {
107
+ const streamEntries = "stream=index,codec_name,codec_long_name,codec_type,codec_tag_string,codec_tag,profile,level,width,height,coded_width,coded_height,r_frame_rate,avg_frame_rate,time_base,start_pts,start_time,duration_ts,duration,bit_rate,sample_fmt,sample_rate,channels,channel_layout,bits_per_sample,initial_padding,disposition";
108
+ return [
109
+ "-v",
110
+ "error",
111
+ "-show_format",
112
+ "-show_streams",
113
+ "-of",
114
+ "json",
115
+ ...options.showPackets ? ["-show_entries", `${streamEntries}:packet=stream_index,pts,pts_time,dts,dts_time,duration,pos,flags`] : ["-show_entries", streamEntries]
116
+ ];
117
+ };
118
+ var FFProbeRunner = class {
119
+ static async probePath(absolutePath, includePackets) {
120
+ const probeCommand = `ffprobe ${buildProbeArgs({ showPackets: includePackets }).join(" ")} ${absolutePath}`;
121
+ log("Probing", probeCommand);
122
+ const probeResult = await execPromise(probeCommand);
123
+ log("Probe result", probeResult.stdout);
124
+ log("Probe stderr", probeResult.stderr);
125
+ return JSON.parse(probeResult.stdout);
126
+ }
127
+ static async probeStream(stream, includePackets) {
128
+ const probe = (0, node_child_process.spawn)("ffprobe", [
129
+ "-i",
130
+ "-",
131
+ ...buildProbeArgs({ showPackets: includePackets })
132
+ ], { stdio: [
133
+ "pipe",
134
+ "pipe",
135
+ "pipe"
136
+ ] });
137
+ const chunks = [];
138
+ const processExit = new Promise((_, reject) => {
139
+ probe.on("exit", (code) => {
140
+ if (code !== 0) reject(/* @__PURE__ */ new Error(`ffprobe exited with code ${code}`));
141
+ });
142
+ probe.on("error", (err) => reject(err));
143
+ });
144
+ probe.stderr.on("data", (data) => {
145
+ log(data.toString());
146
+ });
147
+ probe.stdout.on("data", (data) => {
148
+ chunks.push(data);
149
+ });
150
+ probe.stdin.on("error", (error) => {
151
+ if (error.code === "EPIPE") {
152
+ log("ffprobe closed input pipe");
153
+ return;
154
+ }
155
+ log("ffprobe stdin error", error);
156
+ });
157
+ stream.pipe(probe.stdin);
158
+ try {
159
+ return await Promise.race([new Promise((resolve, reject) => {
160
+ probe.stdout.on("end", () => {
161
+ try {
162
+ const buffer = Buffer.concat(chunks).toString("utf8");
163
+ resolve(JSON.parse(buffer));
164
+ } catch (error) {
165
+ reject(error);
166
+ }
167
+ });
168
+ }), processExit]);
169
+ } finally {
170
+ stream.unpipe(probe.stdin);
171
+ probe.stdin.end();
172
+ stream.destroy();
173
+ }
174
+ }
175
+ };
176
+ var ProbeBase = class {
177
+ get audioStreams() {
178
+ return this.data.streams.filter((stream) => stream.codec_type === "audio");
179
+ }
180
+ get videoStreams() {
181
+ return this.data.streams.filter((stream) => stream.codec_type === "video");
182
+ }
183
+ get streams() {
184
+ return this.data.streams;
185
+ }
186
+ get format() {
187
+ return this.data.format;
188
+ }
189
+ get mustReencodeAudio() {
190
+ return this.audioStreams.some((stream) => stream.codec_name !== "aac");
191
+ }
192
+ get mustReencodeVideo() {
193
+ return false;
194
+ }
195
+ get mustRemux() {
196
+ return this.format.format_name !== "mp4" || this.data.streams.some((stream) => stream.codec_type !== "audio" && stream.codec_type !== "video");
197
+ }
198
+ get hasNonAudioOrVideoStreams() {
199
+ return this.data.streams.some((stream) => stream.codec_type !== "audio" && stream.codec_type !== "video");
200
+ }
201
+ get hasAudio() {
202
+ return this.audioStreams.length > 0;
203
+ }
204
+ get hasVideo() {
205
+ return this.videoStreams.length > 0;
206
+ }
207
+ get isAudioOnly() {
208
+ return this.audioStreams.length > 0 && this.videoStreams.length === 0;
209
+ }
210
+ get isMp3() {
211
+ return this.audioStreams.some((stream) => stream.codec_name === "mp3");
212
+ }
213
+ get isVideoOnly() {
214
+ return this.audioStreams.length === 0 && this.videoStreams.length > 0;
215
+ }
216
+ get mustProcess() {
217
+ return this.mustReencodeAudio || this.mustReencodeVideo || this.mustRemux;
218
+ }
219
+ get audioTimebase() {
220
+ const audioStream = this.audioStreams[0];
221
+ if (!audioStream) return null;
222
+ const [num, den] = audioStream.time_base.split("/").map(Number);
223
+ if (num === void 0 || den === void 0) return null;
224
+ return {
225
+ num,
226
+ den
227
+ };
228
+ }
229
+ get videoTimebase() {
230
+ const videoStream = this.videoStreams[0];
231
+ if (!videoStream) return null;
232
+ const [num, den] = videoStream.time_base.split("/").map(Number);
233
+ if (num === void 0 || den === void 0) return null;
234
+ return {
235
+ num,
236
+ den
237
+ };
238
+ }
239
+ get ffmpegAudioInputOptions() {
240
+ if (!this.hasAudio) return [];
241
+ if (this.isMp3) return ["-c:a", "mp3"];
242
+ return [];
243
+ }
244
+ get ffmpegVideoInputOptions() {
245
+ return [];
246
+ }
247
+ get ffmpegAudioOutputOptions() {
248
+ if (!this.hasAudio) return [];
249
+ if (this.mustReencodeAudio) return [
250
+ "-c:a",
251
+ "aac",
252
+ "-b:a",
253
+ "192k",
254
+ "-ar",
255
+ "48000"
256
+ ];
257
+ return ["-c:a", "copy"];
258
+ }
259
+ get ffmpegVideoOutputOptions() {
260
+ if (!this.hasVideo) return [];
261
+ if (this.mustReencodeVideo) return [
262
+ "-c:v",
263
+ "h264",
264
+ "-bsf:v",
265
+ "filter_units=remove_types=6",
266
+ "-pix_fmt",
267
+ "yuv420p"
268
+ ];
269
+ return [
270
+ "-c:v",
271
+ "copy",
272
+ "-bsf:v",
273
+ "filter_units=remove_types=6"
274
+ ];
275
+ }
276
+ constructor(absolutePath) {
277
+ this.absolutePath = absolutePath;
278
+ }
279
+ createConformingReadstream() {
280
+ if (this.absolutePath === "pipe:0") throw new Error("Cannot create conforming readstream from pipe");
281
+ if (!this.mustProcess) return (0, node_fs.createReadStream)(this.absolutePath);
282
+ const fragmenterArgs = this.isAudioOnly ? [
283
+ "-movflags",
284
+ "frag_keyframe",
285
+ "-frag_duration",
286
+ "4000000"
287
+ ] : ["-movflags", "frag_keyframe"];
288
+ const ffmpegConformanceArgs = [
289
+ ...this.ffmpegAudioInputOptions,
290
+ ...this.ffmpegVideoInputOptions,
291
+ "-i",
292
+ this.absolutePath,
293
+ ...this.ffmpegAudioOutputOptions,
294
+ ...this.ffmpegVideoOutputOptions,
295
+ "-f",
296
+ "mp4",
297
+ "-bitexact",
298
+ ...fragmenterArgs,
299
+ "pipe:1"
300
+ ];
301
+ log("Running ffmpeg", ffmpegConformanceArgs);
302
+ const ffmpegConformer = (0, node_child_process.spawn)("ffmpeg", ffmpegConformanceArgs, { stdio: [
303
+ "ignore",
304
+ "pipe",
305
+ "pipe"
306
+ ] });
307
+ ffmpegConformer.stderr.on("data", (data) => {
308
+ log("CONFORMER: ", data.toString());
309
+ });
310
+ const ffmpegFragmentArgs = [
311
+ "-i",
312
+ "-",
313
+ "-c",
314
+ "copy",
315
+ "-f",
316
+ "mp4",
317
+ "-bitexact",
318
+ ...fragmenterArgs,
319
+ "pipe:1"
320
+ ];
321
+ log("Running ffmpeg", ffmpegFragmentArgs);
322
+ const ffmpegFragmenter = (0, node_child_process.spawn)("ffmpeg", ffmpegFragmentArgs, { stdio: [
323
+ "pipe",
324
+ "pipe",
325
+ "pipe"
326
+ ] });
327
+ ffmpegConformer.stdout.pipe(ffmpegFragmenter.stdin);
328
+ ffmpegFragmenter.stderr.on("data", (data) => {
329
+ log("FRAGMENTER: ", data.toString());
330
+ });
331
+ ffmpegConformer.on("error", (error) => {
332
+ ffmpegFragmenter.stdout.emit("error", error);
333
+ });
334
+ ffmpegFragmenter.on("error", (error) => {
335
+ ffmpegFragmenter.stdout.emit("error", error);
336
+ });
337
+ return ffmpegFragmenter.stdout;
338
+ }
339
+ createTrackReadstream(trackIndex) {
340
+ if (this.absolutePath === "pipe:0") throw new Error("Cannot create track readstream from pipe");
341
+ const track = this.data.streams[trackIndex];
342
+ if (!track) throw new Error(`Track ${trackIndex} not found`);
343
+ const isAudioTrack = track.codec_type === "audio";
344
+ const isVideoTrack = track.codec_type === "video";
345
+ if (!isAudioTrack && !isVideoTrack) throw new Error(`Track ${trackIndex} is not audio or video`);
346
+ const fragmenterArgs = isAudioTrack ? [
347
+ "-movflags",
348
+ "empty_moov+default_base_moof",
349
+ "-frag_duration",
350
+ "4000000"
351
+ ] : ["-movflags", "frag_keyframe+empty_moov+default_base_moof"];
352
+ const codecOptions = isAudioTrack && this.mustReencodeAudio ? this.ffmpegAudioOutputOptions : ["-c", "copy"];
353
+ const bitstreamFilter = isVideoTrack ? ["-bsf:v", "filter_units=remove_types=6"] : [];
354
+ const ffmpegArgs = [
355
+ ...this.ffmpegAudioInputOptions,
356
+ ...this.ffmpegVideoInputOptions,
357
+ "-i",
358
+ this.absolutePath,
359
+ "-map",
360
+ `0:${trackIndex}`,
361
+ ...codecOptions,
362
+ ...bitstreamFilter,
363
+ "-f",
364
+ "mp4",
365
+ "-bitexact",
366
+ ...fragmenterArgs,
367
+ "pipe:1"
368
+ ];
369
+ log("Creating track stream", ffmpegArgs);
370
+ const ffmpegProcess = (0, node_child_process.spawn)("ffmpeg", ffmpegArgs, { stdio: [
371
+ "ignore",
372
+ "pipe",
373
+ "pipe"
374
+ ] });
375
+ ffmpegProcess.stderr.on("data", (data) => {
376
+ log(`TRACK ${trackIndex}: `, data.toString());
377
+ });
378
+ ffmpegProcess.on("error", (error) => {
379
+ ffmpegProcess.stdout.emit("error", error);
380
+ });
381
+ return ffmpegProcess.stdout;
382
+ }
383
+ createScrubTrackReadstream() {
384
+ if (this.absolutePath === "pipe:0") throw new Error("Cannot create scrub track readstream from pipe");
385
+ const videoStream = this.videoStreams[0];
386
+ if (!videoStream) throw new Error("No video stream found for scrub track generation");
387
+ const targetWidth = 320;
388
+ const aspectRatio = videoStream.height / videoStream.width;
389
+ const targetHeight = Math.round(targetWidth * aspectRatio);
390
+ const scrubHeight = targetHeight % 2 === 0 ? targetHeight : targetHeight + 1;
391
+ const [fpsNum, fpsDen] = videoStream.r_frame_rate.split("/").map(Number);
392
+ const frameRate = fpsNum && fpsDen ? `${fpsNum}/${fpsDen}` : "30/1";
393
+ const fragmenterArgs = [
394
+ "-movflags",
395
+ "empty_moov+default_base_moof",
396
+ "-frag_duration",
397
+ "30000000"
398
+ ];
399
+ const ffmpegArgs = [
400
+ ...this.ffmpegAudioInputOptions,
401
+ ...this.ffmpegVideoInputOptions,
402
+ "-i",
403
+ this.absolutePath,
404
+ "-map",
405
+ "0:v:0",
406
+ "-c:v",
407
+ "libx264",
408
+ "-preset",
409
+ "ultrafast",
410
+ "-crf",
411
+ "28",
412
+ "-vf",
413
+ `scale=${targetWidth}:${scrubHeight}`,
414
+ "-r",
415
+ frameRate,
416
+ "-g",
417
+ "10",
418
+ "-f",
419
+ "mp4",
420
+ "-bitexact",
421
+ ...fragmenterArgs,
422
+ "pipe:1"
423
+ ];
424
+ log("Creating scrub track stream", ffmpegArgs);
425
+ const ffmpegProcess = (0, node_child_process.spawn)("ffmpeg", ffmpegArgs, { stdio: [
426
+ "ignore",
427
+ "pipe",
428
+ "pipe"
429
+ ] });
430
+ ffmpegProcess.stderr.on("data", (data) => {
431
+ log("SCRUB TRACK: ", data.toString());
432
+ });
433
+ ffmpegProcess.on("error", (error) => {
434
+ ffmpegProcess.stdout.emit("error", error);
435
+ });
436
+ ffmpegProcess.on("exit", (code, signal) => {
437
+ if (code !== 0 && code !== null) {
438
+ const error = /* @__PURE__ */ new Error(`FFmpeg scrub track process exited with code ${code}${signal ? ` and signal ${signal}` : ""}`);
439
+ ffmpegProcess.stdout.emit("error", error);
440
+ }
441
+ });
442
+ return ffmpegProcess.stdout;
443
+ }
444
+ };
445
+ var Probe = class Probe extends ProbeBase {
446
+ static async probePath(absolutePath) {
447
+ return new Probe(absolutePath, await FFProbeRunner.probePath(absolutePath, false));
448
+ }
449
+ static async probeStream(stream) {
450
+ return new Probe("pipe:0", await FFProbeRunner.probeStream(stream, false));
451
+ }
452
+ constructor(absolutePath, rawData) {
453
+ super(absolutePath);
454
+ this.data = ProbeSchema.parse(rawData);
455
+ }
456
+ };
457
+ var PacketProbe = class PacketProbe extends ProbeBase {
458
+ static async probePath(absolutePath) {
459
+ return new PacketProbe(absolutePath, await FFProbeRunner.probePath(absolutePath, true));
460
+ }
461
+ static async probeStream(stream) {
462
+ return new PacketProbe("pipe:0", await FFProbeRunner.probeStream(stream, true));
463
+ }
464
+ constructor(absolutePath, rawData) {
465
+ super(absolutePath);
466
+ this.data = PacketProbeSchema.parse(rawData);
467
+ }
468
+ get packets() {
469
+ return this.data.packets;
470
+ }
471
+ get bestEffortAudioDuration() {
472
+ const stream = this.audioStreams[0];
473
+ if (!stream) throw new Error("No audio stream found");
474
+ return require_truncateDecimal.truncateDecimal(((stream.duration_ts ?? 0) - (stream.start_pts ?? 0)) / (this.audioTimebase?.den ?? 0), 5);
475
+ }
476
+ get videoPacketDuration() {
477
+ const videoStream = this.videoStreams[0];
478
+ if (!videoStream) return [];
479
+ const videoPackets = this.packets.filter((packet) => packet.stream_index === videoStream.index);
480
+ const [num, den] = videoStream.r_frame_rate.split("/").map(Number);
481
+ if (!num || !den) return [];
482
+ const packetDuration = den / num;
483
+ if (videoPackets.length === 0) return [];
484
+ const ptsTimes = videoPackets.map((p) => p.pts_time);
485
+ const minPts = Math.min(...ptsTimes);
486
+ const totalDuration = Math.max(...ptsTimes) - minPts + packetDuration;
487
+ return require_truncateDecimal.truncateDecimal(Math.round(totalDuration * 1e4) / 1e4, 5);
488
+ }
489
+ };
490
+
491
+ //#endregion
492
+ exports.PacketProbe = PacketProbe;
493
+ exports.Probe = Probe;
494
+ //# sourceMappingURL=Probe.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Probe.cjs","names":["exec","z","chunks: Uint8Array[]","absolutePath: string","truncateDecimal"],"sources":["../src/Probe.ts"],"sourcesContent":["import { exec, spawn } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { createReadStream } from \"node:fs\";\n\nimport * as z from \"zod\";\nimport debug from \"debug\";\nimport type { Readable } from \"node:stream\";\nimport { truncateDecimal } from \"./truncateDecimal\";\n\nconst execPromise = promisify(exec);\n\nconst log = debug(\"ef:assets:probe\");\n\nexport const AudioStreamSchema = z.object({\n index: z.number(),\n codec_name: z.string(),\n codec_long_name: z.string(),\n codec_type: z.literal(\"audio\"),\n codec_tag_string: z.string(),\n codec_tag: z.string(),\n sample_fmt: z.string(),\n sample_rate: z.string(),\n channels: z.number(),\n channel_layout: z.string().optional(),\n bits_per_sample: z.number(),\n initial_padding: z.number().optional(),\n r_frame_rate: z.string(),\n avg_frame_rate: z.string(),\n time_base: z.string(),\n start_pts: z.number().optional(),\n start_time: z.coerce.number().optional(),\n duration_ts: z.number(),\n duration: z.coerce.number(),\n bit_rate: z.string(),\n disposition: z.record(z.unknown()),\n});\n\nexport type AudioStreamSchema = z.infer<typeof AudioStreamSchema>;\n\nexport const VideoStreamSchema = z.object({\n index: z.number(),\n codec_name: z.string(),\n codec_long_name: z.string(),\n codec_type: z.literal(\"video\"),\n codec_tag_string: z.string(),\n codec_tag: z.string(),\n profile: z.string().optional(),\n level: z.number().optional(),\n width: z.number(),\n height: z.number(),\n coded_width: z.number(),\n coded_height: z.number(),\n r_frame_rate: z.string(),\n avg_frame_rate: z.string(),\n time_base: z.string(),\n start_pts: z.number().optional(),\n start_time: z.coerce.number().optional(),\n duration_ts: z.number().optional(),\n duration: z.coerce.number().optional(),\n bit_rate: z.string().optional(),\n disposition: z.record(z.unknown()),\n});\n\nexport type VideoStreamSchema = z.infer<typeof VideoStreamSchema>;\n\nconst ProbeFormatSchema = z.object({\n filename: z.string(),\n nb_streams: z.number(),\n nb_programs: z.number(),\n format_name: z.string(),\n format_long_name: z.string(),\n start_time: z.string().optional(),\n duration: z.string().optional(),\n size: z.string().optional(),\n bit_rate: z.string().optional(),\n probe_score: z.number(),\n});\n\nexport const DataStreamSchema = z.object({\n index: z.number(),\n codec_type: z.literal(\"data\"),\n duration: z.string().optional(),\n duration_ts: z.number().optional(),\n start_pts: z.number().optional(),\n});\n\nexport type DataStreamSchema = z.infer<typeof DataStreamSchema>;\n\nconst StreamSchema = z.discriminatedUnion(\"codec_type\", [\n AudioStreamSchema,\n VideoStreamSchema,\n DataStreamSchema,\n]);\n\nexport type StreamSchema = z.infer<typeof StreamSchema>;\n\nconst PacketSchema = z.object({\n stream_index: z.number(),\n pts: z.number(),\n pts_time: z.coerce.number(),\n dts: z.number(),\n dts_time: z.coerce.number(),\n duration: z.coerce.number().optional(),\n pos: z.coerce.number().optional(),\n flags: z.string().optional(),\n});\n\nexport type PacketSchema = z.infer<typeof PacketSchema>;\n\nconst ProbeSchema = z.object({\n streams: z.array(StreamSchema),\n format: ProbeFormatSchema,\n});\n\nconst PacketProbeSchema = z.object({\n packets: z.array(PacketSchema),\n format: ProbeFormatSchema,\n streams: z.array(StreamSchema),\n});\n\nexport type ProbeSchema = z.infer<typeof ProbeSchema>;\nexport type PacketProbeSchema = z.infer<typeof PacketProbeSchema>;\n\nexport interface TrackSegment {\n cts: number;\n dts: number;\n duration: number;\n offset: number;\n size: number;\n}\n\nexport interface AudioTrackFragmentIndex {\n track: number;\n type: \"audio\";\n timescale: number;\n duration: number;\n channel_count: number;\n sample_rate: number;\n sample_size: number;\n sample_count: number;\n codec: string;\n startTimeOffsetMs?: number;\n initSegment: {\n offset: 0;\n size: number;\n };\n segments: Array<TrackSegment>;\n}\n\nexport interface VideoTrackFragmentIndex {\n track: number;\n type: \"video\";\n timescale: number;\n duration: number;\n width: number;\n height: number;\n sample_count: number;\n codec: string;\n startTimeOffsetMs?: number;\n initSegment: {\n offset: 0;\n size: number;\n };\n segments: Array<TrackSegment>;\n}\n\nexport type TrackFragmentIndex =\n | AudioTrackFragmentIndex\n | VideoTrackFragmentIndex;\n\nconst buildProbeArgs = (options: { showPackets?: boolean }) => {\n const streamEntries =\n \"stream=index,codec_name,codec_long_name,codec_type,codec_tag_string,codec_tag,profile,level,width,height,coded_width,coded_height,r_frame_rate,avg_frame_rate,time_base,start_pts,start_time,duration_ts,duration,bit_rate,sample_fmt,sample_rate,channels,channel_layout,bits_per_sample,initial_padding,disposition\";\n const packetEntries =\n \"packet=stream_index,pts,pts_time,dts,dts_time,duration,pos,flags\";\n\n return [\n \"-v\",\n \"error\",\n \"-show_format\",\n \"-show_streams\",\n \"-of\",\n \"json\",\n ...(options.showPackets\n ? [\"-show_entries\", `${streamEntries}:${packetEntries}`]\n : [\"-show_entries\", streamEntries]),\n ];\n};\n\nclass FFProbeRunner {\n static async probePath(\n absolutePath: string,\n includePackets: boolean,\n ): Promise<any> {\n const probeCommand = `ffprobe ${buildProbeArgs({ showPackets: includePackets }).join(\" \")} ${absolutePath}`;\n log(\"Probing\", probeCommand);\n const probeResult = await execPromise(probeCommand);\n log(\"Probe result\", probeResult.stdout);\n log(\"Probe stderr\", probeResult.stderr);\n return JSON.parse(probeResult.stdout);\n }\n\n static async probeStream(\n stream: Readable,\n includePackets: boolean,\n ): Promise<any> {\n const probe = spawn(\n \"ffprobe\",\n [\"-i\", \"-\", ...buildProbeArgs({ showPackets: includePackets })],\n { stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n );\n\n const chunks: Uint8Array[] = [];\n\n // Handle process exit/error before data processing\n const processExit = new Promise<never>((_, reject) => {\n probe.on(\"exit\", (code) => {\n if (code !== 0) {\n reject(new Error(`ffprobe exited with code ${code}`));\n }\n });\n probe.on(\"error\", (err) => reject(err));\n });\n\n probe.stderr.on(\"data\", (data) => {\n log(data.toString());\n });\n\n probe.stdout.on(\"data\", (data) => {\n chunks.push(data);\n });\n\n // Handle pipe errors\n probe.stdin.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"EPIPE\") {\n log(\"ffprobe closed input pipe\");\n return;\n }\n log(\"ffprobe stdin error\", error);\n });\n\n stream.pipe(probe.stdin);\n\n try {\n const json = await Promise.race([\n new Promise<any>((resolve, reject) => {\n probe.stdout.on(\"end\", () => {\n try {\n const buffer = Buffer.concat(chunks).toString(\"utf8\");\n resolve(JSON.parse(buffer));\n } catch (error) {\n reject(error);\n }\n });\n }),\n processExit,\n ]);\n\n return json;\n } finally {\n // Clean up regardless of success or failure\n stream.unpipe(probe.stdin);\n probe.stdin.end();\n stream.destroy();\n }\n }\n}\n\nabstract class ProbeBase {\n abstract data: ProbeSchema | PacketProbeSchema;\n\n get audioStreams() {\n return this.data.streams.filter(\n (stream) => stream.codec_type === \"audio\",\n ) as AudioStreamSchema[];\n }\n\n get videoStreams() {\n return this.data.streams.filter(\n (stream) => stream.codec_type === \"video\",\n ) as VideoStreamSchema[];\n }\n\n get streams() {\n return this.data.streams;\n }\n\n get format() {\n return this.data.format;\n }\n\n get mustReencodeAudio() {\n return this.audioStreams.some((stream) => stream.codec_name !== \"aac\");\n }\n\n get mustReencodeVideo() {\n return false;\n }\n\n get mustRemux() {\n return (\n this.format.format_name !== \"mp4\" ||\n this.data.streams.some(\n (stream) =>\n stream.codec_type !== \"audio\" && stream.codec_type !== \"video\",\n )\n );\n }\n\n get hasNonAudioOrVideoStreams() {\n return this.data.streams.some(\n (stream) =>\n stream.codec_type !== \"audio\" && stream.codec_type !== \"video\",\n );\n }\n\n get hasAudio() {\n return this.audioStreams.length > 0;\n }\n\n get hasVideo() {\n return this.videoStreams.length > 0;\n }\n\n get isAudioOnly() {\n return this.audioStreams.length > 0 && this.videoStreams.length === 0;\n }\n\n get isMp3() {\n return this.audioStreams.some((stream) => stream.codec_name === \"mp3\");\n }\n\n get isVideoOnly() {\n return this.audioStreams.length === 0 && this.videoStreams.length > 0;\n }\n\n get mustProcess() {\n return this.mustReencodeAudio || this.mustReencodeVideo || this.mustRemux;\n }\n\n get audioTimebase() {\n const audioStream = this.audioStreams[0];\n if (!audioStream) {\n return null;\n }\n const [num, den] = audioStream.time_base.split(\"/\").map(Number);\n if (num === undefined || den === undefined) {\n return null;\n }\n return { num, den };\n }\n\n get videoTimebase() {\n const videoStream = this.videoStreams[0];\n if (!videoStream) {\n return null;\n }\n const [num, den] = videoStream.time_base.split(\"/\").map(Number);\n if (num === undefined || den === undefined) {\n return null;\n }\n return { num, den };\n }\n\n get ffmpegAudioInputOptions() {\n if (!this.hasAudio) {\n return [];\n }\n if (this.isMp3) {\n return [\"-c:a\", \"mp3\"];\n }\n return [];\n }\n\n get ffmpegVideoInputOptions() {\n return [];\n }\n\n get ffmpegAudioOutputOptions() {\n if (!this.hasAudio) {\n return [];\n }\n if (this.mustReencodeAudio) {\n // biome-ignore format: keep cli argument paired together\n return [\"-c:a\", \"aac\", \"-b:a\", \"192k\", \"-ar\", \"48000\"];\n }\n return [\"-c:a\", \"copy\"];\n }\n\n get ffmpegVideoOutputOptions() {\n if (!this.hasVideo) {\n return [];\n }\n if (this.mustReencodeVideo) {\n // biome-ignore format: keep cli argument paired together\n return [\n \"-c:v\",\n \"h264\",\n // Filter out SEI NAL units that aren't supported by the webcodecs decoder\n \"-bsf:v\",\n \"filter_units=remove_types=6\",\n \"-pix_fmt\",\n \"yuv420p\",\n ];\n }\n // biome-ignore format: keep cli argument paired together\n return [\n \"-c:v\",\n \"copy\",\n // Filter out SEI NAL units that aren't supported by the webcodecs decoder\n \"-bsf:v\",\n \"filter_units=remove_types=6\",\n ];\n }\n\n protected constructor(protected absolutePath: string) {}\n\n createConformingReadstream() {\n if (this.absolutePath === \"pipe:0\") {\n throw new Error(\"Cannot create conforming readstream from pipe\");\n }\n if (!this.mustProcess) {\n return createReadStream(this.absolutePath);\n }\n\n const fragmenterArgs = this.isAudioOnly\n ? [\n \"-movflags\",\n \"frag_keyframe\",\n \"-frag_duration\",\n \"4000000\", // Fragment every 4 seconds (in microseconds)\n ]\n : [\"-movflags\", \"frag_keyframe\"];\n\n // biome-ignore format: keep cli argument paired together\n const ffmpegConformanceArgs = [\n ...this.ffmpegAudioInputOptions,\n ...this.ffmpegVideoInputOptions,\n \"-i\",\n this.absolutePath,\n ...this.ffmpegAudioOutputOptions,\n ...this.ffmpegVideoOutputOptions,\n \"-f\",\n \"mp4\",\n \"-bitexact\", // Ensure deterministic output\n ...fragmenterArgs,\n \"pipe:1\",\n ];\n\n log(\"Running ffmpeg\", ffmpegConformanceArgs);\n\n const ffmpegConformer = spawn(\"ffmpeg\", ffmpegConformanceArgs, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n ffmpegConformer.stderr.on(\"data\", (data) => {\n log(\"CONFORMER: \", data.toString());\n });\n\n // biome-ignore format: keep cli argument paired together\n const ffmpegFragmentArgs = [\n \"-i\",\n \"-\",\n \"-c\",\n \"copy\",\n \"-f\",\n \"mp4\",\n \"-bitexact\", // Ensure deterministic output\n ...fragmenterArgs,\n \"pipe:1\",\n ];\n\n log(\"Running ffmpeg\", ffmpegFragmentArgs);\n\n const ffmpegFragmenter = spawn(\"ffmpeg\", ffmpegFragmentArgs, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n ffmpegConformer.stdout.pipe(ffmpegFragmenter.stdin);\n ffmpegFragmenter.stderr.on(\"data\", (data) => {\n log(\"FRAGMENTER: \", data.toString());\n });\n\n ffmpegConformer.on(\"error\", (error) => {\n ffmpegFragmenter.stdout.emit(\"error\", error);\n });\n\n ffmpegFragmenter.on(\"error\", (error) => {\n ffmpegFragmenter.stdout.emit(\"error\", error);\n });\n\n return ffmpegFragmenter.stdout;\n }\n\n createTrackReadstream(trackIndex: number) {\n if (this.absolutePath === \"pipe:0\") {\n throw new Error(\"Cannot create track readstream from pipe\");\n }\n\n const track = this.data.streams[trackIndex];\n if (!track) {\n throw new Error(`Track ${trackIndex} not found`);\n }\n\n const isAudioTrack = track.codec_type === \"audio\";\n const isVideoTrack = track.codec_type === \"video\";\n\n if (!isAudioTrack && !isVideoTrack) {\n throw new Error(`Track ${trackIndex} is not audio or video`);\n }\n\n const fragmenterArgs = isAudioTrack\n ? [\n \"-movflags\",\n \"empty_moov+default_base_moof\",\n \"-frag_duration\",\n \"4000000\", // Fragment every 4 seconds (in microseconds)\n ]\n : [\"-movflags\", \"frag_keyframe+empty_moov+default_base_moof\"];\n\n // Create single-track MP4 with proper fragmentation\n // Use conforming stream system to handle codec compatibility\n const codecOptions =\n isAudioTrack && this.mustReencodeAudio\n ? this.ffmpegAudioOutputOptions\n : [\"-c\", \"copy\"];\n\n // Filter out SEI NAL units (type 6) for video tracks\n // These can cause WebCodecs VideoDecoder to hang or crash in some browsers/Electron\n const bitstreamFilter = isVideoTrack\n ? [\"-bsf:v\", \"filter_units=remove_types=6\"]\n : [];\n\n const ffmpegArgs = [\n ...this.ffmpegAudioInputOptions,\n ...this.ffmpegVideoInputOptions,\n \"-i\",\n this.absolutePath,\n \"-map\",\n `0:${trackIndex}`, // Select only this track\n ...codecOptions, // Use conforming stream codec options\n ...bitstreamFilter, // Remove SEI NAL units that cause WebCodecs issues\n \"-f\",\n \"mp4\",\n \"-bitexact\", // Ensure deterministic output\n ...fragmenterArgs,\n \"pipe:1\",\n ];\n\n log(\"Creating track stream\", ffmpegArgs);\n\n const ffmpegProcess = spawn(\"ffmpeg\", ffmpegArgs, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n ffmpegProcess.stderr.on(\"data\", (data) => {\n log(`TRACK ${trackIndex}: `, data.toString());\n });\n\n ffmpegProcess.on(\"error\", (error) => {\n ffmpegProcess.stdout.emit(\"error\", error);\n });\n\n return ffmpegProcess.stdout;\n }\n\n createScrubTrackReadstream() {\n if (this.absolutePath === \"pipe:0\") {\n throw new Error(\"Cannot create scrub track readstream from pipe\");\n }\n\n const videoStream = this.videoStreams[0];\n if (!videoStream) {\n throw new Error(\"No video stream found for scrub track generation\");\n }\n\n // Calculate proportional height for 320px width\n const targetWidth = 320;\n const aspectRatio = videoStream.height / videoStream.width;\n const targetHeight = Math.round(targetWidth * aspectRatio);\n // Ensure height is even (required for H.264)\n const scrubHeight =\n targetHeight % 2 === 0 ? targetHeight : targetHeight + 1;\n\n // Parse frame rate from r_frame_rate (e.g., \"30/1\" or \"30000/1001\")\n const [fpsNum, fpsDen] = videoStream.r_frame_rate.split(\"/\").map(Number);\n const frameRate = fpsNum && fpsDen ? `${fpsNum}/${fpsDen}` : \"30/1\";\n\n // Scrub track uses 30-second fragments with keyframes every 10 frames for fast seeking.\n // NOTE: Do NOT use frag_keyframe - it would create a fragment at every keyframe.\n // We want multiple keyframes within a single 30-second fragment (single trun with many samples).\n const fragmenterArgs = [\n \"-movflags\",\n \"empty_moov+default_base_moof\",\n \"-frag_duration\",\n \"30000000\", // 30 seconds in microseconds\n ];\n\n // Transcode to low-res H.264 with keyframes every 10 frames for fast seeking\n const ffmpegArgs = [\n ...this.ffmpegAudioInputOptions,\n ...this.ffmpegVideoInputOptions,\n \"-i\",\n this.absolutePath,\n \"-map\",\n \"0:v:0\", // Select first video stream only (no audio for scrub)\n \"-c:v\",\n \"libx264\", // Encode to H.264\n \"-preset\",\n \"ultrafast\", // Fast encoding for scrub track\n \"-crf\",\n \"28\", // Lower quality for smaller file size\n \"-vf\",\n `scale=${targetWidth}:${scrubHeight}`, // Scale to scrub resolution\n \"-r\",\n frameRate, // Maintain native FPS\n \"-g\",\n \"10\", // Keyframe every 10 frames for fast seeking within fragments\n \"-f\",\n \"mp4\",\n \"-bitexact\", // Ensure deterministic output\n ...fragmenterArgs,\n \"pipe:1\",\n ];\n\n log(\"Creating scrub track stream\", ffmpegArgs);\n\n const ffmpegProcess = spawn(\"ffmpeg\", ffmpegArgs, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n ffmpegProcess.stderr.on(\"data\", (data) => {\n log(\"SCRUB TRACK: \", data.toString());\n });\n\n ffmpegProcess.on(\"error\", (error) => {\n ffmpegProcess.stdout.emit(\"error\", error);\n });\n\n // Handle FFmpeg process exit\n ffmpegProcess.on(\"exit\", (code, signal) => {\n if (code !== 0 && code !== null) {\n const error = new Error(\n `FFmpeg scrub track process exited with code ${code}${signal ? ` and signal ${signal}` : \"\"}`,\n );\n ffmpegProcess.stdout.emit(\"error\", error);\n }\n });\n\n return ffmpegProcess.stdout;\n }\n}\n\nexport class Probe extends ProbeBase {\n data: ProbeSchema;\n\n static async probePath(absolutePath: string): Promise<Probe> {\n const json = await FFProbeRunner.probePath(absolutePath, false);\n return new Probe(absolutePath, json);\n }\n\n static async probeStream(stream: Readable): Promise<Probe> {\n const json = await FFProbeRunner.probeStream(stream, false);\n return new Probe(\"pipe:0\", json);\n }\n\n constructor(absolutePath: string, rawData: any) {\n super(absolutePath);\n this.data = ProbeSchema.parse(rawData);\n }\n}\n\nexport class PacketProbe extends ProbeBase {\n data: PacketProbeSchema;\n\n static async probePath(absolutePath: string): Promise<PacketProbe> {\n const json = await FFProbeRunner.probePath(absolutePath, true);\n return new PacketProbe(absolutePath, json);\n }\n\n static async probeStream(stream: Readable): Promise<PacketProbe> {\n const json = await FFProbeRunner.probeStream(stream, true);\n return new PacketProbe(\"pipe:0\", json);\n }\n\n constructor(absolutePath: string, rawData: any) {\n super(absolutePath);\n this.data = PacketProbeSchema.parse(rawData);\n }\n\n get packets() {\n return this.data.packets;\n }\n\n get bestEffortAudioDuration() {\n const stream = this.audioStreams[0];\n if (!stream) {\n throw new Error(\"No audio stream found\");\n }\n return truncateDecimal(\n ((stream.duration_ts ?? 0) - (stream.start_pts ?? 0)) /\n (this.audioTimebase?.den ?? 0),\n 5,\n );\n }\n\n get videoPacketDuration() {\n const videoStream = this.videoStreams[0];\n if (!videoStream) {\n return [];\n }\n const videoPackets = this.packets.filter(\n (packet) => packet.stream_index === videoStream.index,\n );\n\n const frameRate = videoStream.r_frame_rate;\n const [num, den] = frameRate.split(\"/\").map(Number);\n if (!num || !den) {\n return [];\n }\n const packetDuration = den / num;\n\n // Calculate duration using actual packet PTS timing data\n if (videoPackets.length === 0) {\n return [];\n }\n\n const ptsTimes = videoPackets.map((p) => p.pts_time);\n const minPts = Math.min(...ptsTimes);\n const maxPts = Math.max(...ptsTimes);\n const totalDuration = maxPts - minPts + packetDuration;\n\n return truncateDecimal(Math.round(totalDuration * 10000) / 10000, 5);\n }\n}\n"],"mappings":"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"}