@geekapps/silo-elements-nextjs 0.2.62 → 0.2.63
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/VideoPlayer.d.ts +17 -2
- package/dist/VideoPlayer.js +150 -42
- package/dist/VideoPlayer.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +150 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/VideoPlayer.d.ts
CHANGED
|
@@ -18,7 +18,10 @@ type CaptionsProps = {
|
|
|
18
18
|
}>;
|
|
19
19
|
};
|
|
20
20
|
type ThumbnailProps = {
|
|
21
|
-
|
|
21
|
+
/** Single URL (legacy). Use urls for adaptive resolution. */
|
|
22
|
+
src?: string;
|
|
23
|
+
/** Map of width label to URL: { "320w": "...", "640w": "...", "1280w": "...", "1920w": "..." } */
|
|
24
|
+
urls?: Record<string, string>;
|
|
22
25
|
};
|
|
23
26
|
type StoryboardFrameProps = {
|
|
24
27
|
start: number;
|
|
@@ -29,11 +32,23 @@ type StoryboardFrameProps = {
|
|
|
29
32
|
w?: number;
|
|
30
33
|
h?: number;
|
|
31
34
|
};
|
|
35
|
+
type StoryboardResolution = {
|
|
36
|
+
/** VTT content with sprite URLs already resolved (no temp S3 upload needed) */
|
|
37
|
+
vttText?: string | null;
|
|
38
|
+
spriteUrl: string;
|
|
39
|
+
tileW: number;
|
|
40
|
+
tileH: number;
|
|
41
|
+
cols: number;
|
|
42
|
+
rows?: number | null;
|
|
43
|
+
};
|
|
32
44
|
type StoryboardProps = {
|
|
45
|
+
/** Legacy: single VTT URL */
|
|
33
46
|
src?: string;
|
|
34
47
|
fallbackImage?: string;
|
|
35
48
|
width?: number;
|
|
36
49
|
height?: number;
|
|
50
|
+
/** Multi-resolution storyboards from post-worker: { "180p": {...}, "480p": {...} } */
|
|
51
|
+
resolutions?: Record<string, StoryboardResolution>;
|
|
37
52
|
children?: ReactNode;
|
|
38
53
|
};
|
|
39
54
|
type ChaptersProps = {
|
|
@@ -114,4 +129,4 @@ declare namespace Rating {
|
|
|
114
129
|
declare function Video({ title, description, children, className, autoHideControls, defaultVolume, maxHeight, fixedHeight, }: VideoProps): react__default.JSX.Element;
|
|
115
130
|
declare const VideoPlayer: typeof Video;
|
|
116
131
|
|
|
117
|
-
export { AgeRating, type AgeRatingProps, Captions, type CaptionsProps, Chapters, type ChaptersProps, Rating, type RatingProps, Source, type SourceProps, Storyboard, StoryboardFrame, type StoryboardFrameProps, type StoryboardProps, type ThumbnailProps, Video, VideoPlayer, type VideoSourceType, VideoThumbnail };
|
|
132
|
+
export { AgeRating, type AgeRatingProps, Captions, type CaptionsProps, Chapters, type ChaptersProps, Rating, type RatingProps, Source, type SourceProps, Storyboard, StoryboardFrame, type StoryboardFrameProps, type StoryboardProps, type StoryboardResolution, type ThumbnailProps, Video, VideoPlayer, type VideoSourceType, VideoThumbnail };
|
package/dist/VideoPlayer.js
CHANGED
|
@@ -107,13 +107,6 @@ function Video({
|
|
|
107
107
|
cancelled = true;
|
|
108
108
|
};
|
|
109
109
|
}, [parsed.chaptersSrc, parsed.chaptersData]);
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
if (!parsed.thumbnailSrc) {
|
|
112
|
-
setPoster(void 0);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
setPoster(parsed.thumbnailSrc);
|
|
116
|
-
}, [parsed.thumbnailSrc]);
|
|
117
110
|
const initialSourceIndex = useMemo(() => {
|
|
118
111
|
const index = parsed.sources.findIndex((source) => source.default);
|
|
119
112
|
return index >= 0 ? index : 0;
|
|
@@ -150,9 +143,10 @@ function Video({
|
|
|
150
143
|
const [activeCue, setActiveCue] = useState(null);
|
|
151
144
|
const [storyboardSheetSize, setStoryboardSheetSize] = useState(null);
|
|
152
145
|
const storyboardSheetUrlRef = useRef("");
|
|
153
|
-
const [storyboardCues, setStoryboardCues] = useState(
|
|
154
|
-
|
|
155
|
-
);
|
|
146
|
+
const [storyboardCues, setStoryboardCues] = useState([]);
|
|
147
|
+
const storyboardCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
148
|
+
const [activeStoryboardRes, setActiveStoryboardRes] = useState(null);
|
|
149
|
+
const preloadedSpritesRef = useRef(/* @__PURE__ */ new Set());
|
|
156
150
|
const [ratingCounts, setRatingCounts] = useState(
|
|
157
151
|
() => ({ LOVE: parsed.rating?.counts?.LOVE ?? 0, LIKE: parsed.rating?.counts?.LIKE ?? 0, DISLIKE: parsed.rating?.counts?.DISLIKE ?? 0 })
|
|
158
152
|
);
|
|
@@ -195,6 +189,24 @@ function Video({
|
|
|
195
189
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
196
190
|
const [playerHeight, setPlayerHeight] = useState(0);
|
|
197
191
|
const [playerWidth, setPlayerWidth] = useState(0);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
const urls = parsed.thumbnailUrls;
|
|
194
|
+
if (!urls || Object.keys(urls).length === 0) {
|
|
195
|
+
setPoster(parsed.thumbnailSrc ?? void 0);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const sorted = Object.entries(urls).map(([label, url]) => ({ px: parseInt(label.replace("w", ""), 10) || 0, url })).filter((e) => e.px > 0).sort((a, b) => a.px - b.px);
|
|
199
|
+
if (sorted.length === 0) {
|
|
200
|
+
setPoster(parsed.thumbnailSrc ?? void 0);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio ?? 1 : 1;
|
|
204
|
+
const conn = typeof navigator !== "undefined" ? navigator.connection : null;
|
|
205
|
+
const connFactor = conn?.effectiveType === "4g" ? 1 : conn?.effectiveType === "3g" ? 0.5 : 0.25;
|
|
206
|
+
const targetPx = (playerWidth || 640) * dpr * (conn ? connFactor : 1);
|
|
207
|
+
const best = sorted.find((e) => e.px >= targetPx) ?? sorted[sorted.length - 1];
|
|
208
|
+
setPoster(best.url);
|
|
209
|
+
}, [parsed.thumbnailUrls, parsed.thumbnailSrc, playerWidth]);
|
|
198
210
|
const activeSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
|
|
199
211
|
const progressPercent = duration ? currentTime / duration * 100 : 0;
|
|
200
212
|
const bufferedPercent = duration ? bufferedTime / duration * 100 : 0;
|
|
@@ -443,40 +455,71 @@ function Video({
|
|
|
443
455
|
async function loadStoryboard() {
|
|
444
456
|
if (!parsed.storyboard) {
|
|
445
457
|
setStoryboardCues([]);
|
|
458
|
+
setActiveStoryboardRes(null);
|
|
446
459
|
return;
|
|
447
460
|
}
|
|
448
461
|
if (parsed.storyboard.frames.length > 0) {
|
|
449
462
|
setStoryboardCues(parsed.storyboard.frames);
|
|
450
463
|
return;
|
|
451
464
|
}
|
|
465
|
+
const resolutions = parsed.storyboard.resolutions;
|
|
466
|
+
if (resolutions && Object.keys(resolutions).length > 0) {
|
|
467
|
+
storyboardCacheRef.current.clear();
|
|
468
|
+
preloadedSpritesRef.current.clear();
|
|
469
|
+
const RES_ORDER = ["180p", "240p", "360p", "480p", "default"];
|
|
470
|
+
const keys = Object.keys(resolutions).sort((a, b) => {
|
|
471
|
+
const ai = RES_ORDER.indexOf(a);
|
|
472
|
+
const bi = RES_ORDER.indexOf(b);
|
|
473
|
+
return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
|
|
474
|
+
});
|
|
475
|
+
const loadRes = async (key) => {
|
|
476
|
+
if (cancelled) return;
|
|
477
|
+
const res = resolutions[key];
|
|
478
|
+
if (!res) return;
|
|
479
|
+
let cues = [];
|
|
480
|
+
if (res.vttText) {
|
|
481
|
+
cues = parseStoryboardVtt(res.vttText, res.spriteUrl);
|
|
482
|
+
} else {
|
|
483
|
+
cues = [{ start: 0, end: Number.MAX_SAFE_INTEGER, image: res.spriteUrl, w: res.tileW, h: res.tileH }];
|
|
484
|
+
}
|
|
485
|
+
storyboardCacheRef.current.set(key, cues);
|
|
486
|
+
if (!preloadedSpritesRef.current.has(res.spriteUrl)) {
|
|
487
|
+
preloadedSpritesRef.current.add(res.spriteUrl);
|
|
488
|
+
const img = new window.Image();
|
|
489
|
+
img.src = res.spriteUrl;
|
|
490
|
+
}
|
|
491
|
+
return cues;
|
|
492
|
+
};
|
|
493
|
+
const firstKey = keys[0];
|
|
494
|
+
const firstCues = await loadRes(firstKey);
|
|
495
|
+
if (!cancelled && firstCues) {
|
|
496
|
+
setStoryboardCues(firstCues);
|
|
497
|
+
setActiveStoryboardRes(firstKey);
|
|
498
|
+
}
|
|
499
|
+
for (const key of keys.slice(1)) {
|
|
500
|
+
await loadRes(key);
|
|
501
|
+
}
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
452
504
|
if (!parsed.storyboard.src) {
|
|
453
505
|
setStoryboardCues([]);
|
|
454
506
|
return;
|
|
455
507
|
}
|
|
456
508
|
try {
|
|
457
509
|
const response = await fetch(parsed.storyboard.src);
|
|
458
|
-
if (!response.ok)
|
|
459
|
-
throw new Error("Storyboard not found");
|
|
460
|
-
}
|
|
510
|
+
if (!response.ok) throw new Error("Storyboard not found");
|
|
461
511
|
const text = await response.text();
|
|
462
|
-
const cues = parseStoryboardVtt(
|
|
463
|
-
|
|
464
|
-
new URL(parsed.storyboard.src, window.location.href).href
|
|
465
|
-
);
|
|
466
|
-
if (!cancelled) {
|
|
467
|
-
setStoryboardCues(cues);
|
|
468
|
-
}
|
|
512
|
+
const cues = parseStoryboardVtt(text, new URL(parsed.storyboard.src, window.location.href).href);
|
|
513
|
+
if (!cancelled) setStoryboardCues(cues);
|
|
469
514
|
} catch {
|
|
470
515
|
if (!cancelled && parsed.storyboard.fallbackImage) {
|
|
471
|
-
setStoryboardCues([
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
]);
|
|
516
|
+
setStoryboardCues([{
|
|
517
|
+
start: 0,
|
|
518
|
+
end: Number.MAX_SAFE_INTEGER,
|
|
519
|
+
image: parsed.storyboard.fallbackImage,
|
|
520
|
+
w: parsed.storyboard.width ?? 160,
|
|
521
|
+
h: parsed.storyboard.height ?? 90
|
|
522
|
+
}]);
|
|
480
523
|
}
|
|
481
524
|
}
|
|
482
525
|
}
|
|
@@ -787,6 +830,63 @@ function Video({
|
|
|
787
830
|
},
|
|
788
831
|
[duration]
|
|
789
832
|
);
|
|
833
|
+
const upgradeStoryboardResolution = useCallback((hoverTime) => {
|
|
834
|
+
const resolutions = parsed.storyboard?.resolutions;
|
|
835
|
+
if (!resolutions || storyboardCacheRef.current.size === 0) return;
|
|
836
|
+
const RES_ORDER = ["480p", "360p", "240p", "180p", "default"];
|
|
837
|
+
const conn = typeof navigator !== "undefined" ? navigator.connection : null;
|
|
838
|
+
const maxRes = conn?.effectiveType === "2g" ? "180p" : conn?.effectiveType === "3g" ? "240p" : "480p";
|
|
839
|
+
const maxIdx = RES_ORDER.indexOf(maxRes);
|
|
840
|
+
for (let i = 0; i <= maxIdx; i++) {
|
|
841
|
+
const key = RES_ORDER[i];
|
|
842
|
+
if (storyboardCacheRef.current.has(key)) {
|
|
843
|
+
if (activeStoryboardRes !== key) {
|
|
844
|
+
setStoryboardCues(storyboardCacheRef.current.get(key));
|
|
845
|
+
setActiveStoryboardRes(key);
|
|
846
|
+
const spriteUrl = resolutions[key]?.spriteUrl;
|
|
847
|
+
if (spriteUrl && !preloadedSpritesRef.current.has(spriteUrl)) {
|
|
848
|
+
preloadedSpritesRef.current.add(spriteUrl);
|
|
849
|
+
const img = new window.Image();
|
|
850
|
+
img.src = spriteUrl;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
const bestKey = [...storyboardCacheRef.current.keys()].sort((a, b) => {
|
|
857
|
+
const order = ["480p", "360p", "240p", "180p", "default"];
|
|
858
|
+
return order.indexOf(a) - order.indexOf(b);
|
|
859
|
+
})[0];
|
|
860
|
+
if (bestKey) {
|
|
861
|
+
const cues = storyboardCacheRef.current.get(bestKey) ?? [];
|
|
862
|
+
const nearIdx = cues.findIndex((c) => c.start > hoverTime);
|
|
863
|
+
const window2 = cues.slice(Math.max(0, nearIdx - 2), nearIdx + 3);
|
|
864
|
+
for (const c of window2) {
|
|
865
|
+
if (!preloadedSpritesRef.current.has(c.image)) {
|
|
866
|
+
preloadedSpritesRef.current.add(c.image);
|
|
867
|
+
const img = new window.Image();
|
|
868
|
+
img.src = c.image;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}, [parsed.storyboard?.resolutions, activeStoryboardRes]);
|
|
873
|
+
useEffect(() => {
|
|
874
|
+
const resolutions = parsed.storyboard?.resolutions;
|
|
875
|
+
if (!resolutions || storyboardCacheRef.current.size === 0 || !duration) return;
|
|
876
|
+
const RES_ORDER = ["180p", "240p", "360p", "480p", "default"];
|
|
877
|
+
const lowestKey = RES_ORDER.find((k) => storyboardCacheRef.current.has(k));
|
|
878
|
+
if (!lowestKey) return;
|
|
879
|
+
const cues = storyboardCacheRef.current.get(lowestKey) ?? [];
|
|
880
|
+
const playheadIdx = cues.findIndex((c) => c.start > currentTime);
|
|
881
|
+
const nearby = cues.slice(Math.max(0, playheadIdx - 2), playheadIdx + 6);
|
|
882
|
+
for (const c of nearby) {
|
|
883
|
+
if (!preloadedSpritesRef.current.has(c.image)) {
|
|
884
|
+
preloadedSpritesRef.current.add(c.image);
|
|
885
|
+
const img = new window.Image();
|
|
886
|
+
img.src = c.image;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}, [Math.floor(currentTime / 10), parsed.storyboard?.resolutions, duration]);
|
|
790
890
|
const updatePreview = useCallback(
|
|
791
891
|
(clientX) => {
|
|
792
892
|
const progress = progressRef.current;
|
|
@@ -797,6 +897,7 @@ function Video({
|
|
|
797
897
|
const rect = progress.getBoundingClientRect();
|
|
798
898
|
const x = Math.max(0, Math.min(clientX - rect.left, rect.width));
|
|
799
899
|
const time = x / rect.width * duration;
|
|
900
|
+
upgradeStoryboardResolution(time);
|
|
800
901
|
const cue = findStoryboardCue(storyboardCues, time);
|
|
801
902
|
if (!cue) {
|
|
802
903
|
setPreview(null);
|
|
@@ -808,7 +909,7 @@ function Video({
|
|
|
808
909
|
left: Math.max(80, Math.min(x, rect.width - 80))
|
|
809
910
|
});
|
|
810
911
|
},
|
|
811
|
-
[duration, storyboardCues]
|
|
912
|
+
[duration, storyboardCues, upgradeStoryboardResolution]
|
|
812
913
|
);
|
|
813
914
|
const handleProgressPointerMove = useCallback(
|
|
814
915
|
(event) => {
|
|
@@ -822,8 +923,19 @@ function Video({
|
|
|
822
923
|
}, []);
|
|
823
924
|
const handleProgressPointerLeave = useCallback(() => {
|
|
824
925
|
setIsHoveringProgress(false);
|
|
825
|
-
if (!isDragging)
|
|
826
|
-
|
|
926
|
+
if (!isDragging) {
|
|
927
|
+
setPreview(null);
|
|
928
|
+
const resolutions = parsed.storyboard?.resolutions;
|
|
929
|
+
if (resolutions && storyboardCacheRef.current.size > 0) {
|
|
930
|
+
const RES_ORDER = ["180p", "240p", "360p", "480p", "default"];
|
|
931
|
+
const lowestKey = RES_ORDER.find((k) => storyboardCacheRef.current.has(k));
|
|
932
|
+
if (lowestKey && activeStoryboardRes !== lowestKey) {
|
|
933
|
+
setStoryboardCues(storyboardCacheRef.current.get(lowestKey));
|
|
934
|
+
setActiveStoryboardRes(lowestKey);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}, [isDragging, parsed.storyboard?.resolutions, activeStoryboardRes]);
|
|
827
939
|
const handleProgressPointerDown = useCallback(
|
|
828
940
|
(event) => {
|
|
829
941
|
const progress = progressRef.current;
|
|
@@ -1972,7 +2084,8 @@ function parseVideoChildren(children) {
|
|
|
1972
2084
|
}
|
|
1973
2085
|
if (child.type === VideoThumbnail || name === "SiloVideoThumbnail") {
|
|
1974
2086
|
const element = child;
|
|
1975
|
-
parsed.
|
|
2087
|
+
if (element.props.urls) parsed.thumbnailUrls = element.props.urls;
|
|
2088
|
+
if (element.props.src) parsed.thumbnailSrc = element.props.src;
|
|
1976
2089
|
}
|
|
1977
2090
|
if (child.type === AgeRating || name === "SiloAgeRating") {
|
|
1978
2091
|
const element = child;
|
|
@@ -1995,15 +2108,10 @@ function parseVideoChildren(children) {
|
|
|
1995
2108
|
});
|
|
1996
2109
|
parsed.storyboard = {
|
|
1997
2110
|
...element.props.src !== void 0 && { src: element.props.src },
|
|
1998
|
-
...element.props.fallbackImage !== void 0 && {
|
|
1999
|
-
|
|
2000
|
-
},
|
|
2001
|
-
...element.props.
|
|
2002
|
-
width: element.props.width
|
|
2003
|
-
},
|
|
2004
|
-
...element.props.height !== void 0 && {
|
|
2005
|
-
height: element.props.height
|
|
2006
|
-
},
|
|
2111
|
+
...element.props.fallbackImage !== void 0 && { fallbackImage: element.props.fallbackImage },
|
|
2112
|
+
...element.props.width !== void 0 && { width: element.props.width },
|
|
2113
|
+
...element.props.height !== void 0 && { height: element.props.height },
|
|
2114
|
+
...element.props.resolutions !== void 0 && { resolutions: element.props.resolutions },
|
|
2007
2115
|
frames
|
|
2008
2116
|
};
|
|
2009
2117
|
}
|