@bendyline/squisq-video-react 2.4.12 → 2.4.14

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.
@@ -267,7 +267,10 @@ var MIME_MAP = {
267
267
  webm: "video/webm"
268
268
  };
269
269
  var VISUAL_UPDATE_FALLBACK_MS = 100;
270
+ var CAPTURE_URL_COMMIT_MAX_WAITS = 10;
270
271
  var CAPTURE_ASSET_TIMEOUT_MS = 15e3;
272
+ var RENDER_API_TIMEOUT_MS = 15e3;
273
+ var CAPTURE_SETUP_TIMEOUT_MS = 6e4;
271
274
  var RENDER_TIME_EPSILON_SECONDS = 1e-6;
272
275
  var POTENTIALLY_ANIMATED_IMAGE_URL = /(?:^data:image\/(?:gif|webp|avif)[;,]|\.(?:gif|webp|avif)(?:[?#]|$))/i;
273
276
  var CAPTURE_SVG_SELECTOR = "svg.block-svg";
@@ -419,8 +422,8 @@ async function primeIndeterminateCaptureVideos(captureRoot, primedVideos = /* @_
419
422
  primedVideos.add(video);
420
423
  return;
421
424
  }
425
+ video.dataset.captureSequential = "true";
422
426
  if (Number.isFinite(video.duration)) {
423
- video.dataset.captureSequential = "true";
424
427
  primedVideos.add(video);
425
428
  return;
426
429
  }
@@ -443,7 +446,6 @@ async function primeIndeterminateCaptureVideos(captureRoot, primedVideos = /* @_
443
446
  video.currentTime = reachableRestoreTime;
444
447
  }
445
448
  );
446
- video.dataset.captureSequential = "true";
447
449
  primedVideos.add(video);
448
450
  primedCount += 1;
449
451
  })
@@ -487,6 +489,7 @@ function createInlineProvider(images) {
487
489
  }
488
490
  function createCaptureMediaResolutionTracker(provider) {
489
491
  const pending = /* @__PURE__ */ new Set();
492
+ const resolvedUrls = /* @__PURE__ */ new Map();
490
493
  let resolutionGeneration = 0;
491
494
  let observedGeneration = 0;
492
495
  const trackedProvider = {
@@ -494,7 +497,8 @@ function createCaptureMediaResolutionTracker(provider) {
494
497
  resolutionGeneration += 1;
495
498
  const resolution = provider.resolveUrl(relativePath);
496
499
  const completion = resolution.then(
497
- () => {
500
+ (url) => {
501
+ resolvedUrls.set(relativePath, url);
498
502
  pending.delete(completion);
499
503
  },
500
504
  () => {
@@ -521,12 +525,36 @@ function createCaptureMediaResolutionTracker(provider) {
521
525
  const changed = resolutionGeneration !== observedGeneration;
522
526
  observedGeneration = resolutionGeneration;
523
527
  return changed;
524
- }
528
+ },
529
+ resolvedUrls: () => resolvedUrls
525
530
  };
526
531
  }
527
- async function waitForCaptureMediaResolutions(tracker) {
528
- while (await tracker?.waitForSettled()) {
532
+ function stripRelativePrefix(path) {
533
+ return path.replace(/^(?:\.\/)+/, "");
534
+ }
535
+ function showsUncommittedMediaUrl(root, resolved) {
536
+ const mapped = /* @__PURE__ */ new Set();
537
+ for (const [path, url] of resolved) {
538
+ if (url !== path) mapped.add(stripRelativePrefix(path));
539
+ }
540
+ if (mapped.size === 0) return false;
541
+ return Array.from(root.querySelectorAll("img[src], video[src], audio[src], source[src]")).some(
542
+ (element) => mapped.has(stripRelativePrefix(element.getAttribute("src") ?? ""))
543
+ );
544
+ }
545
+ async function waitForCaptureMediaResolutions(tracker, captureRoot) {
546
+ if (!tracker) return;
547
+ let changed = false;
548
+ while (await tracker.waitForSettled()) {
549
+ changed = true;
550
+ await waitForVisualUpdate();
551
+ }
552
+ if (!changed || !captureRoot) return;
553
+ for (let attempt = 0; attempt < CAPTURE_URL_COMMIT_MAX_WAITS && showsUncommittedMediaUrl(captureRoot, tracker.resolvedUrls()); attempt++) {
529
554
  await waitForVisualUpdate();
555
+ while (await tracker.waitForSettled()) {
556
+ await waitForVisualUpdate();
557
+ }
530
558
  }
531
559
  }
532
560
  function shouldIgnoreCaptureSibling(element, captureRoot) {
@@ -1325,7 +1353,7 @@ function useFrameCapture() {
1325
1353
  root.render(playerElement);
1326
1354
  }
1327
1355
  return new Promise((resolve, reject) => {
1328
- const timeout = setTimeout(() => {
1356
+ const apiTimeout = setTimeout(() => {
1329
1357
  const api = renderAPIRef.current;
1330
1358
  const hasSeek = typeof api?.seekTo === "function";
1331
1359
  const hasDur = typeof api?.getDuration === "function";
@@ -1333,24 +1361,36 @@ function useFrameCapture() {
1333
1361
  const hasPlayer = rootEl ? rootEl.querySelector(".doc-player") !== null : false;
1334
1362
  reject(
1335
1363
  new Error(
1336
- `Render API did not initialize within 15s. seekTo=${hasSeek}, getDuration=${hasDur}, player=${hasPlayer}, root=${!!rootEl}`
1364
+ `Render API did not initialize within ${RENDER_API_TIMEOUT_MS / 1e3}s. seekTo=${hasSeek}, getDuration=${hasDur}, player=${hasPlayer}, root=${!!rootEl}`
1337
1365
  )
1338
1366
  );
1339
- }, 15e3);
1367
+ }, RENDER_API_TIMEOUT_MS);
1340
1368
  void renderAPIReady.then(async (api) => {
1369
+ clearTimeout(apiTimeout);
1370
+ let stage = "resolving media URLs";
1371
+ const setupTimeout = setTimeout(() => {
1372
+ reject(
1373
+ new Error(
1374
+ `Frame capture setup did not finish within ${CAPTURE_SETUP_TIMEOUT_MS / 1e3}s while ${stage}.`
1375
+ )
1376
+ );
1377
+ }, CAPTURE_SETUP_TIMEOUT_MS);
1341
1378
  try {
1342
1379
  const captureRoot = container.querySelector("#squisq-capture-root");
1343
1380
  if (!(captureRoot instanceof HTMLElement)) {
1344
1381
  throw new Error("Capture root element not found after player initialization.");
1345
1382
  }
1346
- await waitForCaptureMediaResolutions(mediaResolutionTrackerRef.current);
1383
+ await waitForCaptureMediaResolutions(mediaResolutionTrackerRef.current, captureRoot);
1384
+ stage = "loading fonts and images";
1347
1385
  await waitForCaptureAssets(captureRoot, decodedImagesRef.current);
1386
+ stage = "opening video decoders";
1348
1387
  await videoFrames.attach(captureRoot);
1388
+ stage = "indexing video sources";
1349
1389
  await primeIndeterminateCaptureVideos(captureRoot, primedCaptureVideosRef.current);
1350
- clearTimeout(timeout);
1390
+ clearTimeout(setupTimeout);
1351
1391
  resolve(api.getDuration());
1352
1392
  } catch (assetError) {
1353
- clearTimeout(timeout);
1393
+ clearTimeout(setupTimeout);
1354
1394
  reject(assetError);
1355
1395
  }
1356
1396
  });
@@ -1399,7 +1439,7 @@ function useFrameCapture() {
1399
1439
  `Player committed ${renderedTime.toFixed(6)}s while capture requested ${time.toFixed(6)}s.`
1400
1440
  );
1401
1441
  }
1402
- await waitForCaptureMediaResolutions(mediaResolutionTrackerRef.current);
1442
+ await waitForCaptureMediaResolutions(mediaResolutionTrackerRef.current, root);
1403
1443
  await waitForCaptureAssets(root, decodedImagesRef.current);
1404
1444
  const compositePlan = resolveScheduledVideoCompositePlan(root, {
1405
1445
  includeFullFrameOverlays: true
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-3NJDHK62.js";
8
8
  import {
9
9
  useFrameCapture
10
- } from "./chunk-7ZMJ25DC.js";
10
+ } from "./chunk-2C4BEAWT.js";
11
11
 
12
12
  // src/CoverImageExportModal.tsx
13
13
  import { useCallback, useId, useRef, useState } from "react";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useVideoExport
3
- } from "./chunk-ED7QK6VC.js";
3
+ } from "./chunk-TUIVBMAK.js";
4
4
 
5
5
  // src/VideoExportModal.tsx
6
6
  import { useState, useCallback, useEffect, useId, useRef } from "react";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useFrameCapture
3
- } from "./chunk-7ZMJ25DC.js";
3
+ } from "./chunk-2C4BEAWT.js";
4
4
  import {
5
5
  EXPORT_AUDIO_CHANNELS,
6
6
  EXPORT_AUDIO_SAMPLE_RATE,
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  VideoExportButton,
3
3
  VideoExportModal
4
- } from "../chunk-BNUM3BXO.js";
4
+ } from "../chunk-RO52S6GO.js";
5
5
  import {
6
6
  CoverImageExportModal,
7
7
  coverImageFilename,
8
8
  validateCoverImageDimensions
9
- } from "../chunk-3EJKBFEK.js";
9
+ } from "../chunk-M5AK4MMV.js";
10
10
  import "../chunk-3NJDHK62.js";
11
- import "../chunk-ED7QK6VC.js";
12
- import "../chunk-7ZMJ25DC.js";
11
+ import "../chunk-TUIVBMAK.js";
12
+ import "../chunk-2C4BEAWT.js";
13
13
  import "../chunk-4GFZ3GUE.js";
14
14
  import "../chunk-GBIYYDMY.js";
15
15
  import "../chunk-5MFQMJ5Z.js";
@@ -2,9 +2,9 @@ import {
2
2
  CoverImageExportModal,
3
3
  coverImageFilename,
4
4
  validateCoverImageDimensions
5
- } from "../chunk-3EJKBFEK.js";
5
+ } from "../chunk-M5AK4MMV.js";
6
6
  import "../chunk-3NJDHK62.js";
7
- import "../chunk-7ZMJ25DC.js";
7
+ import "../chunk-2C4BEAWT.js";
8
8
  import "../chunk-4GFZ3GUE.js";
9
9
  export {
10
10
  CoverImageExportModal,
@@ -7,7 +7,7 @@ import {
7
7
  } from "../chunk-3NJDHK62.js";
8
8
  import {
9
9
  useFrameCapture
10
- } from "../chunk-7ZMJ25DC.js";
10
+ } from "../chunk-2C4BEAWT.js";
11
11
  import "../chunk-4GFZ3GUE.js";
12
12
 
13
13
  // src/DashboardImageExportModal.tsx
@@ -3,10 +3,10 @@ import {
3
3
  resolveVideoCoverFramePlan,
4
4
  resolveVideoExportCover,
5
5
  useVideoExport
6
- } from "../chunk-ED7QK6VC.js";
6
+ } from "../chunk-TUIVBMAK.js";
7
7
  import {
8
8
  useFrameCapture
9
- } from "../chunk-7ZMJ25DC.js";
9
+ } from "../chunk-2C4BEAWT.js";
10
10
  import "../chunk-4GFZ3GUE.js";
11
11
  import "../chunk-GBIYYDMY.js";
12
12
  import "../chunk-5MFQMJ5Z.js";
package/dist/index.js CHANGED
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  VideoExportButton,
3
3
  VideoExportModal
4
- } from "./chunk-BNUM3BXO.js";
4
+ } from "./chunk-RO52S6GO.js";
5
5
  import {
6
6
  CoverImageExportModal,
7
7
  coverImageFilename,
8
8
  validateCoverImageDimensions
9
- } from "./chunk-3EJKBFEK.js";
9
+ } from "./chunk-M5AK4MMV.js";
10
10
  import "./chunk-3NJDHK62.js";
