@editframe/assets 0.7.0-beta.1 → 0.7.0-beta.4

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.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const MP4Box = require("mp4box");
4
+ const debug = require("debug");
4
5
  function _interopNamespaceDefault(e) {
5
6
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
6
7
  if (e) {
@@ -18,6 +19,7 @@ function _interopNamespaceDefault(e) {
18
19
  return Object.freeze(n);
19
20
  }
20
21
  const MP4Box__namespace = /* @__PURE__ */ _interopNamespaceDefault(MP4Box);
22
+ const log = debug("ef:av:mp4file");
21
23
  class MP4File extends MP4Box__namespace.ISOFile {
22
24
  constructor() {
23
25
  super(...arguments);
@@ -114,7 +116,7 @@ class MP4File extends MP4Box__namespace.ISOFile {
114
116
  fragTrak.segmentStream
115
117
  );
116
118
  } catch (error) {
117
- console.log("Failed to createFragment", error);
119
+ console.error("Failed to createFragment", error);
118
120
  }
119
121
  if (result) {
120
122
  fragTrak.segmentStream = result;
@@ -140,6 +142,9 @@ class MP4File extends MP4Box__namespace.ISOFile {
140
142
  if (trak.nextSample >= trak.samples.length) {
141
143
  trackInfoForFrag.complete = true;
142
144
  }
145
+ log(
146
+ `Yielding fragment #${trackInfoForFrag.index} for track=${fragTrak.id}`
147
+ );
143
148
  const startSample = fragmentStartSamples[fragTrak.id];
144
149
  const endSample = trak.samples[trak.nextSample - 1];
145
150
  if (!startSample || !endSample) {
@@ -1,4 +1,6 @@
1
1
  import * as MP4Box from "mp4box";
2
+ import debug from "debug";
3
+ const log = debug("ef:av:mp4file");
2
4
  class MP4File extends MP4Box.ISOFile {
3
5
  constructor() {
4
6
  super(...arguments);
@@ -95,7 +97,7 @@ class MP4File extends MP4Box.ISOFile {
95
97
  fragTrak.segmentStream
96
98
  );
97
99
  } catch (error) {
98
- console.log("Failed to createFragment", error);
100
+ console.error("Failed to createFragment", error);
99
101
  }
100
102
  if (result) {
101
103
  fragTrak.segmentStream = result;
@@ -121,6 +123,9 @@ class MP4File extends MP4Box.ISOFile {
121
123
  if (trak.nextSample >= trak.samples.length) {
122
124
  trackInfoForFrag.complete = true;
123
125
  }
126
+ log(
127
+ `Yielding fragment #${trackInfoForFrag.index} for track=${fragTrak.id}`
128
+ );
124
129
  const startSample = fragmentStartSamples[fragTrak.id];
125
130
  const endSample = trak.samples[trak.nextSample - 1];
126
131
  if (!startSample || !endSample) {
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const node_child_process = require("node:child_process");
4
- const execPromise = require("../../../lib/util/execPromise.cjs");
5
- const z = require("zod");
6
4
  const node_fs = require("node:fs");
5
+ const z = require("zod");
6
+ const debug = require("debug");
7
+ const execPromise = require("../../../lib/util/execPromise.cjs");
7
8
  function _interopNamespaceDefault(e) {
8
9
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
9
10
  if (e) {
@@ -21,6 +22,7 @@ function _interopNamespaceDefault(e) {
21
22
  return Object.freeze(n);
22
23
  }
23
24
  const z__namespace = /* @__PURE__ */ _interopNamespaceDefault(z);
25
+ const log = debug("ef:assets:probe");
24
26
  const AudioStreamSchema = z__namespace.object({
25
27
  index: z__namespace.number(),
26
28
  codec_name: z__namespace.string(),
@@ -91,9 +93,11 @@ class Probe {
91
93
  this.data = ProbeSchema.parse(rawData);
92
94
  }
93
95
  static async probePath(absolutePath) {
94
- const probeResult = await execPromise.execPromise(
95
- `ffprobe -v error -show_format -show_streams -of json ${absolutePath}`
96
- );
96
+ const probeCommand = `ffprobe -v error -show_format -show_streams -of json ${absolutePath}`;
97
+ log("Probing", probeCommand);
98
+ const probeResult = await execPromise.execPromise(probeCommand);
99
+ log("Probe result", probeResult.stdout);
100
+ log("Probe stderr", probeResult.stderr);
97
101
  const json = JSON.parse(probeResult.stdout);
98
102
  return new Probe(absolutePath, json);
99
103
  }
@@ -114,9 +118,7 @@ class Probe {
114
118
  return this.data.format;
115
119
  }
116
120
  get mustReencodeAudio() {
117
- return this.audioStreams.some(
118
- (stream) => stream.codec_name !== "aac" || stream.sample_rate !== "48000"
119
- );
121
+ return this.audioStreams.some((stream) => stream.codec_name !== "aac");
120
122
  }
121
123
  get mustReencodeVideo() {
122
124
  return false;
@@ -148,9 +150,7 @@ class Probe {
148
150
  "-c:a",
149
151
  "aac",
150
152
  "-b:a",
151
- "192k",
152
- "-ar",
153
- "48000"
153
+ "192k"
154
154
  ];
155
155
  }
156
156
  return ["-c:a", "copy"];
@@ -163,31 +163,60 @@ class Probe {
163
163
  return [
164
164
  "-c:v",
165
165
  "h264",
166
+ // Filter out SEI NAL units that aren't supported by the webcodecs decoder
167
+ "-bsf:v",
168
+ "filter_units=remove_types=6",
166
169
  "-pix_fmt",
167
170
  "yuv420p"
168
171
  ];
169
172
  }
170
- return ["-c:v", "copy"];
173
+ return [
174
+ "-c:v",
175
+ "copy",
176
+ // Filter out SEI NAL units that aren't supported by the webcodecs decoder
177
+ "-bsf:v",
178
+ "filter_units=remove_types=6"
179
+ ];
171
180
  }
172
181
  createConformingReadstream() {
173
182
  if (!this.mustProcess) {
174
183
  return node_fs.createReadStream(this.absolutePath);
175
184
  }
176
- return node_child_process.spawn("ffmpeg", [
185
+ const ffmpegConformanceArgs = [
177
186
  "-i",
178
187
  this.absolutePath,
179
188
  ...this.ffmpegAudioOptions,
180
189
  ...this.ffmpegVideoOptions,
181
190
  "-f",
182
191
  "mp4",
183
- "-frag_duration",
184
- "8000000",
192
+ "-movflags",
193
+ "cmaf+frag_keyframe+empty_moov",
194
+ "pipe:1"
195
+ ];
196
+ log("Running ffmpeg", ffmpegConformanceArgs);
197
+ const ffmpegConformer = node_child_process.spawn("ffmpeg", ffmpegConformanceArgs, {
198
+ stdio: ["ignore", "pipe", "pipe"]
199
+ });
200
+ ffmpegConformer.stderr.on("data", (data) => {
201
+ log(data.toString());
202
+ });
203
+ const ffmpegFragmentArgs = [
204
+ "-i",
205
+ "-",
206
+ "-c",
207
+ "copy",
208
+ "-f",
209
+ "mp4",
185
210
  "-movflags",
186
211
  "frag_keyframe+empty_moov",
187
212
  "pipe:1"
188
- ], {
189
- stdio: ["ignore", "pipe", "inherit"]
190
- }).stdout;
213
+ ];
214
+ log("Running ffmpeg", ffmpegFragmentArgs);
215
+ const ffmpegFragmenter = node_child_process.spawn("ffmpeg", ffmpegFragmentArgs, {
216
+ stdio: ["pipe", "pipe", "pipe"]
217
+ });
218
+ ffmpegConformer.stdout.pipe(ffmpegFragmenter.stdin);
219
+ return ffmpegFragmenter.stdout;
191
220
  }
192
221
  }
193
222
  exports.AudioStreamSchema = AudioStreamSchema;
@@ -1,7 +1,9 @@
1
1
  import { spawn } from "node:child_process";
2
- import { execPromise } from "../../../lib/util/execPromise.js";
3
- import * as z from "zod";
4
2
  import { createReadStream } from "node:fs";
3
+ import * as z from "zod";
4
+ import debug from "debug";
5
+ import { execPromise } from "../../../lib/util/execPromise.js";
6
+ const log = debug("ef:assets:probe");
5
7
  const AudioStreamSchema = z.object({
6
8
  index: z.number(),
7
9
  codec_name: z.string(),
@@ -72,9 +74,11 @@ class Probe {
72
74
  this.data = ProbeSchema.parse(rawData);
73
75
  }
74
76
  static async probePath(absolutePath) {
75
- const probeResult = await execPromise(
76
- `ffprobe -v error -show_format -show_streams -of json ${absolutePath}`
77
- );
77
+ const probeCommand = `ffprobe -v error -show_format -show_streams -of json ${absolutePath}`;
78
+ log("Probing", probeCommand);
79
+ const probeResult = await execPromise(probeCommand);
80
+ log("Probe result", probeResult.stdout);
81
+ log("Probe stderr", probeResult.stderr);
78
82
  const json = JSON.parse(probeResult.stdout);
79
83
  return new Probe(absolutePath, json);
80
84
  }
@@ -95,9 +99,7 @@ class Probe {
95
99
  return this.data.format;
96
100
  }
97
101
  get mustReencodeAudio() {
98
- return this.audioStreams.some(
99
- (stream) => stream.codec_name !== "aac" || stream.sample_rate !== "48000"
100
- );
102
+ return this.audioStreams.some((stream) => stream.codec_name !== "aac");
101
103
  }
102
104
  get mustReencodeVideo() {
103
105
  return false;
@@ -129,9 +131,7 @@ class Probe {
129
131
  "-c:a",
130
132
  "aac",
131
133
  "-b:a",
132
- "192k",
133
- "-ar",
134
- "48000"
134
+ "192k"
135
135
  ];
136
136
  }
137
137
  return ["-c:a", "copy"];
@@ -144,31 +144,60 @@ class Probe {
144
144
  return [
145
145
  "-c:v",
146
146
  "h264",
147
+ // Filter out SEI NAL units that aren't supported by the webcodecs decoder
148
+ "-bsf:v",
149
+ "filter_units=remove_types=6",
147
150
  "-pix_fmt",
148
151
  "yuv420p"
149
152
  ];
150
153
  }
151
- return ["-c:v", "copy"];
154
+ return [
155
+ "-c:v",
156
+ "copy",
157
+ // Filter out SEI NAL units that aren't supported by the webcodecs decoder
158
+ "-bsf:v",
159
+ "filter_units=remove_types=6"
160
+ ];
152
161
  }
153
162
  createConformingReadstream() {
154
163
  if (!this.mustProcess) {
155
164
  return createReadStream(this.absolutePath);
156
165
  }
157
- return spawn("ffmpeg", [
166
+ const ffmpegConformanceArgs = [
158
167
  "-i",
159
168
  this.absolutePath,
160
169
  ...this.ffmpegAudioOptions,
161
170
  ...this.ffmpegVideoOptions,
162
171
  "-f",
163
172
  "mp4",
164
- "-frag_duration",
165
- "8000000",
173
+ "-movflags",
174
+ "cmaf+frag_keyframe+empty_moov",
175
+ "pipe:1"
176
+ ];
177
+ log("Running ffmpeg", ffmpegConformanceArgs);
178
+ const ffmpegConformer = spawn("ffmpeg", ffmpegConformanceArgs, {
179
+ stdio: ["ignore", "pipe", "pipe"]
180
+ });
181
+ ffmpegConformer.stderr.on("data", (data) => {
182
+ log(data.toString());
183
+ });
184
+ const ffmpegFragmentArgs = [
185
+ "-i",
186
+ "-",
187
+ "-c",
188
+ "copy",
189
+ "-f",
190
+ "mp4",
166
191
  "-movflags",
167
192
  "frag_keyframe+empty_moov",
168
193
  "pipe:1"
169
- ], {
170
- stdio: ["ignore", "pipe", "inherit"]
171
- }).stdout;
194
+ ];
195
+ log("Running ffmpeg", ffmpegFragmentArgs);
196
+ const ffmpegFragmenter = spawn("ffmpeg", ffmpegFragmentArgs, {
197
+ stdio: ["pipe", "pipe", "pipe"]
198
+ });
199
+ ffmpegConformer.stdout.pipe(ffmpegFragmenter.stdin);
200
+ return ffmpegFragmenter.stdout;
172
201
  }
173
202
  }
174
203
  export {
@@ -13,7 +13,7 @@ const idempotentTask = ({
13
13
  }) => {
14
14
  const tasks = {};
15
15
  return async (rootDir, absolutePath, ...args) => {
16
- const log = debug(`@ef:${label}`);
16
+ const log = debug(`ef:${label}`);
17
17
  const md5$1 = await md5.md5FilePath(absolutePath);
18
18
  const cacheDir = path.join(rootDir, ".cache", md5$1);
19
19
  log(`Cache dir: ${cacheDir}`);
@@ -21,16 +21,16 @@ const idempotentTask = ({
21
21
  const cachePath = path.join(cacheDir, filename(absolutePath, ...args));
22
22
  const key = cachePath;
23
23
  if (node_fs.existsSync(cachePath)) {
24
- log(`Returning cached @ef:${label} task for ${key}`);
24
+ log(`Returning cached ef:${label} task for ${key}`);
25
25
  return { cachePath, md5Sum: md5$1 };
26
26
  }
27
27
  const maybeTask = tasks[key];
28
28
  if (maybeTask) {
29
- log(`Returning existing @ef:${label} task for ${key}`);
29
+ log(`Returning existing ef:${label} task for ${key}`);
30
30
  await maybeTask;
31
31
  return { cachePath, md5Sum: md5$1 };
32
32
  }
33
- log(`Creating new @ef:${label} task for ${key}`);
33
+ log(`Creating new ef:${label} task for ${key}`);
34
34
  const task = runner(absolutePath, ...args);
35
35
  tasks[key] = task;
36
36
  log(`Awaiting task for ${key}`);
@@ -11,7 +11,7 @@ const idempotentTask = ({
11
11
  }) => {
12
12
  const tasks = {};
13
13
  return async (rootDir, absolutePath, ...args) => {
14
- const log = debug(`@ef:${label}`);
14
+ const log = debug(`ef:${label}`);
15
15
  const md5 = await md5FilePath(absolutePath);
16
16
  const cacheDir = path.join(rootDir, ".cache", md5);
17
17
  log(`Cache dir: ${cacheDir}`);
@@ -19,16 +19,16 @@ const idempotentTask = ({
19
19
  const cachePath = path.join(cacheDir, filename(absolutePath, ...args));
20
20
  const key = cachePath;
21
21
  if (existsSync(cachePath)) {
22
- log(`Returning cached @ef:${label} task for ${key}`);
22
+ log(`Returning cached ef:${label} task for ${key}`);
23
23
  return { cachePath, md5Sum: md5 };
24
24
  }
25
25
  const maybeTask = tasks[key];
26
26
  if (maybeTask) {
27
- log(`Returning existing @ef:${label} task for ${key}`);
27
+ log(`Returning existing ef:${label} task for ${key}`);
28
28
  await maybeTask;
29
29
  return { cachePath, md5Sum: md5 };
30
30
  }
31
- log(`Creating new @ef:${label} task for ${key}`);
31
+ log(`Creating new ef:${label} task for ${key}`);
32
32
  const task = runner(absolutePath, ...args);
33
33
  tasks[key] = task;
34
34
  log(`Awaiting task for ${key}`);
@@ -4,7 +4,7 @@ const execPromise = require("../../../../lib/util/execPromise.cjs");
4
4
  const idempotentTask = require("../idempotentTask.cjs");
5
5
  const debug = require("debug");
6
6
  const path = require("node:path");
7
- const log = debug("@ef:generateCaptions");
7
+ const log = debug("ef:generateCaptions");
8
8
  const generateCaptionData = idempotentTask.idempotentTask({
9
9
  label: "captions",
10
10
  filename: (absolutePath) => `${path.basename(absolutePath)}.captions.json`,
@@ -2,7 +2,7 @@ import { execPromise } from "../../../../lib/util/execPromise.js";
2
2
  import { idempotentTask } from "../idempotentTask.js";
3
3
  import debug from "debug";
4
4
  import { basename } from "node:path";
5
- const log = debug("@ef:generateCaptions");
5
+ const log = debug("ef:generateCaptions");
6
6
  const generateCaptionData = idempotentTask({
7
7
  label: "captions",
8
8
  filename: (absolutePath) => `${basename(absolutePath)}.captions.json`,
@@ -11,7 +11,7 @@ const generateTrackTask = idempotentTask.idempotentTask({
11
11
  label: "track",
12
12
  filename: (absolutePath, trackId) => `${path.basename(absolutePath)}.track-${trackId}.mp4`,
13
13
  runner: async (absolutePath, trackId) => {
14
- const log = debug("@ef:generateTrackFragment");
14
+ const log = debug("ef:generateTrackFragment");
15
15
  const probe = await Probe.Probe.probePath(absolutePath);
16
16
  const readStream = probe.createConformingReadstream();
17
17
  const mp4File = new MP4File.MP4File();
@@ -9,7 +9,7 @@ const generateTrackTask = idempotentTask({
9
9
  label: "track",
10
10
  filename: (absolutePath, trackId) => `${basename(absolutePath)}.track-${trackId}.mp4`,
11
11
  runner: async (absolutePath, trackId) => {
12
- const log = debug("@ef:generateTrackFragment");
12
+ const log = debug("ef:generateTrackFragment");
13
13
  const probe = await Probe.probePath(absolutePath);
14
14
  const readStream = probe.createConformingReadstream();
15
15
  const mp4File = new MP4File();
@@ -7,7 +7,7 @@ const mp4FileWritable = require("../mp4FileWritable.cjs");
7
7
  const path = require("node:path");
8
8
  const Probe = require("../Probe.cjs");
9
9
  const generateTrackFragmentIndexFromPath = async (absolutePath) => {
10
- const log = debug("@ef:generateTrackFragment");
10
+ const log = debug("ef:generateTrackFragment");
11
11
  const probe = await Probe.Probe.probePath(absolutePath);
12
12
  const readStream = probe.createConformingReadstream();
13
13
  const mp4File = new MP4File.MP4File();
@@ -5,7 +5,7 @@ import { mp4FileWritable } from "../mp4FileWritable.js";
5
5
  import { basename } from "node:path";
6
6
  import { Probe } from "../Probe.js";
7
7
  const generateTrackFragmentIndexFromPath = async (absolutePath) => {
8
- const log = debug("@ef:generateTrackFragment");
8
+ const log = debug("ef:generateTrackFragment");
9
9
  const probe = await Probe.probePath(absolutePath);
10
10
  const readStream = probe.createConformingReadstream();
11
11
  const mp4File = new MP4File();
package/package.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "name": "@editframe/assets",
3
- "version": "0.7.0-beta.1",
3
+ "version": "0.7.0-beta.4",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
7
- "import": "./dist/packages/assets/src/index.js",
8
- "require": "./dist/packages/assets/src/index.cjs"
7
+ "import": {
8
+ "default": "./dist/packages/assets/src/index.js",
9
+ "types": "./dist/packages/assets/src/index.d.ts"
10
+ },
11
+ "require": {
12
+ "default": "./dist/packages/assets/src/index.cjs",
13
+ "types": "./dist/packages/assets/src/index.d.ts"
14
+ }
9
15
  }
10
16
  },
11
17
  "type": "module",
12
- "types": "./dist/packages/assets/src/index.d.ts",
13
18
  "scripts": {
14
19
  "typecheck": "tsc --noEmit --emitDeclarationOnly false",
15
20
  "build": "vite build",
@@ -18,6 +23,7 @@
18
23
  "author": "",
19
24
  "license": "UNLICENSED",
20
25
  "dependencies": {
26
+ "debug": "^4.3.5",
21
27
  "mp4box": "^0.5.2",
22
28
  "zod": "^3.23.8"
23
29
  },
@@ -3,7 +3,7 @@ import { idempotentTask } from "../idempotentTask";
3
3
  import debug from "debug";
4
4
  import { basename } from "node:path";
5
5
 
6
- const log = debug("@ef:generateCaptions");
6
+ const log = debug("ef:generateCaptions");
7
7
 
8
8
  const generateCaptionData = idempotentTask({
9
9
  label: "captions",
@@ -11,7 +11,7 @@ const generateTrackTask = idempotentTask({
11
11
  filename: (absolutePath: string, trackId: number) =>
12
12
  `${basename(absolutePath)}.track-${trackId}.mp4`,
13
13
  runner: async (absolutePath, trackId: number) => {
14
- const log = debug("@ef:generateTrackFragment");
14
+ const log = debug("ef:generateTrackFragment");
15
15
  const probe = await Probe.probePath(absolutePath);
16
16
  const readStream = probe.createConformingReadstream();
17
17
  const mp4File = new MP4File();
@@ -6,7 +6,7 @@ import { basename } from "node:path";
6
6
  import { Probe, type TrackFragmentIndex } from "../Probe";
7
7
 
8
8
  const generateTrackFragmentIndexFromPath = async (absolutePath: string) => {
9
- const log = debug("@ef:generateTrackFragment");
9
+ const log = debug("ef:generateTrackFragment");
10
10
  const probe = await Probe.probePath(absolutePath);
11
11
  const readStream = probe.createConformingReadstream();
12
12