@geekapps/silo-elements-nextjs 0.2.62 → 0.2.64

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/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { ImageUploader } from './ImageUploader.js';
2
2
  export { VideoUploader } from './VideoUploader.js';
3
3
  export { FileUploader } from './FileUploader.js';
4
4
  export { MediaUploader } from './MediaUploader.js';
5
- export { AgeRating, AgeRatingProps, Captions, CaptionsProps, Chapters, ChaptersProps, Rating, RatingProps, Source, SourceProps, Storyboard, StoryboardFrame, StoryboardFrameProps, StoryboardProps, Video, VideoPlayer, VideoSourceType, VideoThumbnail, ThumbnailProps as VideoThumbnailProps } from './VideoPlayer.js';
5
+ export { AgeRating, AgeRatingProps, Captions, CaptionsProps, Chapters, ChaptersProps, Rating, RatingProps, Source, SourceProps, Storyboard, StoryboardFrame, StoryboardFrameProps, StoryboardProps, StoryboardResolution, Video, VideoPlayer, VideoSourceType, VideoThumbnail, ThumbnailProps as VideoThumbnailProps } from './VideoPlayer.js';
6
6
  export { DropZone } from './components/DropZone.js';
7
7
  export { ProgressBar } from './components/ProgressBar.js';
8
8
  import * as react from 'react';
