@hyperframes/producer 0.4.22 → 0.4.23

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.
@@ -101919,7 +101919,7 @@ function getAttr(tag, attr) {
101919
101919
  return match2 ? match2[1] ?? null : null;
101920
101920
  }
101921
101921
  function hasAttr(tag, attr) {
101922
- return new RegExp(`${attr}=["']`).test(tag);
101922
+ return new RegExp(`\\s${attr}(?:\\s|=|>|/)`).test(tag);
101923
101923
  }
101924
101924
  function injectAttr(tag, attr, value) {
101925
101925
  return tag.replace(/>$/, ` ${attr}="${value}">`);
@@ -102030,7 +102030,8 @@ function extractResolvedMedia(html) {
102030
102030
  src: getAttr(tag, "src") ?? void 0,
102031
102031
  start: startStr !== null ? parseFloat(startStr) : 0,
102032
102032
  duration,
102033
- mediaStart: mediaStartStr ? parseFloat(mediaStartStr) : 0
102033
+ mediaStart: mediaStartStr ? parseFloat(mediaStartStr) : 0,
102034
+ loop: hasAttr(tag, "loop")
102034
102035
  });
102035
102036
  }
102036
102037
  return resolved;
@@ -105498,6 +105499,7 @@ async function extractMediaMetadata(filePath) {
105498
105499
  videoCodec: "png",
105499
105500
  hasAudio: false,
105500
105501
  isVFR: false,
105502
+ hasAlpha: false,
105501
105503
  colorSpace: stillImageMeta.colorSpace
105502
105504
  };
105503
105505
  }
@@ -105512,6 +105514,9 @@ async function extractMediaMetadata(filePath) {
105512
105514
  const colorSpaceVal = videoStream.color_space || "";
105513
105515
  const ffprobeColorSpace = colorTransfer || colorPrimaries || colorSpaceVal ? { colorTransfer, colorPrimaries, colorSpace: colorSpaceVal } : null;
105514
105516
  const colorSpace = ffprobeColorSpace ?? stillImageMeta?.colorSpace ?? null;
105517
+ const pixelFormat = videoStream.pix_fmt || "";
105518
+ const alphaMode = videoStream.tags?.alpha_mode || "";
105519
+ const hasAlpha = /(^|[^a-z])yuva|rgba|argb|bgra|gbrap|gray[a-z0-9]*a/i.test(pixelFormat) || alphaMode === "1";
105515
105520
  return {
105516
105521
  durationSeconds: output2?.format.duration ? parseFloat(output2.format.duration) : 0,
105517
105522
  width: videoStream.width || stillImageMeta?.width || 0,
@@ -105520,6 +105525,7 @@ async function extractMediaMetadata(filePath) {
105520
105525
  videoCodec: videoStream.codec_name || "unknown",
105521
105526
  hasAudio: output2?.streams.some((s) => s.codec_type === "audio") ?? false,
105522
105527
  isVFR,
105528
+ hasAlpha,
105523
105529
  colorSpace
105524
105530
  };
105525
105531
  })();
@@ -105820,6 +105826,7 @@ function parseVideoElements(html) {
105820
105826
  start,
105821
105827
  end,
105822
105828
  mediaStart: mediaStartAttr ? parseFloat(mediaStartAttr) : 0,
105829
+ loop: el.hasAttribute("loop"),
105823
105830
  hasAudio: hasAudioAttr === "true"
105824
105831
  });
105825
105832
  }
@@ -105855,10 +105862,11 @@ function parseImageElements(html) {
105855
105862
  }
