@editframe/assets 0.40.0 → 0.40.1-beta.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 (35) hide show
  1. package/dist/Probe.cjs +28 -9
  2. package/dist/Probe.cjs.map +1 -1
  3. package/dist/Probe.d.cts +1 -0
  4. package/dist/Probe.d.ts +1 -0
  5. package/dist/Probe.js +29 -9
  6. package/dist/Probe.js.map +1 -1
  7. package/dist/generateFragmentIndex.cjs +39 -26
  8. package/dist/generateFragmentIndex.cjs.map +1 -1
  9. package/dist/generateFragmentIndex.d.cts +5 -1
  10. package/dist/generateFragmentIndex.d.ts +5 -1
  11. package/dist/generateFragmentIndex.js +35 -27
  12. package/dist/generateFragmentIndex.js.map +1 -1
  13. package/dist/generateSingleTrack.cjs.map +1 -1
  14. package/dist/generateSingleTrack.js.map +1 -1
  15. package/dist/idempotentTask.cjs +29 -5
  16. package/dist/idempotentTask.cjs.map +1 -1
  17. package/dist/idempotentTask.js +29 -5
  18. package/dist/idempotentTask.js.map +1 -1
  19. package/dist/package.cjs +12 -0
  20. package/dist/package.cjs.map +1 -0
  21. package/dist/package.js +6 -0
  22. package/dist/package.js.map +1 -0
  23. package/dist/tasks/findOrCreateCaptions.cjs +13 -6
  24. package/dist/tasks/findOrCreateCaptions.cjs.map +1 -1
  25. package/dist/tasks/findOrCreateCaptions.js +13 -6
  26. package/dist/tasks/findOrCreateCaptions.js.map +1 -1
  27. package/dist/tasks/generateScrubTrack.cjs +1 -11
  28. package/dist/tasks/generateScrubTrack.cjs.map +1 -1
  29. package/dist/tasks/generateScrubTrack.js +1 -11
  30. package/dist/tasks/generateScrubTrack.js.map +1 -1
  31. package/dist/tasks/generateTrackFragmentIndex.cjs +22 -28
  32. package/dist/tasks/generateTrackFragmentIndex.cjs.map +1 -1
  33. package/dist/tasks/generateTrackFragmentIndex.js +22 -28
  34. package/dist/tasks/generateTrackFragmentIndex.js.map +1 -1
  35. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"generateTrackFragmentIndex.cjs","names":["Probe","startTimeOffsetMs: number | undefined","trackFragmentIndexes: Record<number, TrackFragmentIndex>","generateFragmentIndex","idempotentTask"],"sources":["../../src/tasks/generateTrackFragmentIndex.ts"],"sourcesContent":["import { idempotentTask } from \"../idempotentTask.js\";\nimport debug from \"debug\";\nimport { basename } from \"node:path\";\nimport { Probe } from \"../Probe.js\";\nimport { generateFragmentIndex } from \"../generateFragmentIndex.js\";\nimport type { TrackFragmentIndex } from \"../Probe.js\";\n\nexport const generateTrackFragmentIndexFromPath = async (\n absolutePath: string,\n) => {\n const log = debug(\"ef:generateTrackFragment\");\n const probe = await Probe.probePath(absolutePath);\n\n // Extract timing offset from probe metadata (same logic as processISOBMFF.ts)\n let startTimeOffsetMs: number | undefined;\n\n // First check format-level start_time\n if (probe.format.start_time && Number(probe.format.start_time) !== 0) {\n startTimeOffsetMs = Number(probe.format.start_time) * 1000;\n log(\n `Extracted format start_time offset: ${probe.format.start_time}s (${startTimeOffsetMs}ms)`,\n );\n } else {\n // Check for video stream start_time (more common)\n const videoStream = probe.streams.find(\n (stream) => stream.codec_type === \"video\",\n );\n if (\n videoStream &&\n videoStream.start_time &&\n Number(videoStream.start_time) !== 0\n ) {\n startTimeOffsetMs = Number(videoStream.start_time) * 1000;\n log(\n `Extracted video stream start_time offset: ${videoStream.start_time}s (${startTimeOffsetMs}ms)`,\n );\n } else {\n log(\n \"No format/stream timing offset found - will detect from composition time\",\n );\n }\n }\n\n log(\n `Generating track fragment index for ${absolutePath} using single-track approach`,\n );\n\n // FIXED: Generate fragment indexes from individual single-track files\n // This ensures byte offsets match the actual single-track files that clients will request\n const trackFragmentIndexes: Record<number, TrackFragmentIndex> = {};\n\n // Process each audio/video stream as a separate track\n for (let streamIndex = 0; streamIndex < probe.streams.length; streamIndex++) {\n const stream = probe.streams[streamIndex]!;\n\n // Only process audio and video streams\n if (stream.codec_type !== \"audio\" && stream.codec_type !== \"video\") {\n continue;\n }\n\n const trackId = streamIndex + 1; // Convert to 1-based track ID\n log(`Processing track ${trackId} (${stream.codec_type})`);\n\n // Generate single-track file and its fragment index\n const trackStream = probe.createTrackReadstream(streamIndex);\n const trackIdMapping = { 0: trackId }; // Map single-track stream index 0 to original track ID\n\n const singleTrackIndexes = await generateFragmentIndex(\n trackStream,\n startTimeOffsetMs,\n trackIdMapping,\n );\n\n // Merge the single-track index into the combined result\n Object.assign(trackFragmentIndexes, singleTrackIndexes);\n }\n\n // Generate scrub track fragment index if video stream exists\n if (probe.videoStreams.length > 0) {\n try {\n log(\"Generating scrub track fragment index\");\n // Generate scrub track stream and fragment index directly (don't generate full file)\n const scrubStream = probe.createScrubTrackReadstream();\n const scrubTrackId = -1;\n const trackIdMapping = { 0: scrubTrackId }; // Map single-track stream index 0 to scrub track ID -1\n\n const scrubFragmentIndex = await generateFragmentIndex(\n scrubStream,\n startTimeOffsetMs,\n trackIdMapping,\n );\n\n if (scrubFragmentIndex[scrubTrackId]) {\n trackFragmentIndexes[scrubTrackId] = scrubFragmentIndex[scrubTrackId]!;\n log(\"Scrub track fragment index generated successfully\");\n }\n } catch (error) {\n log(`Failed to generate scrub track fragment index: ${error}`);\n // Don't fail the entire operation if scrub track generation fails\n }\n }\n\n return trackFragmentIndexes;\n};\n\nconst generateTrackFragmentIndexTask = idempotentTask({\n label: \"trackFragmentIndex\",\n filename: (absolutePath) => `${basename(absolutePath)}.tracks.json`,\n runner: async (absolutePath: string) => {\n const index = await generateTrackFragmentIndexFromPath(absolutePath);\n return JSON.stringify(index, null, 2);\n },\n});\n\nexport const generateTrackFragmentIndex = async (\n cacheRoot: string,\n absolutePath: string,\n) => {\n try {\n return await generateTrackFragmentIndexTask(cacheRoot, absolutePath);\n } catch (error) {\n console.trace(\"Error generating track fragment index\", error);\n throw error;\n }\n};\n"],"mappings":";;;;;;;;;;AAOA,MAAa,qCAAqC,OAChD,iBACG;CACH,MAAM,yBAAY,2BAA2B;CAC7C,MAAM,QAAQ,MAAMA,oBAAM,UAAU,aAAa;CAGjD,IAAIC;AAGJ,KAAI,MAAM,OAAO,cAAc,OAAO,MAAM,OAAO,WAAW,KAAK,GAAG;AACpE,sBAAoB,OAAO,MAAM,OAAO,WAAW,GAAG;AACtD,MACE,uCAAuC,MAAM,OAAO,WAAW,KAAK,kBAAkB,KACvF;QACI;EAEL,MAAM,cAAc,MAAM,QAAQ,MAC/B,WAAW,OAAO,eAAe,QACnC;AACD,MACE,eACA,YAAY,cACZ,OAAO,YAAY,WAAW,KAAK,GACnC;AACA,uBAAoB,OAAO,YAAY,WAAW,GAAG;AACrD,OACE,6CAA6C,YAAY,WAAW,KAAK,kBAAkB,KAC5F;QAED,KACE,2EACD;;AAIL,KACE,uCAAuC,aAAa,8BACrD;CAID,MAAMC,uBAA2D,EAAE;AAGnE,MAAK,IAAI,cAAc,GAAG,cAAc,MAAM,QAAQ,QAAQ,eAAe;EAC3E,MAAM,SAAS,MAAM,QAAQ;AAG7B,MAAI,OAAO,eAAe,WAAW,OAAO,eAAe,QACzD;EAGF,MAAM,UAAU,cAAc;AAC9B,MAAI,oBAAoB,QAAQ,IAAI,OAAO,WAAW,GAAG;EAMzD,MAAM,qBAAqB,MAAMC,oDAHb,MAAM,sBAAsB,YAAY,EAK1D,mBAJqB,EAAE,GAAG,SAAS,CAMpC;AAGD,SAAO,OAAO,sBAAsB,mBAAmB;;AAIzD,KAAI,MAAM,aAAa,SAAS,EAC9B,KAAI;AACF,MAAI,wCAAwC;EAE5C,MAAM,cAAc,MAAM,4BAA4B;EACtD,MAAM,eAAe;EAGrB,MAAM,qBAAqB,MAAMA,oDAC/B,aACA,mBAJqB,EAAE,GAAG,cAAc,CAMzC;AAED,MAAI,mBAAmB,eAAe;AACpC,wBAAqB,gBAAgB,mBAAmB;AACxD,OAAI,oDAAoD;;UAEnD,OAAO;AACd,MAAI,kDAAkD,QAAQ;;AAKlE,QAAO;;AAGT,MAAM,iCAAiCC,sCAAe;CACpD,OAAO;CACP,WAAW,iBAAiB,2BAAY,aAAa,CAAC;CACtD,QAAQ,OAAO,iBAAyB;EACtC,MAAM,QAAQ,MAAM,mCAAmC,aAAa;AACpE,SAAO,KAAK,UAAU,OAAO,MAAM,EAAE;;CAExC,CAAC;AAEF,MAAa,6BAA6B,OACxC,WACA,iBACG;AACH,KAAI;AACF,SAAO,MAAM,+BAA+B,WAAW,aAAa;UAC7D,OAAO;AACd,UAAQ,MAAM,yCAAyC,MAAM;AAC7D,QAAM"}
