@bendyline/squisq-video-react 2.3.1 → 2.3.3
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.
- package/dist/{chunk-CRXJOZSH.js → chunk-FX5CHFGY.js} +1 -1
- package/dist/{chunk-YTEDBL6F.js → chunk-I5PNMCSZ.js} +132 -9
- package/dist/{chunk-U3RSDWQL.js → chunk-UXTYD6U5.js} +1 -1
- package/dist/{chunk-NTRCMTCF.js → chunk-YZN7URUB.js} +1 -1
- package/dist/components/index.js +4 -4
- package/dist/cover-image/index.js +2 -2
- package/dist/hooks/index.js +2 -2
- package/dist/index.js +4 -4
- package/package.json +4 -4
|
@@ -27,6 +27,8 @@ var CAPTURE_VIDEO_READINESS_POLL_MS = 16;
|
|
|
27
27
|
var CAPTURE_VIDEO_END_PROBE_TIME = 1e101;
|
|
28
28
|
var SCHEDULED_MEDIA_SELECTOR = ".doc-player__media-clips";
|
|
29
29
|
var SCHEDULED_VIDEO_SELECTOR = `${SCHEDULED_MEDIA_SELECTOR} video[data-clip-id]`;
|
|
30
|
+
var CAPTION_OVERLAY_SELECTOR = ".caption-overlay, .social-caption-overlay";
|
|
31
|
+
var COVER_BLOCK_SELECTOR = ".doc-player__block--cover";
|
|
30
32
|
async function waitForImageDecode(image) {
|
|
31
33
|
const src = image.currentSrc || image.src;
|
|
32
34
|
if (!src) return;
|
|
@@ -144,6 +146,7 @@ async function primeIndeterminateCaptureVideos(captureRoot, primedVideos = /* @_
|
|
|
144
146
|
return;
|
|
145
147
|
}
|
|
146
148
|
if (Number.isFinite(video.duration)) {
|
|
149
|
+
video.dataset.captureSequential = "true";
|
|
147
150
|
primedVideos.add(video);
|
|
148
151
|
return;
|
|
149
152
|
}
|
|
@@ -581,7 +584,7 @@ function scheduledVideoIsVisual(video) {
|
|
|
581
584
|
function scheduledVideoPresentation(video) {
|
|
582
585
|
return video.closest(SCHEDULED_MEDIA_SELECTOR)?.dataset.presentation;
|
|
583
586
|
}
|
|
584
|
-
function planScheduledVideoComposite(captureRoot) {
|
|
587
|
+
function planScheduledVideoComposite(captureRoot, options = {}) {
|
|
585
588
|
const activeVideos = Array.from(
|
|
586
589
|
captureRoot.querySelectorAll(SCHEDULED_VIDEO_SELECTOR)
|
|
587
590
|
).filter(scheduledVideoIsVisual);
|
|
@@ -592,21 +595,60 @@ function planScheduledVideoComposite(captureRoot) {
|
|
|
592
595
|
const presentation = scheduledVideoPresentation(video);
|
|
593
596
|
if (presentation === "background") underlays.push(video);
|
|
594
597
|
else if (presentation === "picture-in-picture") overlays.push(video);
|
|
595
|
-
else
|
|
598
|
+
else if (presentation === "full-frame" && options.includeFullFrameOverlays) {
|
|
599
|
+
overlays.push(video);
|
|
600
|
+
} else return null;
|
|
596
601
|
}
|
|
597
602
|
return { underlays, overlays };
|
|
598
603
|
}
|
|
599
|
-
function
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
)
|
|
603
|
-
|
|
604
|
-
|
|
604
|
+
function resolveScheduledVideoCompositePlan(captureRoot, options = {}) {
|
|
605
|
+
const plan = planScheduledVideoComposite(captureRoot, options);
|
|
606
|
+
if (!plan) return null;
|
|
607
|
+
if (captureRoot.querySelector(COVER_BLOCK_SELECTOR)) {
|
|
608
|
+
return { underlays: [], overlays: [] };
|
|
609
|
+
}
|
|
610
|
+
return plan;
|
|
611
|
+
}
|
|
612
|
+
function clearAncestorBackdropsFor(clonedRoot, targets) {
|
|
613
|
+
for (const target of targets) {
|
|
614
|
+
for (let element = target.parentElement; element; element = element === clonedRoot ? null : element.parentElement) {
|
|
605
615
|
element.style.backgroundColor = "transparent";
|
|
606
616
|
element.style.backgroundImage = "none";
|
|
607
617
|
}
|
|
608
618
|
}
|
|
609
619
|
}
|
|
620
|
+
function clearScheduledUnderlayBackdrops(clonedRoot) {
|
|
621
|
+
clearAncestorBackdropsFor(
|
|
622
|
+
clonedRoot,
|
|
623
|
+
Array.from(
|
|
624
|
+
clonedRoot.querySelectorAll(
|
|
625
|
+
`${SCHEDULED_MEDIA_SELECTOR}[data-presentation="background"]`
|
|
626
|
+
)
|
|
627
|
+
)
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
function getCaptureCaptionOverlays(captureRoot) {
|
|
631
|
+
return Array.from(captureRoot.querySelectorAll(CAPTION_OVERLAY_SELECTOR));
|
|
632
|
+
}
|
|
633
|
+
function clearCaptionOverlayBackdrops(clonedRoot) {
|
|
634
|
+
clearAncestorBackdropsFor(clonedRoot, getCaptureCaptionOverlays(clonedRoot));
|
|
635
|
+
}
|
|
636
|
+
function shouldIgnoreCaptureCaptionSibling(element, captureRoot, captionOverlays) {
|
|
637
|
+
if (shouldIgnoreCaptureSibling(element, captureRoot)) return true;
|
|
638
|
+
if (element === captureRoot || element.contains(captureRoot)) return false;
|
|
639
|
+
if (!captureRoot.contains(element)) return false;
|
|
640
|
+
return !captionOverlays.some(
|
|
641
|
+
(overlay) => overlay === element || overlay.contains(element) || element.contains(overlay)
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
function getCaptionLayerStateKey(captionOverlays, ownerDocument, width, height) {
|
|
645
|
+
return JSON.stringify({
|
|
646
|
+
markup: captionOverlays.map((overlay) => overlay.outerHTML),
|
|
647
|
+
fontStatus: ownerDocument.fonts?.status ?? "unsupported",
|
|
648
|
+
width,
|
|
649
|
+
height
|
|
650
|
+
});
|
|
651
|
+
}
|
|
610
652
|
function cssPixelValue(value) {
|
|
611
653
|
const parsed = Number.parseFloat(value);
|
|
612
654
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
@@ -773,6 +815,9 @@ function useFrameCapture() {
|
|
|
773
815
|
const captureSvgRasterCacheRef = useRef(createCaptureSvgRasterCache());
|
|
774
816
|
const primedCaptureVideosRef = useRef(/* @__PURE__ */ new WeakSet());
|
|
775
817
|
const dimensionsRef = useRef({ width: 1920, height: 1080 });
|
|
818
|
+
const captionLayerCanvasRef = useRef(null);
|
|
819
|
+
const captionLayerKeyRef = useRef(null);
|
|
820
|
+
const captionLayerHasContentRef = useRef(false);
|
|
776
821
|
const init = useCallback(
|
|
777
822
|
async (doc, renderOptions, captionMode) => {
|
|
778
823
|
if (rootRef.current || containerRef.current || mediaProviderRef.current || captureCanvasRef.current || captureBaseCanvasRef.current) {
|
|
@@ -782,6 +827,7 @@ function useFrameCapture() {
|
|
|
782
827
|
const oldOwnsMediaProvider = ownsMediaProviderRef.current;
|
|
783
828
|
const oldCaptureCanvas = captureCanvasRef.current;
|
|
784
829
|
const oldCaptureBaseCanvas = captureBaseCanvasRef.current;
|
|
830
|
+
const oldCaptionLayerCanvas = captionLayerCanvasRef.current;
|
|
785
831
|
rootRef.current = null;
|
|
786
832
|
containerRef.current = null;
|
|
787
833
|
renderAPIRef.current = null;
|
|
@@ -789,6 +835,9 @@ function useFrameCapture() {
|
|
|
789
835
|
ownsMediaProviderRef.current = false;
|
|
790
836
|
captureCanvasRef.current = null;
|
|
791
837
|
captureBaseCanvasRef.current = null;
|
|
838
|
+
captionLayerCanvasRef.current = null;
|
|
839
|
+
captionLayerKeyRef.current = null;
|
|
840
|
+
captionLayerHasContentRef.current = false;
|
|
792
841
|
lastVisualStateKeyRef.current = null;
|
|
793
842
|
hasCapturedFrameRef.current = false;
|
|
794
843
|
decodedImagesRef.current = /* @__PURE__ */ new WeakSet();
|
|
@@ -809,6 +858,10 @@ function useFrameCapture() {
|
|
|
809
858
|
oldCaptureBaseCanvas.width = 0;
|
|
810
859
|
oldCaptureBaseCanvas.height = 0;
|
|
811
860
|
}
|
|
861
|
+
if (oldCaptionLayerCanvas) {
|
|
862
|
+
oldCaptionLayerCanvas.width = 0;
|
|
863
|
+
oldCaptionLayerCanvas.height = 0;
|
|
864
|
+
}
|
|
812
865
|
resolve();
|
|
813
866
|
}, 0);
|
|
814
867
|
});
|
|
@@ -829,6 +882,8 @@ function useFrameCapture() {
|
|
|
829
882
|
captureBaseCanvas.style.width = `${width}px`;
|
|
830
883
|
captureBaseCanvas.style.height = `${height}px`;
|
|
831
884
|
captureBaseCanvasRef.current = captureBaseCanvas;
|
|
885
|
+
captionLayerKeyRef.current = null;
|
|
886
|
+
captionLayerHasContentRef.current = false;
|
|
832
887
|
lastVisualStateKeyRef.current = null;
|
|
833
888
|
hasCapturedFrameRef.current = false;
|
|
834
889
|
decodedImagesRef.current = /* @__PURE__ */ new WeakSet();
|
|
@@ -956,7 +1011,9 @@ function useFrameCapture() {
|
|
|
956
1011
|
);
|
|
957
1012
|
}
|
|
958
1013
|
await waitForCaptureAssets(root, decodedImagesRef.current);
|
|
959
|
-
const compositePlan =
|
|
1014
|
+
const compositePlan = resolveScheduledVideoCompositePlan(root, {
|
|
1015
|
+
includeFullFrameOverlays: true
|
|
1016
|
+
});
|
|
960
1017
|
const hasUnderlays = compositePlan !== null && compositePlan.underlays.length > 0;
|
|
961
1018
|
const rasterMode = compositePlan ? hasUnderlays ? "base-underlay" : "base" : "full";
|
|
962
1019
|
const visualStateKey = options.reuseIfUnchanged ? `${rasterMode}:${getFrameVisualStateKey(root, time, {
|
|
@@ -990,6 +1047,7 @@ function useFrameCapture() {
|
|
|
990
1047
|
if (compositePlan) {
|
|
991
1048
|
if (hasUnderlays) clearScheduledUnderlayBackdrops(clonedRoot);
|
|
992
1049
|
clonedRoot.querySelectorAll(SCHEDULED_MEDIA_SELECTOR).forEach((element) => element.remove());
|
|
1050
|
+
clonedRoot.querySelectorAll(CAPTION_OVERLAY_SELECTOR).forEach((element) => element.remove());
|
|
993
1051
|
}
|
|
994
1052
|
transientCloneCanvases.push(...prepareScheduledVideoClones(root, clonedRoot));
|
|
995
1053
|
await rasterizeCaptureSvgClones(
|
|
@@ -1021,6 +1079,64 @@ function useFrameCapture() {
|
|
|
1021
1079
|
drawScheduledVideosOnto(captureCanvas, root, compositePlan.underlays);
|
|
1022
1080
|
outputContext.drawImage(captureBaseCanvas, 0, 0);
|
|
1023
1081
|
drawScheduledVideosOnto(captureCanvas, root, compositePlan.overlays);
|
|
1082
|
+
const captionOverlays = getCaptureCaptionOverlays(root);
|
|
1083
|
+
if (captionOverlays.length > 0) {
|
|
1084
|
+
const hasCaptionText = captionOverlays.some(
|
|
1085
|
+
(overlay) => (overlay.textContent ?? "").trim().length > 0
|
|
1086
|
+
);
|
|
1087
|
+
const captionKey = getCaptionLayerStateKey(
|
|
1088
|
+
captionOverlays,
|
|
1089
|
+
root.ownerDocument,
|
|
1090
|
+
width,
|
|
1091
|
+
height
|
|
1092
|
+
);
|
|
1093
|
+
if (captionLayerKeyRef.current !== captionKey) {
|
|
1094
|
+
if (hasCaptionText) {
|
|
1095
|
+
let captionCanvas = captionLayerCanvasRef.current;
|
|
1096
|
+
if (!captionCanvas) {
|
|
1097
|
+
captionCanvas = document.createElement("canvas");
|
|
1098
|
+
captionLayerCanvasRef.current = captionCanvas;
|
|
1099
|
+
}
|
|
1100
|
+
if (captionCanvas.width !== width || captionCanvas.height !== height) {
|
|
1101
|
+
captionCanvas.width = width;
|
|
1102
|
+
captionCanvas.height = height;
|
|
1103
|
+
}
|
|
1104
|
+
const captionContext = captionCanvas.getContext("2d");
|
|
1105
|
+
if (!captionContext) {
|
|
1106
|
+
throw new Error("Could not create the caption layer canvas context");
|
|
1107
|
+
}
|
|
1108
|
+
captionContext.setTransform(1, 0, 0, 1, 0, 0);
|
|
1109
|
+
captionContext.clearRect(0, 0, width, height);
|
|
1110
|
+
await html2canvas(root, {
|
|
1111
|
+
canvas: captionCanvas,
|
|
1112
|
+
width,
|
|
1113
|
+
height,
|
|
1114
|
+
scale: 1,
|
|
1115
|
+
useCORS: true,
|
|
1116
|
+
allowTaint: true,
|
|
1117
|
+
backgroundColor: null,
|
|
1118
|
+
logging: false,
|
|
1119
|
+
onclone: (_clonedDocument, clonedRoot) => {
|
|
1120
|
+
clearCaptionOverlayBackdrops(clonedRoot);
|
|
1121
|
+
},
|
|
1122
|
+
ignoreElements: (element) => shouldIgnoreCaptureCaptionSibling(element, root, captionOverlays)
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
captionLayerKeyRef.current = captionKey;
|
|
1126
|
+
captionLayerHasContentRef.current = hasCaptionText;
|
|
1127
|
+
}
|
|
1128
|
+
if (captionLayerHasContentRef.current && captionLayerCanvasRef.current) {
|
|
1129
|
+
const overlayOpacity = Number.parseFloat(
|
|
1130
|
+
root.ownerDocument.defaultView?.getComputedStyle(captionOverlays[0]).opacity ?? "1"
|
|
1131
|
+
);
|
|
1132
|
+
if (!Number.isFinite(overlayOpacity) || overlayOpacity > 0) {
|
|
1133
|
+
outputContext.save();
|
|
1134
|
+
outputContext.globalAlpha = Number.isFinite(overlayOpacity) ? overlayOpacity : 1;
|
|
1135
|
+
outputContext.drawImage(captionLayerCanvasRef.current, 0, 0);
|
|
1136
|
+
outputContext.restore();
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1024
1140
|
}
|
|
1025
1141
|
return captureCanvas;
|
|
1026
1142
|
},
|
|
@@ -1055,6 +1171,13 @@ function useFrameCapture() {
|
|
|
1055
1171
|
captureBaseCanvasRef.current.height = 0;
|
|
1056
1172
|
captureBaseCanvasRef.current = null;
|
|
1057
1173
|
}
|
|
1174
|
+
if (captionLayerCanvasRef.current) {
|
|
1175
|
+
captionLayerCanvasRef.current.width = 0;
|
|
1176
|
+
captionLayerCanvasRef.current.height = 0;
|
|
1177
|
+
captionLayerCanvasRef.current = null;
|
|
1178
|
+
}
|
|
1179
|
+
captionLayerKeyRef.current = null;
|
|
1180
|
+
captionLayerHasContentRef.current = false;
|
|
1058
1181
|
lastVisualStateKeyRef.current = null;
|
|
1059
1182
|
hasCapturedFrameRef.current = false;
|
|
1060
1183
|
decodedImagesRef.current = /* @__PURE__ */ new WeakSet();
|
package/dist/components/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VideoExportButton,
|
|
3
3
|
VideoExportModal
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-FX5CHFGY.js";
|
|
5
5
|
import {
|
|
6
6
|
CoverImageExportModal,
|
|
7
7
|
coverImageFilename,
|
|
8
8
|
validateCoverImageDimensions
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-YZN7URUB.js";
|
|
10
|
+
import "../chunk-UXTYD6U5.js";
|
|
11
|
+
import "../chunk-I5PNMCSZ.js";
|
|
12
12
|
import "../chunk-32QCPFXE.js";
|
|
13
13
|
import "../chunk-5MFQMJ5Z.js";
|
|
14
14
|
export {
|
|
@@ -2,8 +2,8 @@ import {
|
|
|
2
2
|
CoverImageExportModal,
|
|
3
3
|
coverImageFilename,
|
|
4
4
|
validateCoverImageDimensions
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import "../chunk-
|
|
5
|
+
} from "../chunk-YZN7URUB.js";
|
|
6
|
+
import "../chunk-I5PNMCSZ.js";
|
|
7
7
|
export {
|
|
8
8
|
CoverImageExportModal,
|
|
9
9
|
coverImageFilename,
|
package/dist/hooks/index.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
resolveVideoCoverFramePlan,
|
|
4
4
|
resolveVideoExportCover,
|
|
5
5
|
useVideoExport
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-UXTYD6U5.js";
|
|
7
7
|
import {
|
|
8
8
|
useFrameCapture
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-I5PNMCSZ.js";
|
|
10
10
|
import "../chunk-32QCPFXE.js";
|
|
11
11
|
import "../chunk-5MFQMJ5Z.js";
|
|
12
12
|
export {
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VideoExportButton,
|
|
3
3
|
VideoExportModal
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FX5CHFGY.js";
|
|
5
5
|
import {
|
|
6
6
|
CoverImageExportModal,
|
|
7
7
|
coverImageFilename,
|
|
8
8
|
validateCoverImageDimensions
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-YZN7URUB.js";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_VIDEO_COVER_PRE_ROLL_SECONDS,
|
|
12
12
|
resolveVideoCoverFramePlan,
|
|
13
13
|
resolveVideoExportCover,
|
|
14
14
|
useVideoExport
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-UXTYD6U5.js";
|
|
16
16
|
import {
|
|
17
17
|
useFrameCapture
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-I5PNMCSZ.js";
|
|
19
19
|
import {
|
|
20
20
|
createEncoder,
|
|
21
21
|
supportsWebCodecs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-video-react",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "React components for browser-based MP4 and animated-GIF export of Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -69,9 +69,9 @@
|
|
|
69
69
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@bendyline/squisq": "2.
|
|
73
|
-
"@bendyline/squisq-video": "2.2.
|
|
74
|
-
"@bendyline/squisq-react": "2.
|
|
72
|
+
"@bendyline/squisq": "2.7.1",
|
|
73
|
+
"@bendyline/squisq-video": "2.2.12",
|
|
74
|
+
"@bendyline/squisq-react": "2.7.1",
|
|
75
75
|
"@ffmpeg/core": "0.12.9",
|
|
76
76
|
"@ffmpeg/ffmpeg": "0.12.15",
|
|
77
77
|
"@ffmpeg/util": "0.12.2",
|