11
11
  import {
12
12
  DEFAULT_VIDEO_COVER_PRE_ROLL_SECONDS,
13
13
  resolveVideoCoverFramePlan,
14
14
  resolveVideoExportCover,
15
15
  useVideoExport
16
- } from "./chunk-ED7QK6VC.js";
16
+ } from "./chunk-TUIVBMAK.js";
17
17
  import {
18
18
  useFrameCapture
19
- } from "./chunk-7ZMJ25DC.js";
19
+ } from "./chunk-2C4BEAWT.js";
20
20
  import "./chunk-4GFZ3GUE.js";
21
21
  import {
22
22
  createEncoder,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-video-react",
3
- "version": "2.4.12",
3
+ "version": "2.4.14",
4
4
  "description": "React components for browser-based MP4 and animated-GIF export of Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -77,9 +77,9 @@
77
77
  "react-dom": "^18.0.0 || ^19.0.0"
78
78
  },
79
79
  "dependencies": {
80
- "@bendyline/squisq": "2.11.8",
81
- "@bendyline/squisq-video": "2.3.11",
82
- "@bendyline/squisq-react": "2.11.8",
80
+ "@bendyline/squisq": "2.11.9",
81
+ "@bendyline/squisq-video": "2.3.12",
82
+ "@bendyline/squisq-react": "2.11.10",
83
83
  "@ffmpeg/core": "0.12.9",
84
84
  "@ffmpeg/ffmpeg": "0.12.15",
85
85
  "@ffmpeg/util": "0.12.2",