1
+ {"version":3,"file":"generateTrackFragmentIndex.cjs","names":["Probe","generateFragmentIndex","scrubTask: Promise<Record<number, TrackFragmentIndex> | null>","trackFragmentIndexes: Record<number, TrackFragmentIndex>","idempotentTask"],"sources":["../../src/tasks/generateTrackFragmentIndex.ts"],"sourcesContent":["import { idempotentTask } from \"../idempotentTask.js\";\nimport debug from \"debug\";\nimport { basename } from \"node:path\";\nimport { Probe } from \"../Probe.js\";\nimport { generateFragmentIndex } from \"../generateFragmentIndex.js\";\nimport type { TrackFragmentIndex } from \"../Probe.js\";\n\nexport const generateTrackFragmentIndexFromPath = async (\n absolutePath: string,\n) => {\n const log = debug(\"ef:generateTrackFragment\");\n const probe = await Probe.probePath(absolutePath);\n\n const startTimeOffsetMs = probe.startTimeOffsetMs;\n if (startTimeOffsetMs !== undefined) {\n log(`Extracted start_time offset: ${startTimeOffsetMs}ms`);\n } else {\n log(\"No format/stream timing offset found - will detect from composition time\");\n }\n\n log(\n `Generating track fragment index for ${absolutePath} using single-track approach`,\n );\n\n // Process all audio/video streams and scrub track in parallel\n const trackTasks = probe.streams\n .map((stream, streamIndex) => {\n if (stream.codec_type !== \"audio\" && stream.codec_type !== \"video\") {\n return null;\n }\n const trackId = streamIndex + 1;\n log(`Processing track ${trackId} (${stream.codec_type})`);\n const trackStream = probe.createTrackReadstream(streamIndex);\n const trackIdMapping = { 0: trackId };\n return generateFragmentIndex(trackStream, startTimeOffsetMs, trackIdMapping);\n })\n .filter((task): task is Promise<Record<number, TrackFragmentIndex>> => task !== null);\n\n const scrubTask: Promise<Record<number, TrackFragmentIndex> | null> =\n probe.videoStreams.length > 0\n ? (async () => {\n try {\n log(\"Generating scrub track fragment index\");\n const scrubStream = probe.createScrubTrackReadstream();\n const scrubTrackId = -1;\n const result = await generateFragmentIndex(\n scrubStream,\n startTimeOffsetMs,\n { 0: scrubTrackId },\n );\n log(\"Scrub track fragment index generated successfully\");\n return result;\n } catch (error) {\n log(`Failed to generate scrub track fragment index: ${error}`);\n return null;\n }\n })()\n : Promise.resolve(null);\n\n const [trackResults, scrubResult] = await Promise.all([\n Promise.all(trackTasks),\n scrubTask,\n ]);\n\n const trackFragmentIndexes: Record<number, TrackFragmentIndex> = {};\n for (const result of trackResults) {\n Object.assign(trackFragmentIndexes, result);\n }\n if (scrubResult) {\n Object.assign(trackFragmentIndexes, scrubResult);\n }\n\n return trackFragmentIndexes;\n};\n\nconst generateTrackFragmentIndexTask = idempotentTask({\n label: \"trackFragmentIndex\",\n filename: (absolutePath) => `${basename(absolutePath)}.tracks.json`,\n runner: async (absolutePath: string) => {\n const index = await generateTrackFragmentIndexFromPath(absolutePath);\n return JSON.stringify(index, null, 2);\n },\n});\n\nexport const generateTrackFragmentIndex = async (\n cacheRoot: string,\n absolutePath: string,\n) => {\n try {\n return await generateTrackFragmentIndexTask(cacheRoot, absolutePath);\n } catch (error) {\n console.trace(\"Error generating track fragment index\", error);\n throw error;\n }\n};\n"],"mappings":";;;;;;;;;;AAOA,MAAa,qCAAqC,OAChD,iBACG;CACH,MAAM,yBAAY,2BAA2B;CAC7C,MAAM,QAAQ,MAAMA,oBAAM,UAAU,aAAa;CAEjD,MAAM,oBAAoB,MAAM;AAChC,KAAI,sBAAsB,OACxB,KAAI,gCAAgC,kBAAkB,IAAI;KAE1D,KAAI,2EAA2E;AAGjF,KACE,uCAAuC,aAAa,8BACrD;CAGD,MAAM,aAAa,MAAM,QACtB,KAAK,QAAQ,gBAAgB;AAC5B,MAAI,OAAO,eAAe,WAAW,OAAO,eAAe,QACzD,QAAO;EAET,MAAM,UAAU,cAAc;AAC9B,MAAI,oBAAoB,QAAQ,IAAI,OAAO,WAAW,GAAG;AAGzD,SAAOC,oDAFa,MAAM,sBAAsB,YAAY,EAElB,mBADnB,EAAE,GAAG,SAAS,CACuC;GAC5E,CACD,QAAQ,SAA8D,SAAS,KAAK;CAEvF,MAAMC,YACJ,MAAM,aAAa,SAAS,KACvB,YAAY;AACX,MAAI;AACF,OAAI,wCAAwC;GAC5C,MAAM,cAAc,MAAM,4BAA4B;GACtD,MAAM,eAAe;GACrB,MAAM,SAAS,MAAMD,oDACnB,aACA,mBACA,EAAE,GAAG,cAAc,CACpB;AACD,OAAI,oDAAoD;AACxD,UAAO;WACA,OAAO;AACd,OAAI,kDAAkD,QAAQ;AAC9D,UAAO;;KAEP,GACJ,QAAQ,QAAQ,KAAK;CAE3B,MAAM,CAAC,cAAc,eAAe,MAAM,QAAQ,IAAI,CACpD,QAAQ,IAAI,WAAW,EACvB,UACD,CAAC;CAEF,MAAME,uBAA2D,EAAE;AACnE,MAAK,MAAM,UAAU,aACnB,QAAO,OAAO,sBAAsB,OAAO;AAE7C,KAAI,YACF,QAAO,OAAO,sBAAsB,YAAY;AAGlD,QAAO;;AAGT,MAAM,iCAAiCC,sCAAe;CACpD,OAAO;CACP,WAAW,iBAAiB,2BAAY,aAAa,CAAC;CACtD,QAAQ,OAAO,iBAAyB;EACtC,MAAM,QAAQ,MAAM,mCAAmC,aAAa;AACpE,SAAO,KAAK,UAAU,OAAO,MAAM,EAAE;;CAExC,CAAC;AAEF,MAAa,6BAA6B,OACxC,WACA,iBACG;AACH,KAAI;AACF,SAAO,MAAM,+BAA+B,WAAW,aAAa;UAC7D,OAAO;AACd,UAAQ,MAAM,yCAAyC,MAAM;AAC7D,QAAM"}
@@ -8,39 +8,33 @@ import { basename } from "node:path";
8
8
  const generateTrackFragmentIndexFromPath = async (absolutePath) => {
9
9
  const log = debug("ef:generateTrackFragment");
10
10
  const probe = await Probe.probePath(absolutePath);
11
- let startTimeOffsetMs;
12
- if (probe.format.start_time && Number(probe.format.start_time) !== 0) {
13
- startTimeOffsetMs = Number(probe.format.start_time) * 1e3;
14
- log(`Extracted format start_time offset: ${probe.format.start_time}s (${startTimeOffsetMs}ms)`);
15
- } else {
16
- const videoStream = probe.streams.find((stream) => stream.codec_type === "video");
17
- if (videoStream && videoStream.start_time && Number(videoStream.start_time) !== 0) {
18
- startTimeOffsetMs = Number(videoStream.start_time) * 1e3;
19
- log(`Extracted video stream start_time offset: ${videoStream.start_time}s (${startTimeOffsetMs}ms)`);
20
- } else log("No format/stream timing offset found - will detect from composition time");
21
- }
11
+ const startTimeOffsetMs = probe.startTimeOffsetMs;
12
+ if (startTimeOffsetMs !== void 0) log(`Extracted start_time offset: ${startTimeOffsetMs}ms`);
13
+ else log("No format/stream timing offset found - will detect from composition time");
22
14
  log(`Generating track fragment index for ${absolutePath} using single-track approach`);
23
- const trackFragmentIndexes = {};
24
- for (let streamIndex = 0; streamIndex < probe.streams.length; streamIndex++) {
25
- const stream = probe.streams[streamIndex];
26
- if (stream.codec_type !== "audio" && stream.codec_type !== "video") continue;
15
+ const trackTasks = probe.streams.map((stream, streamIndex) => {
16
+ if (stream.codec_type !== "audio" && stream.codec_type !== "video") return null;
27
17
  const trackId = streamIndex + 1;
28
18
  log(`Processing track ${trackId} (${stream.codec_type})`);
29
- const singleTrackIndexes = await generateFragmentIndex(probe.createTrackReadstream(streamIndex), startTimeOffsetMs, { 0: trackId });
30
- Object.assign(trackFragmentIndexes, singleTrackIndexes);
31
- }
32
- if (probe.videoStreams.length > 0) try {
33
- log("Generating scrub track fragment index");
34
- const scrubStream = probe.createScrubTrackReadstream();
35
- const scrubTrackId = -1;
36
- const scrubFragmentIndex = await generateFragmentIndex(scrubStream, startTimeOffsetMs, { 0: scrubTrackId });
37
- if (scrubFragmentIndex[scrubTrackId]) {
38
- trackFragmentIndexes[scrubTrackId] = scrubFragmentIndex[scrubTrackId];
19
+ return generateFragmentIndex(probe.createTrackReadstream(streamIndex), startTimeOffsetMs, { 0: trackId });
20
+ }).filter((task) => task !== null);
21
+ const scrubTask = probe.videoStreams.length > 0 ? (async () => {
22
+ try {
23
+ log("Generating scrub track fragment index");
24
+ const scrubStream = probe.createScrubTrackReadstream();
25
+ const scrubTrackId = -1;
26
+ const result = await generateFragmentIndex(scrubStream, startTimeOffsetMs, { 0: scrubTrackId });
39
27
  log("Scrub track fragment index generated successfully");
28
+ return result;
29
+ } catch (error) {
30
+ log(`Failed to generate scrub track fragment index: ${error}`);
31
+ return null;
40
32
  }
41
- } catch (error) {
42
- log(`Failed to generate scrub track fragment index: ${error}`);
43
- }
33
+ })() : Promise.resolve(null);
34
+ const [trackResults, scrubResult] = await Promise.all([Promise.all(trackTasks), scrubTask]);
35
+ const trackFragmentIndexes = {};
36
+ for (const result of trackResults) Object.assign(trackFragmentIndexes, result);
37
+ if (scrubResult) Object.assign(trackFragmentIndexes, scrubResult);
44
38
  return trackFragmentIndexes;
45
39
  };
46
40
  const generateTrackFragmentIndexTask = idempotentTask({
@@ -1 +1 @@
1
- {"version":3,"file":"generateTrackFragmentIndex.js","names":["startTimeOffsetMs: number | undefined","trackFragmentIndexes: Record<number, TrackFragmentIndex>"],"sources":["../../src/tasks/generateTrackFragmentIndex.ts"],"sourcesContent":["import { idempotentTask } from \"../idempotentTask.js\";\nimport debug from \"debug\";\nimport { basename } from \"node:path\";\nimport { Probe } from \"../Probe.js\";\nimport { generateFragmentIndex } from \"../generateFragmentIndex.js\";\nimport type { TrackFragmentIndex } from \"../Probe.js\";\n\nexport const generateTrackFragmentIndexFromPath = async (\n absolutePath: string,\n) => {\n const log = debug(\"ef:generateTrackFragment\");\n const probe = await Probe.probePath(absolutePath);\n\n // Extract timing offset from probe metadata (same logic as processISOBMFF.ts)\n let startTimeOffsetMs: number | undefined;\n\n // First check format-level start_time\n if (probe.format.start_time && Number(probe.format.start_time) !== 0) {\n startTimeOffsetMs = Number(probe.format.start_time) * 1000;\n log(\n `Extracted format start_time offset: ${probe.format.start_time}s (${startTimeOffsetMs}ms)`,\n );\n } else {\n // Check for video stream start_time (more common)\n const videoStream = probe.streams.find(\n (stream) => stream.codec_type === \"video\",\n );\n if (\n videoStream &&\n videoStream.start_time &&\n Number(videoStream.start_time) !== 0\n ) {\n startTimeOffsetMs = Number(videoStream.start_time) * 1000;\n log(\n `Extracted video stream start_time offset: ${videoStream.start_time}s (${startTimeOffsetMs}ms)`,\n );\n } else {\n log(\n \"No format/stream timing offset found - will detect from composition time\",\n );\n }\n }\n\n log(\n `Generating track fragment index for ${absolutePath} using single-track approach`,\n );\n\n // FIXED: Generate fragment indexes from individual single-track files\n // This ensures byte offsets match the actual single-track files that clients will request\n const trackFragmentIndexes: Record<number, TrackFragmentIndex> = {};\n\n // Process each audio/video stream as a separate track\n for (let streamIndex = 0; streamIndex < probe.streams.length; streamIndex++) {\n const stream = probe.streams[streamIndex]!;\n\n // Only process audio and video streams\n if (stream.codec_type !== \"audio\" && stream.codec_type !== \"video\") {\n continue;\n }\n\n const trackId = streamIndex + 1; // Convert to 1-based track ID\n log(`Processing track ${trackId} (${stream.codec_type})`);\n\n // Generate single-track file and its fragment index\n const trackStream = probe.createTrackReadstream(streamIndex);\n const trackIdMapping = { 0: trackId }; // Map single-track stream index 0 to original track ID\n\n const singleTrackIndexes = await generateFragmentIndex(\n trackStream,\n startTimeOffsetMs,\n trackIdMapping,\n );\n\n // Merge the single-track index into the combined result\n Object.assign(trackFragmentIndexes, singleTrackIndexes);\n }\n\n // Generate scrub track fragment index if video stream exists\n if (probe.videoStreams.length > 0) {\n try {\n log(\"Generating scrub track fragment index\");\n // Generate scrub track stream and fragment index directly (don't generate full file)\n const scrubStream = probe.createScrubTrackReadstream();\n const scrubTrackId = -1;\n const trackIdMapping = { 0: scrubTrackId }; // Map single-track stream index 0 to scrub track ID -1\n\n const scrubFragmentIndex = await generateFragmentIndex(\n scrubStream,\n startTimeOffsetMs,\n trackIdMapping,\n );\n\n if (scrubFragmentIndex[scrubTrackId]) {\n trackFragmentIndexes[scrubTrackId] = scrubFragmentIndex[scrubTrackId]!;\n log(\"Scrub track fragment index generated successfully\");\n }\n } catch (error) {\n log(`Failed to generate scrub track fragment index: ${error}`);\n // Don't fail the entire operation if scrub track generation fails\n }\n }\n\n return trackFragmentIndexes;\n};\n\nconst generateTrackFragmentIndexTask = idempotentTask({\n label: \"trackFragmentIndex\",\n filename: (absolutePath) => `${basename(absolutePath)}.tracks.json`,\n runner: async (absolutePath: string) => {\n const index = await generateTrackFragmentIndexFromPath(absolutePath);\n return JSON.stringify(index, null, 2);\n },\n});\n\nexport const generateTrackFragmentIndex = async (\n cacheRoot: string,\n absolutePath: string,\n) => {\n try {\n return await generateTrackFragmentIndexTask(cacheRoot, absolutePath);\n } catch (error) {\n console.trace(\"Error generating track fragment index\", error);\n throw error;\n }\n};\n"],"mappings":";;;;;;;AAOA,MAAa,qCAAqC,OAChD,iBACG;CACH,MAAM,MAAM,MAAM,2BAA2B;CAC7C,MAAM,QAAQ,MAAM,MAAM,UAAU,aAAa;CAGjD,IAAIA;AAGJ,KAAI,MAAM,OAAO,cAAc,OAAO,MAAM,OAAO,WAAW,KAAK,GAAG;AACpE,sBAAoB,OAAO,MAAM,OAAO,WAAW,GAAG;AACtD,MACE,uCAAuC,MAAM,OAAO,WAAW,KAAK,kBAAkB,KACvF;QACI;EAEL,MAAM,cAAc,MAAM,QAAQ,MAC/B,WAAW,OAAO,eAAe,QACnC;AACD,MACE,eACA,YAAY,cACZ,OAAO,YAAY,WAAW,KAAK,GACnC;AACA,uBAAoB,OAAO,YAAY,WAAW,GAAG;AACrD,OACE,6CAA6C,YAAY,WAAW,KAAK,kBAAkB,KAC5F;QAED,KACE,2EACD;;AAIL,KACE,uCAAuC,aAAa,8BACrD;CAID,MAAMC,uBAA2D,EAAE;AAGnE,MAAK,IAAI,cAAc,GAAG,cAAc,MAAM,QAAQ,QAAQ,eAAe;EAC3E,MAAM,SAAS,MAAM,QAAQ;AAG7B,MAAI,OAAO,eAAe,WAAW,OAAO,eAAe,QACzD;EAGF,MAAM,UAAU,cAAc;AAC9B,MAAI,oBAAoB,QAAQ,IAAI,OAAO,WAAW,GAAG;EAMzD,MAAM,qBAAqB,MAAM,sBAHb,MAAM,sBAAsB,YAAY,EAK1D,mBAJqB,EAAE,GAAG,SAAS,CAMpC;AAGD,SAAO,OAAO,sBAAsB,mBAAmB;;AAIzD,KAAI,MAAM,aAAa,SAAS,EAC9B,KAAI;AACF,MAAI,wCAAwC;EAE5C,MAAM,cAAc,MAAM,4BAA4B;EACtD,MAAM,eAAe;EAGrB,MAAM,qBAAqB,MAAM,sBAC/B,aACA,mBAJqB,EAAE,GAAG,cAAc,CAMzC;AAED,MAAI,mBAAmB,eAAe;AACpC,wBAAqB,gBAAgB,mBAAmB;AACxD,OAAI,oDAAoD;;UAEnD,OAAO;AACd,MAAI,kDAAkD,QAAQ;;AAKlE,QAAO;;AAGT,MAAM,iCAAiC,eAAe;CACpD,OAAO;CACP,WAAW,iBAAiB,GAAG,SAAS,aAAa,CAAC;CACtD,QAAQ,OAAO,iBAAyB;EACtC,MAAM,QAAQ,MAAM,mCAAmC,aAAa;AACpE,SAAO,KAAK,UAAU,OAAO,MAAM,EAAE;;CAExC,CAAC;AAEF,MAAa,6BAA6B,OACxC,WACA,iBACG;AACH,KAAI;AACF,SAAO,MAAM,+BAA+B,WAAW,aAAa;UAC7D,OAAO;AACd,UAAQ,MAAM,yCAAyC,MAAM;AAC7D,QAAM"}
1
+ {"version":3,"file":"generateTrackFragmentIndex.js","names":["scrubTask: Promise<Record<number, TrackFragmentIndex> | null>","trackFragmentIndexes: Record<number, TrackFragmentIndex>"],"sources":["../../src/tasks/generateTrackFragmentIndex.ts"],"sourcesContent":["import { idempotentTask } from \"../idempotentTask.js\";\nimport debug from \"debug\";\nimport { basename } from \"node:path\";\nimport { Probe } from \"../Probe.js\";\nimport { generateFragmentIndex } from \"../generateFragmentIndex.js\";\nimport type { TrackFragmentIndex } from \"../Probe.js\";\n\nexport const generateTrackFragmentIndexFromPath = async (\n absolutePath: string,\n) => {\n const log = debug(\"ef:generateTrackFragment\");\n const probe = await Probe.probePath(absolutePath);\n\n const startTimeOffsetMs = probe.startTimeOffsetMs;\n if (startTimeOffsetMs !== undefined) {\n log(`Extracted start_time offset: ${startTimeOffsetMs}ms`);\n } else {\n log(\"No format/stream timing offset found - will detect from composition time\");\n }\n\n log(\n `Generating track fragment index for ${absolutePath} using single-track approach`,\n );\n\n // Process all audio/video streams and scrub track in parallel\n const trackTasks = probe.streams\n .map((stream, streamIndex) => {\n if (stream.codec_type !== \"audio\" && stream.codec_type !== \"video\") {\n return null;\n }\n const trackId = streamIndex + 1;\n log(`Processing track ${trackId} (${stream.codec_type})`);\n const trackStream = probe.createTrackReadstream(streamIndex);\n const trackIdMapping = { 0: trackId };\n return generateFragmentIndex(trackStream, startTimeOffsetMs, trackIdMapping);\n })\n .filter((task): task is Promise<Record<number, TrackFragmentIndex>> => task !== null);\n\n const scrubTask: Promise<Record<number, TrackFragmentIndex> | null> =\n probe.videoStreams.length > 0\n ? (async () => {\n try {\n log(\"Generating scrub track fragment index\");\n const scrubStream = probe.createScrubTrackReadstream();\n const scrubTrackId = -1;\n const result = await generateFragmentIndex(\n scrubStream,\n startTimeOffsetMs,\n { 0: scrubTrackId },\n );\n log(\"Scrub track fragment index generated successfully\");\n return result;\n } catch (error) {\n log(`Failed to generate scrub track fragment index: ${error}`);\n return null;\n }\n })()\n : Promise.resolve(null);\n\n const [trackResults, scrubResult] = await Promise.all([\n Promise.all(trackTasks),\n scrubTask,\n ]);\n\n const trackFragmentIndexes: Record<number, TrackFragmentIndex> = {};\n for (const result of trackResults) {\n Object.assign(trackFragmentIndexes, result);\n }\n if (scrubResult) {\n Object.assign(trackFragmentIndexes, scrubResult);\n }\n\n return trackFragmentIndexes;\n};\n\nconst generateTrackFragmentIndexTask = idempotentTask({\n label: \"trackFragmentIndex\",\n filename: (absolutePath) => `${basename(absolutePath)}.tracks.json`,\n runner: async (absolutePath: string) => {\n const index = await generateTrackFragmentIndexFromPath(absolutePath);\n return JSON.stringify(index, null, 2);\n },\n});\n\nexport const generateTrackFragmentIndex = async (\n cacheRoot: string,\n absolutePath: string,\n) => {\n try {\n return await generateTrackFragmentIndexTask(cacheRoot, absolutePath);\n } catch (error) {\n console.trace(\"Error generating track fragment index\", error);\n throw error;\n }\n};\n"],"mappings":";;;;;;;AAOA,MAAa,qCAAqC,OAChD,iBACG;CACH,MAAM,MAAM,MAAM,2BAA2B;CAC7C,MAAM,QAAQ,MAAM,MAAM,UAAU,aAAa;CAEjD,MAAM,oBAAoB,MAAM;AAChC,KAAI,sBAAsB,OACxB,KAAI,gCAAgC,kBAAkB,IAAI;KAE1D,KAAI,2EAA2E;AAGjF,KACE,uCAAuC,aAAa,8BACrD;CAGD,MAAM,aAAa,MAAM,QACtB,KAAK,QAAQ,gBAAgB;AAC5B,MAAI,OAAO,eAAe,WAAW,OAAO,eAAe,QACzD,QAAO;EAET,MAAM,UAAU,cAAc;AAC9B,MAAI,oBAAoB,QAAQ,IAAI,OAAO,WAAW,GAAG;AAGzD,SAAO,sBAFa,MAAM,sBAAsB,YAAY,EAElB,mBADnB,EAAE,GAAG,SAAS,CACuC;GAC5E,CACD,QAAQ,SAA8D,SAAS,KAAK;CAEvF,MAAMA,YACJ,MAAM,aAAa,SAAS,KACvB,YAAY;AACX,MAAI;AACF,OAAI,wCAAwC;GAC5C,MAAM,cAAc,MAAM,4BAA4B;GACtD,MAAM,eAAe;GACrB,MAAM,SAAS,MAAM,sBACnB,aACA,mBACA,EAAE,GAAG,cAAc,CACpB;AACD,OAAI,oDAAoD;AACxD,UAAO;WACA,OAAO;AACd,OAAI,kDAAkD,QAAQ;AAC9D,UAAO;;KAEP,GACJ,QAAQ,QAAQ,KAAK;CAE3B,MAAM,CAAC,cAAc,eAAe,MAAM,QAAQ,IAAI,CACpD,QAAQ,IAAI,WAAW,EACvB,UACD,CAAC;CAEF,MAAMC,uBAA2D,EAAE;AACnE,MAAK,MAAM,UAAU,aACnB,QAAO,OAAO,sBAAsB,OAAO;AAE7C,KAAI,YACF,QAAO,OAAO,sBAAsB,YAAY;AAGlD,QAAO;;AAGT,MAAM,iCAAiC,eAAe;CACpD,OAAO;CACP,WAAW,iBAAiB,GAAG,SAAS,aAAa,CAAC;CACtD,QAAQ,OAAO,iBAAyB;EACtC,MAAM,QAAQ,MAAM,mCAAmC,aAAa;AACpE,SAAO,KAAK,UAAU,OAAO,MAAM,EAAE;;CAExC,CAAC;AAEF,MAAa,6BAA6B,OACxC,WACA,iBACG;AACH,KAAI;AACF,SAAO,MAAM,+BAA+B,WAAW,aAAa;UAC7D,OAAO;AACd,UAAQ,MAAM,yCAAyC,MAAM;AAC7D,QAAM"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/assets",
3
- "version": "0.40.0",
3
+ "version": "0.40.1-beta.0",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -59,4 +59,4 @@
59
59
  "./types.json": "./types.json"
60
60
  }
61
61
  }
62
- }
62
+ }