@geekapps/silo-elements-nextjs 0.2.61 → 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/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
@@ -759,6 +759,8 @@ function FileUploader({
759
759
  showVideoOptions = false,
760
760
  image,
761
761
  video,
762
+ isPrivate,
763
+ captionLanguage,
762
764
  theme,
763
765
  renderIcon,
764
766
  renderProgress,
@@ -804,7 +806,13 @@ function FileUploader({
804
806
  const effectiveVideo = showVideoOptions ? resolvedVideo : video ?? resolvedVideo;
805
807
  if (multiple && files.length > 1) {
806
808
  try {
807
- const results = await batch.upload(files, { ...bucket !== void 0 && { bucket }, image: effectiveImage, video: effectiveVideo });
809
+ const results = await batch.upload(files, {
810
+ ...bucket !== void 0 && { bucket },
811
+ image: effectiveImage,
812
+ video: effectiveVideo,
813
+ ...isPrivate !== void 0 && { isPrivate },
814
+ ...captionLanguage && { captionLanguage }
815
+ });
808
816
  onBatchUpload?.(results);
809
817
  results.forEach((r) => onUpload?.(r));
810
818
  } catch (err) {
@@ -814,13 +822,18 @@ function FileUploader({
814
822
  const file = files[0];
815
823
  if (!file) return;
816
824
  try {
817
- const result = await single.upload(file, { ...bucket !== void 0 && { bucket }, image: effectiveImage, video: effectiveVideo });
825
+ const result = await single.upload(file, {
826
+ ...bucket !== void 0 && { bucket },
827
+ image: effectiveImage,
828
+ video: effectiveVideo,
829
+ ...isPrivate !== void 0 && { isPrivate }
830
+ });
818
831
  if (result) onUpload?.(result);
819
832
  } catch (err) {
820
833
  onError?.(err instanceof Error ? err : new Error(String(err)));
821
834
  }
822
835
  }
823
- }, [single, batch, multiple, bucket, image, video, imageOpts, videoOpts, showImageOptions, showVideoOptions, onUpload, onBatchUpload, onError]);
836
+ }, [single, batch, multiple, bucket, image, video, isPrivate, captionLanguage, imageOpts, videoOpts, showImageOptions, showVideoOptions, onUpload, onBatchUpload, onError]);
824
837
  const handleFiles = useCallback(async (files) => {
825
838
  const needsStaging = allowRename || showImageOptions && files.some((f) => f.type.startsWith("image/")) || showVideoOptions && files.some((f) => f.type.startsWith("video/"));
826
839
  if (needsStaging) {
@@ -1234,13 +1247,6 @@ function Video({
1234
1247
  cancelled = true;
1235
1248
  };
1236
1249
  }, [parsed.chaptersSrc, parsed.chaptersData]);
1237
- useEffect(() => {
1238
- if (!parsed.thumbnailSrc) {
1239
- setPoster(void 0);
1240
- return;
1241
- }
1242
- setPoster(parsed.thumbnailSrc);
1243
- }, [parsed.thumbnailSrc]);
1244
1250
  const initialSourceIndex = useMemo(() => {
1245
1251
  const index = parsed.sources.findIndex((source) => source.default);
1246
1252
  return index >= 0 ? index : 0;
@@ -1277,9 +1283,10 @@ function Video({
1277
1283
  const [activeCue, setActiveCue] = useState(null);
1278
1284
  const [storyboardSheetSize, setStoryboardSheetSize] = useState(null);
1279
1285
  const storyboardSheetUrlRef = useRef("");
1280
- const [storyboardCues, setStoryboardCues] = useState(
1281
- []
1282
- );
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());
1283
1290
  const [ratingCounts, setRatingCounts] = useState(
1284
1291
  () => ({ LOVE: parsed.rating?.counts?.LOVE ?? 0, LIKE: parsed.rating?.counts?.LIKE ?? 0, DISLIKE: parsed.rating?.counts?.DISLIKE ?? 0 })
1285
1292
  );
@@ -1322,6 +1329,24 @@ function Video({
1322
1329
  const [isFullscreen, setIsFullscreen] = useState(false);
1323
1330
  const [playerHeight, setPlayerHeight] = useState(0);
1324
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]);
1325
1350
  const activeSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
1326
1351
  const progressPercent = duration ? currentTime / duration * 100 : 0;
1327
1352
  const bufferedPercent = duration ? bufferedTime / duration * 100 : 0;
@@ -1570,40 +1595,71 @@ function Video({
1570
1595
  async function loadStoryboard() {
1571
1596
  if (!parsed.storyboard) {
1572
1597
  setStoryboardCues([]);
1598
+ setActiveStoryboardRes(null);
1573
1599
  return;
1574
1600
  }
1575
1601
  if (parsed.storyboard.frames.length > 0) {
1576
1602
  setStoryboardCues(parsed.storyboard.frames);
1577
1603
  return;
1578
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
+ }
1579
1644
  if (!parsed.storyboard.src) {
1580
1645
  setStoryboardCues([]);
1581
1646
  return;
1582
1647
  }
1583
1648
  try {
1584
1649
  const response = await fetch(parsed.storyboard.src);
1585
- if (!response.ok) {
1586
- throw new Error("Storyboard not found");
1587
- }
1650
+ if (!response.ok) throw new Error("Storyboard not found");
1588
1651
  const text = await response.text();
1589
- const cues = parseStoryboardVtt(
1590
- text,
1591
- new URL(parsed.storyboard.src, window.location.href).href
1592
- );
1593
- if (!cancelled) {
1594
- setStoryboardCues(cues);
1595
- }
1652
+ const cues = parseStoryboardVtt(text, new URL(parsed.storyboard.src, window.location.href).href);
1653
+ if (!cancelled) setStoryboardCues(cues);
1596
1654
  } catch {
1597
1655
  if (!cancelled && parsed.storyboard.fallbackImage) {
1598
- setStoryboardCues([
1599
- {
1600
- start: 0,
1601
- end: Number.MAX_SAFE_INTEGER,
1602
- image: parsed.storyboard.fallbackImage,
1603
- w: parsed.storyboard.width ?? 160,
1604
- h: parsed.storyboard.height ?? 90
1605
- }
1606
- ]);
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
+ }]);
1607
1663
  }
1608
1664
  }
1609
1665
  }
@@ -1914,6 +1970,63 @@ function Video({
1914
1970
  },
1915
1971
  [duration]
1916
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]);
1917
2030
  const updatePreview = useCallback(
1918
2031
  (clientX) => {
1919
2032
  const progress = progressRef.current;
@@ -1924,6 +2037,7 @@ function Video({
1924
2037
  const rect = progress.getBoundingClientRect();
1925
2038
  const x = Math.max(0, Math.min(clientX - rect.left, rect.width));
1926
2039
  const time = x / rect.width * duration;
2040
+ upgradeStoryboardResolution(time);
1927
2041
  const cue = findStoryboardCue(storyboardCues, time);
1928
2042
  if (!cue) {
1929
2043
  setPreview(null);
@@ -1935,7 +2049,7 @@ function Video({
1935
2049
  left: Math.max(80, Math.min(x, rect.width - 80))
1936
2050
  });
1937
2051
  },
1938
- [duration, storyboardCues]
2052
+ [duration, storyboardCues, upgradeStoryboardResolution]
1939
2053
  );
1940
2054
  const handleProgressPointerMove = useCallback(
1941
2055
  (event) => {
@@ -1949,8 +2063,19 @@ function Video({
1949
2063
  }, []);
1950
2064
  const handleProgressPointerLeave = useCallback(() => {
1951
2065
  setIsHoveringProgress(false);
1952
- if (!isDragging) setPreview(null);
1953
- }, [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]);
1954
2079
  const handleProgressPointerDown = useCallback(
1955
2080
  (event) => {
1956
2081
  const progress = progressRef.current;
@@ -3099,7 +3224,8 @@ function parseVideoChildren(children) {
3099
3224
  }
3100
3225
  if (child.type === VideoThumbnail || name === "SiloVideoThumbnail") {
3101
3226
  const element = child;
3102
- 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;
3103
3229
  }
3104
3230
  if (child.type === AgeRating || name === "SiloAgeRating") {
3105
3231
  const element = child;
@@ -3122,15 +3248,10 @@ function parseVideoChildren(children) {
3122
3248
  });
3123
3249
  parsed.storyboard = {
3124
3250
  ...element.props.src !== void 0 && { src: element.props.src },
3125
- ...element.props.fallbackImage !== void 0 && {
3126
- fallbackImage: element.props.fallbackImage
3127
- },
3128
- ...element.props.width !== void 0 && {
3129
- width: element.props.width
3130
- },
3131
- ...element.props.height !== void 0 && {
3132
- height: element.props.height
3133
- },
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 },
3134
3255
  frames
3135
3256
  };
3136
3257
  }