package/dist/index.js CHANGED
@@ -760,7 +760,7 @@ function FileUploader({
760
760
  image,
761
761
  video,
762
762
  isPrivate,
763
- captionLanguage,
763
+ captionLanguages,
764
764
  theme,
765
765
  renderIcon,
766
766
  renderProgress,
@@ -811,7 +811,7 @@ function FileUploader({
811
811
  image: effectiveImage,
812
812
  video: effectiveVideo,
813
813
  ...isPrivate !== void 0 && { isPrivate },
814
- ...captionLanguage && { captionLanguage }
814
+ ...captionLanguages?.length && { captionLanguages }
815
815
  });
816
816
  onBatchUpload?.(results);
817
817
  results.forEach((r) => onUpload?.(r));
@@ -833,7 +833,7 @@ function FileUploader({
833
833
  onError?.(err instanceof Error ? err : new Error(String(err)));
834
834
  }
835
835
  }
836
- }, [single, batch, multiple, bucket, image, video, isPrivate, captionLanguage, imageOpts, videoOpts, showImageOptions, showVideoOptions, onUpload, onBatchUpload, onError]);
836
+ }, [single, batch, multiple, bucket, image, video, isPrivate, captionLanguages, imageOpts, videoOpts, showImageOptions, showVideoOptions, onUpload, onBatchUpload, onError]);
837
837
  const handleFiles = useCallback(async (files) => {
838
838
  const needsStaging = allowRename || showImageOptions && files.some((f) => f.type.startsWith("image/")) || showVideoOptions && files.some((f) => f.type.startsWith("video/"));
839
839
  if (needsStaging) {
@@ -1247,13 +1247,6 @@ function Video({
1247
1247
  cancelled = true;
1248
1248
  };
1249
1249
  }, [parsed.chaptersSrc, parsed.chaptersData]);
1250
- useEffect(() => {
1251
- if (!parsed.thumbnailSrc) {
1252
- setPoster(void 0);
1253
- return;
1254
- }
1255
- setPoster(parsed.thumbnailSrc);
1256
- }, [parsed.thumbnailSrc]);
1257
1250
  const initialSourceIndex = useMemo(() => {
1258
1251
  const index = parsed.sources.findIndex((source) => source.default);
1259
1252
  return index >= 0 ? index : 0;
@@ -1290,9 +1283,10 @@ function Video({
1290
1283
  const [activeCue, setActiveCue] = useState(null);
1291
1284
  const [storyboardSheetSize, setStoryboardSheetSize] = useState(null);
1292
1285
  const storyboardSheetUrlRef = useRef("");
1293
- const [storyboardCues, setStoryboardCues] = useState(
1294
- []
1295
- );
1286
+ const [storyboardCues, setStoryboardCues] = useState([]);
1287
+ const storyboardCacheRef = useRef(/* @__PURE__ */ new Map());
1288
+ const [activeStoryboardRes, setActiveStoryboardRes] = useState(null);
1289
+ const preloadedSpritesRef = useRef(/* @__PURE__ */ new Set());
1296
1290
  const [ratingCounts, setRatingCounts] = useState(
1297
1291
  () => ({ LOVE: parsed.rating?.counts?.LOVE ?? 0, LIKE: parsed.rating?.counts?.LIKE ?? 0, DISLIKE: parsed.rating?.counts?.DISLIKE ?? 0 })
1298
1292
  );
@@ -1335,6 +1329,24 @@ function Video({
1335
1329
  const [isFullscreen, setIsFullscreen] = useState(false);
1336
1330
  const [playerHeight, setPlayerHeight] = useState(0);
1337
1331
  const [playerWidth, setPlayerWidth] = useState(0);
1332
+ useEffect(() => {
1333
+ const urls = parsed.thumbnailUrls;
1334
+ if (!urls || Object.keys(urls).length === 0) {
1335
+ setPoster(parsed.thumbnailSrc ?? void 0);
1336
+ return;
1337
+ }
1338
+ 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);
1339
+ if (sorted.length === 0) {
1340
+ setPoster(parsed.thumbnailSrc ?? void 0);
1341
+ return;
1342
+ }
1343
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio ?? 1 : 1;
1344
+ const conn = typeof navigator !== "undefined" ? navigator.connection : null;
1345
+ const connFactor = conn?.effectiveType === "4g" ? 1 : conn?.effectiveType === "3g" ? 0.5 : 0.25;
1346
+ const targetPx = (playerWidth || 640) * dpr * (conn ? connFactor : 1);
1347
+ const best = sorted.find((e) => e.px >= targetPx) ?? sorted[sorted.length - 1];
1348
+ setPoster(best.url);
1349
+ }, [parsed.thumbnailUrls, parsed.thumbnailSrc, playerWidth]);
1338
1350
  const activeSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
1339
1351
  const progressPercent = duration ? currentTime / duration * 100 : 0;
1340
1352
  const bufferedPercent = duration ? bufferedTime / duration * 100 : 0;
@@ -1583,40 +1595,71 @@ function Video({
1583
1595
  async function loadStoryboard() {
1584
1596
  if (!parsed.storyboard) {
1585
1597
  setStoryboardCues([]);
1598
+ setActiveStoryboardRes(null);
1586
1599
  return;
1587
1600
  }
1588
1601
  if (parsed.storyboard.frames.length > 0) {
1589
1602
  setStoryboardCues(parsed.storyboard.frames);
1590
1603
  return;
1591
1604
  }
1605
+ const resolutions = parsed.storyboard.resolutions;
1606
+ if (resolutions && Object.keys(resolutions).length > 0) {
1607
+ storyboardCacheRef.current.clear();
1608
+ preloadedSpritesRef.current.clear();
1609
+ const RES_ORDER = ["180p", "240p", "360p", "480p", "default"];
1610
+ const keys = Object.keys(resolutions).sort((a, b) => {
1611
+ const ai = RES_ORDER.indexOf(a);
1612
+ const bi = RES_ORDER.indexOf(b);
1613
+ return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
1614
+ });
1615
+ const loadRes = async (key) => {
1616
+ if (cancelled) return;
1617
+ const res = resolutions[key];
1618
+ if (!res) return;
1619
+ let cues = [];
1620
+ if (res.vttText) {
1621
+ cues = parseStoryboardVtt(res.vttText, res.spriteUrl);
1622
+ } else {
1623
+ cues = [{ start: 0, end: Number.MAX_SAFE_INTEGER, image: res.spriteUrl, w: res.tileW, h: res.tileH }];
1624
+ }
1625
+ storyboardCacheRef.current.set(key, cues);
1626
+ if (!preloadedSpritesRef.current.has(res.spriteUrl)) {
1627
+ preloadedSpritesRef.current.add(res.spriteUrl);
1628
+ const img = new window.Image();
1629
+ img.src = res.spriteUrl;
1630
+ }
1631
+ return cues;
1632
+ };
1633
+ const firstKey = keys[0];
1634
+ const firstCues = await loadRes(firstKey);
1635
+ if (!cancelled && firstCues) {
1636
+ setStoryboardCues(firstCues);
1637
+ setActiveStoryboardRes(firstKey);
1638
+ }
1639
+ for (const key of keys.slice(1)) {
1640
+ await loadRes(key);
1641
+ }
1642
+ return;
1643
+ }
1592
1644
  if (!parsed.storyboard.src) {
1593
1645
  setStoryboardCues([]);
1594
1646
  return;
1595
1647
  }
1596
1648
  try {
1597
1649
  const response = await fetch(parsed.storyboard.src);
1598
- if (!response.ok) {
1599
- throw new Error("Storyboard not found");
1600
- }
1650
+ if (!response.ok) throw new Error("Storyboard not found");
1601
1651
  const text = await response.text();
1602
- const cues = parseStoryboardVtt(
1603
- text,
1604
- new URL(parsed.storyboard.src, window.location.href).href
1605
- );
1606
- if (!cancelled) {
1607
- setStoryboardCues(cues);
1608
- }
1652
+ const cues = parseStoryboardVtt(text, new URL(parsed.storyboard.src, window.location.href).href);
1653
+ if (!cancelled) setStoryboardCues(cues);
1609
1654
  } catch {
1610
1655
  if (!cancelled && parsed.storyboard.fallbackImage) {
1611
- setStoryboardCues([
1612
- {
1613
- start: 0,
1614
- end: Number.MAX_SAFE_INTEGER,
1615
- image: parsed.storyboard.fallbackImage,
1616
- w: parsed.storyboard.width ?? 160,
1617
- h: parsed.storyboard.height ?? 90
1618
- }
1619
- ]);
1656
+ setStoryboardCues([{
1657
+ start: 0,
1658
+ end: Number.MAX_SAFE_INTEGER,
1659
+ image: parsed.storyboard.fallbackImage,
1660
+ w: parsed.storyboard.width ?? 160,
1661
+ h: parsed.storyboard.height ?? 90
1662
+ }]);
1620
1663
  }
1621
1664
  }
1622
1665
  }
@@ -1927,6 +1970,63 @@ function Video({
1927
1970
  },
1928
1971
  [duration]
1929
1972
  );
1973
+ const upgradeStoryboardResolution = useCallback((hoverTime) => {
1974
+ const resolutions = parsed.storyboard?.resolutions;
1975
+ if (!resolutions || storyboardCacheRef.current.size === 0) return;
1976
+ const RES_ORDER = ["480p", "360p", "240p", "180p", "default"];
1977
+ const conn = typeof navigator !== "undefined" ? navigator.connection : null;
1978
+ const maxRes = conn?.effectiveType === "2g" ? "180p" : conn?.effectiveType === "3g" ? "240p" : "480p";
1979
+ const maxIdx = RES_ORDER.indexOf(maxRes);
1980
+ for (let i = 0; i <= maxIdx; i++) {
1981
+ const key = RES_ORDER[i];
1982
+ if (storyboardCacheRef.current.has(key)) {
1983
+ if (activeStoryboardRes !== key) {
1984
+ setStoryboardCues(storyboardCacheRef.current.get(key));
1985
+ setActiveStoryboardRes(key);
1986
+ const spriteUrl = resolutions[key]?.spriteUrl;
1987
+ if (spriteUrl && !preloadedSpritesRef.current.has(spriteUrl)) {
1988
+ preloadedSpritesRef.current.add(spriteUrl);
1989
+ const img = new window.Image();
1990
+ img.src = spriteUrl;
1991
+ }
1992
+ }
1993
+ break;
1994
+ }
1995
+ }
1996
+ const bestKey = [...storyboardCacheRef.current.keys()].sort((a, b) => {
1997
+ const order = ["480p", "360p", "240p", "180p", "default"];
1998
+ return order.indexOf(a) - order.indexOf(b);
1999
+ })[0];
2000
+ if (bestKey) {
2001
+ const cues = storyboardCacheRef.current.get(bestKey) ?? [];
2002
+ const nearIdx = cues.findIndex((c) => c.start > hoverTime);
2003
+ const window2 = cues.slice(Math.max(0, nearIdx - 2), nearIdx + 3);
2004
+ for (const c of window2) {
2005
+ if (!preloadedSpritesRef.current.has(c.image)) {
2006
+ preloadedSpritesRef.current.add(c.image);
2007
+ const img = new window.Image();
2008
+ img.src = c.image;
2009
+ }
2010
+ }
2011
+ }
2012
+ }, [parsed.storyboard?.resolutions, activeStoryboardRes]);
2013
+ useEffect(() => {
2014
+ const resolutions = parsed.storyboard?.resolutions;
2015
+ if (!resolutions || storyboardCacheRef.current.size === 0 || !duration) return;
2016
+ const RES_ORDER = ["180p", "240p", "360p", "480p", "default"];
2017
+ const lowestKey = RES_ORDER.find((k) => storyboardCacheRef.current.has(k));
2018
+ if (!lowestKey) return;
2019
+ const cues = storyboardCacheRef.current.get(lowestKey) ?? [];
2020
+ const playheadIdx = cues.findIndex((c) => c.start > currentTime);
2021
+ const nearby = cues.slice(Math.max(0, playheadIdx - 2), playheadIdx + 6);
2022
+ for (const c of nearby) {
2023
+ if (!preloadedSpritesRef.current.has(c.image)) {
2024
+ preloadedSpritesRef.current.add(c.image);
2025
+ const img = new window.Image();
2026
+ img.src = c.image;
2027
+ }
2028
+ }
2029
+ }, [Math.floor(currentTime / 10), parsed.storyboard?.resolutions, duration]);
1930
2030
  const updatePreview = useCallback(
1931
2031
  (clientX) => {
1932
2032
  const progress = progressRef.current;
@@ -1937,6 +2037,7 @@ function Video({
1937
2037
  const rect = progress.getBoundingClientRect();
1938
2038
  const x = Math.max(0, Math.min(clientX - rect.left, rect.width));
1939
2039
  const time = x / rect.width * duration;
2040
+ upgradeStoryboardResolution(time);
1940
2041
  const cue = findStoryboardCue(storyboardCues, time);
1941
2042
  if (!cue) {
1942
2043
  setPreview(null);
@@ -1948,7 +2049,7 @@ function Video({
1948
2049
  left: Math.max(80, Math.min(x, rect.width - 80))
1949
2050
  });
1950
2051
  },
1951
- [duration, storyboardCues]
2052
+ [duration, storyboardCues, upgradeStoryboardResolution]
1952
2053
  );
1953
2054
  const handleProgressPointerMove = useCallback(
1954
2055
  (event) => {
@@ -1962,8 +2063,19 @@ function Video({
1962
2063
  }, []);
1963
2064
  const handleProgressPointerLeave = useCallback(() => {
1964
2065
  setIsHoveringProgress(false);
1965
- if (!isDragging) setPreview(null);
1966
- }, [isDragging]);
2066
+ if (!isDragging) {
2067
+ setPreview(null);
2068
+ const resolutions = parsed.storyboard?.resolutions;
2069
+ if (resolutions && storyboardCacheRef.current.size > 0) {
2070
+ const RES_ORDER = ["180p", "240p", "360p", "480p", "default"];
2071
+ const lowestKey = RES_ORDER.find((k) => storyboardCacheRef.current.has(k));
2072
+ if (lowestKey && activeStoryboardRes !== lowestKey) {
2073
+ setStoryboardCues(storyboardCacheRef.current.get(lowestKey));
2074
+ setActiveStoryboardRes(lowestKey);
2075
+ }
2076
+ }
2077
+ }
2078
+ }, [isDragging, parsed.storyboard?.resolutions, activeStoryboardRes]);
1967
2079
  const handleProgressPointerDown = useCallback(
1968
2080
  (event) => {
1969
2081
  const progress = progressRef.current;
@@ -3112,7 +3224,8 @@ function parseVideoChildren(children) {
3112
3224
  }
3113
3225
  if (child.type === VideoThumbnail || name === "SiloVideoThumbnail") {
3114
3226
  const element = child;
3115
- parsed.thumbnailSrc = element.props.src;
3227
+ if (element.props.urls) parsed.thumbnailUrls = element.props.urls;
3228
+ if (element.props.src) parsed.thumbnailSrc = element.props.src;
3116
3229
  }
3117
3230
  if (child.type === AgeRating || name === "SiloAgeRating") {
3118
3231
  const element = child;
@@ -3135,15 +3248,10 @@ function parseVideoChildren(children) {
3135
3248
  });
3136
3249
  parsed.storyboard = {
3137
3250
  ...element.props.src !== void 0 && { src: element.props.src },
3138
- ...element.props.fallbackImage !== void 0 && {
3139
- fallbackImage: element.props.fallbackImage
3140
- },
3141
- ...element.props.width !== void 0 && {
3142
- width: element.props.width
3143
- },
3144
- ...element.props.height !== void 0 && {
3145
- height: element.props.height
3146
- },
3251
+ ...element.props.fallbackImage !== void 0 && { fallbackImage: element.props.fallbackImage },
3252
+ ...element.props.width !== void 0 && { width: element.props.width },
3253
+ ...element.props.height !== void 0 && { height: element.props.height },
3254
+ ...element.props.resolutions !== void 0 && { resolutions: element.props.resolutions },
3147
3255
  frames
3148
3256
  };
3149
3257
  }