105856
105863
  async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config2, outputDirOverride) {
105857
105864
  const ffmpegProcessTimeout = config2?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
105858
- const { fps, outputDir, quality = 95, format: format3 = "jpg" } = options;
105865
+ const { fps, outputDir, quality = 95 } = options;
105859
105866
  const videoOutputDir = outputDirOverride ?? join9(outputDir, videoId);
105860
105867
  if (!existsSync9(videoOutputDir)) mkdirSync6(videoOutputDir, { recursive: true });
105861
105868
  const metadata = await extractMediaMetadata(videoPath);
105869
+ const format3 = resolveFrameFormat(metadata, options.format);
105862
105870
  const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${format3}`;
105863
105871
  const outputPattern = join9(videoOutputDir, framePattern);
105864
105872
  const isHdr = isHdrColorSpace(metadata.colorSpace);
@@ -105867,6 +105875,9 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
105867
105875
  if (isHdr && isMacOS) {
105868
105876
  args.push("-hwaccel", "videotoolbox");
105869
105877
  }
105878
+ if (metadata.hasAlpha && metadata.videoCodec === "vp9") {
105879
+ args.push("-c:v", "libvpx-vp9");
105880
+ }
105870
105881
  args.push("-ss", String(startTime), "-i", videoPath, "-t", String(duration));
105871
105882
  const vfFilters = [];
105872
105883
  if (isHdr && isMacOS) {
@@ -105978,6 +105989,10 @@ function resolveSegmentDuration(requested, mediaStart, metadata) {
105978
105989
  const sourceRemaining = metadata.durationSeconds - mediaStart;
105979
105990
  return sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
105980
105991
  }
105992
+ function resolveFrameFormat(metadata, requested) {
105993
+ if (requested) return requested;
105994
+ return metadata.hasAlpha ? "png" : "jpg";
105995
+ }
105981
105996
  async function convertVfrToCfr(inputPath, outputPath, targetFps, startTime, duration, signal, config2) {
105982
105997
  const timeout2 = config2?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
105983
105998
  const args = [
@@ -106170,12 +106185,12 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config2,
106170
106185
  breakdown.vfrPreflightMs = Date.now() - vfrPreflightStart;
106171
106186
  const phase3Start = Date.now();
106172
106187
  const cacheRootDir = config2?.extractCacheDir;
106173
- const cacheFormat = options.format ?? "jpg";
106174
106188
  async function tryCachedExtract(video, videoPath, videoDuration, i) {
106175
106189
  if (!cacheRootDir) return null;
106176
106190
  const keyInput = cacheKeyInputs[i];
106177
106191
  const probedMeta = videoMetadata[i];
106178
106192
  if (!keyInput || !probedMeta) return null;
106193
+ const cacheFormat = resolveFrameFormat(probedMeta, options.format);
106179
106194
  const keyDuration = resolveSegmentDuration(
106180
106195
  keyInput.end - keyInput.start,
106181
106196
  keyInput.mediaStart,
@@ -106208,7 +106223,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config2,
106208
106223
  video.id,
106209
106224
  video.mediaStart,
106210
106225
  videoDuration,
106211
- options,
106226
+ { ...options, format: cacheFormat },
106212
106227
  signal,
106213
106228
  config2,
106214
106229
  lookup.entry.dir
@@ -106238,7 +106253,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config2,
106238
106253
  video.id,
106239
106254
  video.mediaStart,
106240
106255
  videoDuration,
106241
- options,
106256
+ { ...options, format: resolveFrameFormat(probedMeta, options.format) },
106242
106257
  signal,
106243
106258
  config2
106244
106259
  );
@@ -106271,10 +106286,17 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config2,
106271
106286
  phaseBreakdown: breakdown
106272
106287
  };
106273
106288
  }
106274
- function getFrameAtTime(extracted, globalTime, videoStart) {
106275
- const localTime = globalTime - videoStart;
106289
+ function getFrameAtTime(extracted, globalTime, videoStart, loop = false, mediaStart = 0) {
106290
+ let localTime = globalTime - videoStart;
106276
106291
  if (localTime < 0) return null;
106292
+ const loopDuration = Math.max(0, extracted.metadata.durationSeconds - mediaStart);
106293
+ if (loop && loopDuration > 0 && localTime >= loopDuration) {
106294
+ localTime %= loopDuration;
106295
+ }
106277
106296
  const frameIndex = Math.floor(localTime * extracted.fps);
106297
+ if (loop && frameIndex >= extracted.totalFrames && extracted.totalFrames > 0) {
106298
+ return extracted.framePaths.get(extracted.totalFrames - 1) || null;
106299
+ }
106278
106300
  if (frameIndex < 0 || frameIndex >= extracted.totalFrames) return null;
106279
106301
  return extracted.framePaths.get(frameIndex) || null;
106280
106302
  }
@@ -106284,8 +106306,8 @@ var FrameLookupTable = class {
106284
106306
  activeVideoIds = /* @__PURE__ */ new Set();
106285
106307
  startCursor = 0;
106286
106308
  lastTime = null;
106287
- addVideo(extracted, start, end, mediaStart) {
106288
- this.videos.set(extracted.videoId, { extracted, start, end, mediaStart });
106309
+ addVideo(extracted, start, end, mediaStart, loop = false) {
106310
+ this.videos.set(extracted.videoId, { extracted, start, end, mediaStart, loop });
106289
106311
  this.orderedVideos = Array.from(this.videos.entries()).map(([videoId, video]) => ({ videoId, ...video })).sort((a, b) => a.start - b.start);
106290
106312
  this.resetActiveState();
106291
106313
  }
@@ -106293,7 +106315,7 @@ var FrameLookupTable = class {
106293
106315
  const video = this.videos.get(videoId);
106294
106316
  if (!video) return null;
106295
106317
  if (globalTime < video.start || globalTime >= video.end) return null;
106296
- return getFrameAtTime(video.extracted, globalTime, video.start);
106318
+ return getFrameAtTime(video.extracted, globalTime, video.start, video.loop, video.mediaStart);
106297
106319
  }
106298
106320
  resetActiveState() {
106299
106321
  this.activeVideoIds.clear();
@@ -106342,8 +106364,19 @@ var FrameLookupTable = class {
106342
106364
  for (const videoId of this.activeVideoIds) {
106343
106365
  const video = this.videos.get(videoId);
106344
106366
  if (!video) continue;
106345
- const localTime = globalTime - video.start;
106367
+ let localTime = globalTime - video.start;
106368
+ const loopDuration = Math.max(0, video.extracted.metadata.durationSeconds - video.mediaStart);
106369
+ if (video.loop && loopDuration > 0 && localTime >= loopDuration) {
106370
+ localTime %= loopDuration;
106371
+ }
106346
106372
  const frameIndex = Math.floor(localTime * video.extracted.fps);
106373
+ if (video.loop && frameIndex >= video.extracted.totalFrames) {
106374
+ const framePath2 = video.extracted.framePaths.get(video.extracted.totalFrames - 1);
106375
+ if (framePath2) {
106376
+ frames.set(videoId, { framePath: framePath2, frameIndex: video.extracted.totalFrames - 1 });
106377
+ }
106378
+ continue;
106379
+ }
106347
106380
  if (frameIndex < 0 || frameIndex >= video.extracted.totalFrames) continue;
106348
106381
  const framePath = video.extracted.framePaths.get(frameIndex);
106349
106382
  if (!framePath) continue;
@@ -106377,7 +106410,7 @@ function createFrameLookupTable(videos, extracted) {
106377
106410
  for (const ext of extracted) extractedMap.set(ext.videoId, ext);
106378
106411
  for (const video of videos) {
106379
106412
  const ext = extractedMap.get(video.id);
106380
- if (ext) table.addVideo(ext, video.start, video.end, video.mediaStart);
106413
+ if (ext) table.addVideo(ext, video.start, video.end, video.mediaStart, video.loop);
106381
106414
  }
106382
106415
  return table;
106383
106416
  }
@@ -109300,7 +109333,7 @@ async function compileHtmlFile(html, baseDir, downloadDir) {
109300
109333
  let compiledHtml = resolutions.length > 0 ? injectDurations(staticCompiled, resolutions) : staticCompiled;
109301
109334
  const preResolved = extractResolvedMedia(compiledHtml);
109302
109335
  const clampResults = await Promise.all(
109303
- preResolved.filter((el) => !!el.src).map(async (el) => {
109336
+ preResolved.filter((el) => !!el.src && !el.loop).map(async (el) => {
109304
109337
  const { duration: maxDuration } = await resolveMediaDuration(
109305
109338
  el.src,
109306
109339
  el.mediaStart,
@@ -109908,6 +109941,7 @@ async function discoverMediaFromBrowser(page) {
109908
109941
  const end = parseFloat(htmlEl.getAttribute("data-end") || "0");
109909
109942
  const duration = parseFloat(htmlEl.getAttribute("data-duration") || "0");
109910
109943
  const mediaStart = parseFloat(htmlEl.getAttribute("data-media-start") || "0");
109944
+ const loop = htmlEl.hasAttribute("loop");
109911
109945
  const hasAudio = htmlEl.getAttribute("data-has-audio") === "true";
109912
109946
  const volume = parseFloat(htmlEl.getAttribute("data-volume") || "1");
109913
109947
  results.push({
@@ -109918,6 +109952,7 @@ async function discoverMediaFromBrowser(page) {
109918
109952
  end,
109919
109953
  duration,
109920
109954
  mediaStart,
109955
+ loop,
109921
109956
  hasAudio,
109922
109957
  volume
109923
109958
  });
@@ -110792,6 +110827,9 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
110792
110827
  if (el.hasAudio && !existing.hasAudio) {
110793
110828
  existing.hasAudio = true;
110794
110829
  }
110830
+ if (el.loop && !existing.loop) {
110831
+ existing.loop = true;
110832
+ }
110795
110833
  }
110796
110834
  } else {
110797
110835
  composition.videos.push({
@@ -110800,6 +110838,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
110800
110838
  start: el.start,
110801
110839
  end: el.end,
110802
110840
  mediaStart: el.mediaStart,
110841
+ loop: el.loop,
110803
110842
  hasAudio: el.hasAudio
110804
110843
  });
110805
110844
  existingVideoIds.add(el.id);