hyperframes 0.4.8 → 0.4.10

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 (2) hide show
  1. package/dist/cli.js +118 -5
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -54,7 +54,7 @@ var VERSION;
54
54
  var init_version = __esm({
55
55
  "src/version.ts"() {
56
56
  "use strict";
57
- VERSION = true ? "0.4.8" : "0.0.0-dev";
57
+ VERSION = true ? "0.4.10" : "0.0.0-dev";
58
58
  }
59
59
  });
60
60
 
@@ -5411,8 +5411,8 @@ function flushSync() {
5411
5411
  eventQueue = [];
5412
5412
  const payload = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
5413
5413
  try {
5414
- const { spawn: spawn11 } = __require("child_process");
5415
- const child = spawn11(
5414
+ const { spawn: spawn12 } = __require("child_process");
5415
+ const child = spawn12(
5416
5416
  process.execPath,
5417
5417
  [
5418
5418
  "-e",
@@ -34137,9 +34137,60 @@ __export(snapshot_exports, {
34137
34137
  default: () => snapshot_default,
34138
34138
  examples: () => examples18
34139
34139
  });
34140
- import { existsSync as existsSync45, readFileSync as readFileSync31, mkdirSync as mkdirSync24 } from "fs";
34140
+ import { spawn as spawn11 } from "child_process";
34141
+ import { existsSync as existsSync45, mkdtempSync as mkdtempSync2, readFileSync as readFileSync31, mkdirSync as mkdirSync24, rmSync as rmSync9 } from "fs";
34142
+ import { tmpdir as tmpdir4 } from "os";
34141
34143
  import { resolve as resolve32, join as join45, dirname as dirname20, relative as relative4, isAbsolute as isAbsolute4 } from "path";
34142
34144
  import { fileURLToPath as fileURLToPath8 } from "url";
34145
+ async function extractVideoFrameToBuffer(videoPath, timeSeconds) {
34146
+ const tmp = mkdtempSync2(join45(tmpdir4(), "hf-snapshot-frame-"));
34147
+ const outPath = join45(tmp, "frame.png");
34148
+ try {
34149
+ const result = await new Promise(
34150
+ (resolvePromise) => {
34151
+ const ff = spawn11("ffmpeg", [
34152
+ "-hide_banner",
34153
+ "-loglevel",
34154
+ "error",
34155
+ "-ss",
34156
+ String(Math.max(0, timeSeconds)),
34157
+ "-i",
34158
+ videoPath,
34159
+ "-frames:v",
34160
+ "1",
34161
+ "-q:v",
34162
+ "2",
34163
+ "-y",
34164
+ outPath
34165
+ ]);
34166
+ let stderr = "";
34167
+ let timedOut = false;
34168
+ const timer = setTimeout(() => {
34169
+ timedOut = true;
34170
+ ff.kill("SIGTERM");
34171
+ }, FFMPEG_EXTRACT_TIMEOUT_MS);
34172
+ ff.stderr.on("data", (d) => {
34173
+ stderr += d.toString();
34174
+ });
34175
+ ff.on("close", (code) => {
34176
+ clearTimeout(timer);
34177
+ resolvePromise({ code, stderr, timedOut });
34178
+ });
34179
+ ff.on("error", () => {
34180
+ clearTimeout(timer);
34181
+ resolvePromise({ code: null, stderr: "ffmpeg spawn failed", timedOut });
34182
+ });
34183
+ }
34184
+ );
34185
+ if (result.code !== 0 || result.timedOut || !existsSync45(outPath)) return null;
34186
+ return readFileSync31(outPath);
34187
+ } finally {
34188
+ try {
34189
+ rmSync9(tmp, { recursive: true, force: true });
34190
+ } catch {
34191
+ }
34192
+ }
34193
+ }
34143
34194
  async function captureSnapshots(projectDir, opts) {
34144
34195
  const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
34145
34196
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
@@ -34254,6 +34305,14 @@ async function captureSnapshots(projectDir, opts) {
34254
34305
  const positions = opts.at?.length ? opts.at : numFrames === 1 ? [duration / 2] : Array.from({ length: numFrames }, (_2, i2) => i2 / (numFrames - 1) * duration);
34255
34306
  const snapshotDir = join45(projectDir, "snapshots");
34256
34307
  mkdirSync24(snapshotDir, { recursive: true });
34308
+ let injectVideoFramesBatch2 = null;
34309
+ let syncVideoFrameVisibility2 = null;
34310
+ try {
34311
+ const engine = await Promise.resolve().then(() => (init_src2(), src_exports));
34312
+ injectVideoFramesBatch2 = engine.injectVideoFramesBatch;
34313
+ syncVideoFrameVisibility2 = engine.syncVideoFrameVisibility;
34314
+ } catch {
34315
+ }
34257
34316
  for (let i2 = 0; i2 < positions.length; i2++) {
34258
34317
  const time = positions[i2];
34259
34318
  await page.evaluate((t3) => {
@@ -34276,6 +34335,59 @@ async function captureSnapshots(projectDir, opts) {
34276
34335
  () => new Promise((r2) => requestAnimationFrame(() => requestAnimationFrame(() => r2())))
34277
34336
  );
34278
34337
  await new Promise((r2) => setTimeout(r2, 200));
34338
+ if (injectVideoFramesBatch2 && syncVideoFrameVisibility2) {
34339
+ const active = await page.evaluate((t3) => {
34340
+ return Array.from(document.querySelectorAll("video[data-start]")).map((el) => {
34341
+ const v = el;
34342
+ const start = parseFloat(v.dataset.start ?? "0") || 0;
34343
+ const rawRate = v.defaultPlaybackRate;
34344
+ const playbackRate = Number.isFinite(rawRate) && rawRate > 0 ? Math.max(0.1, Math.min(5, rawRate)) : 1;
34345
+ const mediaStart = parseFloat(v.dataset.playbackStart ?? v.dataset.mediaStart ?? "0") || 0;
34346
+ const rawDuration = parseFloat(v.dataset.duration ?? "");
34347
+ const srcDur = Number.isFinite(v.duration) && v.duration > 0 ? v.duration : 0;
34348
+ const duration2 = Number.isFinite(rawDuration) && rawDuration > 0 ? rawDuration : srcDur > 0 ? Math.max(0, (srcDur - mediaStart) / playbackRate) : Number.POSITIVE_INFINITY;
34349
+ const relTime = (t3 - start) * playbackRate + mediaStart;
34350
+ const activeNow = t3 >= start && t3 < start + duration2 && relTime >= 0 && !!v.id;
34351
+ return {
34352
+ id: v.id,
34353
+ src: v.currentSrc || v.src,
34354
+ relTime,
34355
+ active: activeNow
34356
+ };
34357
+ }).filter((entry) => entry.active && entry.src);
34358
+ }, time);
34359
+ const updates = [];
34360
+ for (const v of active) {
34361
+ let filePath = null;
34362
+ try {
34363
+ const url = new URL(v.src);
34364
+ const decodedPath = decodeURIComponent(url.pathname).replace(/^\//, "");
34365
+ const candidate = resolve32(projectDir, decodedPath);
34366
+ const rel = relative4(projectDir, candidate);
34367
+ if (!rel.startsWith("..") && !isAbsolute4(rel) && existsSync45(candidate)) {
34368
+ filePath = candidate;
34369
+ }
34370
+ } catch {
34371
+ }
34372
+ if (!filePath) continue;
34373
+ const png = await extractVideoFrameToBuffer(filePath, Math.max(0, v.relTime));
34374
+ if (!png) continue;
34375
+ updates.push({
34376
+ videoId: v.id,
34377
+ dataUri: `data:image/png;base64,${png.toString("base64")}`
34378
+ });
34379
+ }
34380
+ try {
34381
+ if (updates.length > 0) {
34382
+ await injectVideoFramesBatch2(page, updates);
34383
+ }
34384
+ await syncVideoFrameVisibility2(
34385
+ page,
34386
+ active.map((a) => a.id)
34387
+ );
34388
+ } catch {
34389
+ }
34390
+ }
34279
34391
  const timeLabel = opts.at?.length ? `${time.toFixed(1)}s` : `${Math.round(time / duration * 100)}pct`;
34280
34392
  const filename = `frame-${String(i2).padStart(2, "0")}-at-${timeLabel}.png`;
34281
34393
  const framePath = join45(snapshotDir, filename);
@@ -34290,7 +34402,7 @@ async function captureSnapshots(projectDir, opts) {
34290
34402
  }
34291
34403
  return savedPaths;
34292
34404
  }
34293
- var __filename2, __dirname3, examples18, snapshot_default;
34405
+ var __filename2, __dirname3, FFMPEG_EXTRACT_TIMEOUT_MS, examples18, snapshot_default;
34294
34406
  var init_snapshot = __esm({
34295
34407
  "src/commands/snapshot.ts"() {
34296
34408
  "use strict";
@@ -34299,6 +34411,7 @@ var init_snapshot = __esm({
34299
34411
  init_colors();
34300
34412
  __filename2 = fileURLToPath8(import.meta.url);
34301
34413
  __dirname3 = dirname20(__filename2);
34414
+ FFMPEG_EXTRACT_TIMEOUT_MS = 3e4;
34302
34415
  examples18 = [
34303
34416
  ["Capture 5 key frames from a composition", "snapshot captures/stripe"],
34304
34417
  ["Capture 10 evenly-spaced frames", "snapshot captures/stripe --frames 10"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperframes",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "HyperFrames CLI — create, preview, and render HTML video compositions",
5
5
  "repository": {
6
6
  "type": "git",