vidply 1.0.30 → 1.0.31

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.
@@ -159,9 +159,16 @@ async function captureVideoFrame(video, time, options = {}) {
159
159
  const timeDiff = Math.abs(video.currentTime - time);
160
160
  if (timeDiff < 0.1 && video.readyState >= 2) {
161
161
  captureFrame();
162
- } else {
162
+ } else if (video.readyState >= 1) {
163
163
  video.addEventListener("seeked", onSeeked);
164
164
  video.currentTime = time;
165
+ } else {
166
+ const onLoadedMetadata = () => {
167
+ video.removeEventListener("loadedmetadata", onLoadedMetadata);
168
+ video.addEventListener("seeked", onSeeked);
169
+ video.currentTime = time;
170
+ };
171
+ video.addEventListener("loadedmetadata", onLoadedMetadata);
165
172
  }
166
173
  });
167
174
  }
@@ -836,33 +843,48 @@ var ControlBar = class {
836
843
  }
837
844
  const renderer = this.player.renderer;
838
845
  const hasVideoMedia = renderer && renderer.media && renderer.media.tagName === "VIDEO";
839
- const isHTML5Renderer = renderer && (renderer.constructor.name === "HTML5Renderer" || renderer.constructor.name === "HLSRenderer" && hasVideoMedia);
846
+ const isHTML5Renderer = hasVideoMedia && renderer.media === this.player.element && !renderer.hls && typeof renderer.seek === "function";
840
847
  this.previewSupported = isHTML5Renderer && hasVideoMedia;
841
848
  if (this.previewSupported) {
842
849
  this.previewVideo = document.createElement("video");
843
850
  this.previewVideo.muted = true;
844
- this.previewVideo.preload = "metadata";
851
+ this.previewVideo.preload = "auto";
852
+ this.previewVideo.playsInline = true;
845
853
  this.previewVideo.style.position = "absolute";
846
854
  this.previewVideo.style.visibility = "hidden";
847
855
  this.previewVideo.style.width = "1px";
848
856
  this.previewVideo.style.height = "1px";
849
857
  this.previewVideo.style.top = "-9999px";
850
858
  const mainVideo = renderer.media || this.player.element;
859
+ let videoSrc = null;
851
860
  if (mainVideo.src) {
852
- this.previewVideo.src = mainVideo.src;
861
+ videoSrc = mainVideo.src;
853
862
  } else {
854
863
  const source = mainVideo.querySelector("source");
855
864
  if (source) {
856
- this.previewVideo.src = source.src;
865
+ videoSrc = source.src;
857
866
  }
858
867
  }
859
- this.previewVideo.addEventListener("error", () => {
860
- this.player.log("Preview video failed to load", "warn");
868
+ if (!videoSrc) {
869
+ this.player.log("No video source found for preview", "warn");
870
+ this.previewSupported = false;
871
+ return;
872
+ }
873
+ if (mainVideo.crossOrigin) {
874
+ this.previewVideo.crossOrigin = mainVideo.crossOrigin;
875
+ }
876
+ this.previewVideo.addEventListener("error", (e) => {
877
+ this.player.log("Preview video failed to load:", e, "warn");
861
878
  this.previewSupported = false;
862
879
  });
880
+ this.previewVideo.addEventListener("loadedmetadata", () => {
881
+ this.previewVideoReady = true;
882
+ }, { once: true });
863
883
  if (this.player.container) {
864
884
  this.player.container.appendChild(this.previewVideo);
865
885
  }
886
+ this.previewVideo.src = videoSrc;
887
+ this.previewVideoReady = false;
866
888
  }
867
889
  }
868
890
  /**
@@ -874,6 +896,45 @@ var ControlBar = class {
874
896
  if (!this.previewSupported || !this.previewVideo) {
875
897
  return null;
876
898
  }
899
+ if (!this.previewVideoReady) {
900
+ if (this.previewVideo.readyState < 2) {
901
+ await new Promise((resolve, reject) => {
902
+ const timeout = setTimeout(() => {
903
+ reject(new Error("Preview video data load timeout"));
904
+ }, 1e4);
905
+ const cleanup = () => {
906
+ clearTimeout(timeout);
907
+ this.previewVideo.removeEventListener("loadeddata", checkReady);
908
+ this.previewVideo.removeEventListener("canplay", checkReady);
909
+ this.previewVideo.removeEventListener("error", onError);
910
+ };
911
+ const checkReady = () => {
912
+ if (this.previewVideo.readyState >= 2) {
913
+ cleanup();
914
+ this.previewVideoReady = true;
915
+ resolve();
916
+ }
917
+ };
918
+ const onError = () => {
919
+ cleanup();
920
+ reject(new Error("Preview video failed to load"));
921
+ };
922
+ if (this.previewVideo.readyState >= 1) {
923
+ this.previewVideo.addEventListener("loadeddata", checkReady);
924
+ }
925
+ this.previewVideo.addEventListener("canplay", checkReady);
926
+ this.previewVideo.addEventListener("error", onError);
927
+ if (this.previewVideo.readyState >= 2) {
928
+ checkReady();
929
+ }
930
+ }).catch(() => {
931
+ this.previewSupported = false;
932
+ return null;
933
+ });
934
+ } else {
935
+ this.previewVideoReady = true;
936
+ }
937
+ }
877
938
  const cacheKey = Math.floor(time);
878
939
  if (this.previewThumbnailCache.has(cacheKey)) {
879
940
  return this.previewThumbnailCache.get(cacheKey);
@@ -885,7 +946,7 @@ var ControlBar = class {
885
946
  maxHeight: 90
886
947
  });
887
948
  if (dataURL) {
888
- if (this.previewThumbnailCache.size > 20) {
949
+ if (this.previewThumbnailCache.size >= 20) {
889
950
  const firstKey = this.previewThumbnailCache.keys().next().value;
890
951
  this.previewThumbnailCache.delete(firstKey);
891
952
  }
@@ -898,19 +959,32 @@ var ControlBar = class {
898
959
  * @param {number} time - Time in seconds
899
960
  */
900
961
  async updatePreviewThumbnail(time) {
901
- if (!this.previewSupported) {
962
+ if (!this.previewSupported || !this.controls.progressPreview) {
902
963
  return;
903
964
  }
904
965
  if (this.previewThumbnailTimeout) {
905
966
  clearTimeout(this.previewThumbnailTimeout);
906
967
  }
907
968
  this.previewThumbnailTimeout = setTimeout(async () => {
908
- const thumbnail = await this.generatePreviewThumbnail(time);
909
- if (thumbnail && this.controls.progressPreview) {
910
- this.controls.progressPreview.style.backgroundImage = `url(${thumbnail})`;
911
- this.controls.progressPreview.style.display = "block";
969
+ try {
970
+ const thumbnail = await this.generatePreviewThumbnail(time);
971
+ if (thumbnail && this.controls.progressPreview) {
972
+ this.controls.progressPreview.style.backgroundImage = `url("${thumbnail}")`;
973
+ this.controls.progressPreview.style.display = "block";
974
+ this.controls.progressPreview.style.backgroundRepeat = "no-repeat";
975
+ this.controls.progressPreview.style.backgroundPosition = "center";
976
+ } else {
977
+ if (this.controls.progressPreview) {
978
+ this.controls.progressPreview.style.display = "none";
979
+ }
980
+ }
981
+ this.currentPreviewTime = time;
982
+ } catch (error) {
983
+ this.player.log("Preview thumbnail update failed:", error, "warn");
984
+ if (this.controls.progressPreview) {
985
+ this.controls.progressPreview.style.display = "none";
986
+ }
912
987
  }
913
- this.currentPreviewTime = time;
914
988
  }, 100);
915
989
  }
916
990
  setupProgressBarEvents() {
@@ -944,7 +1018,9 @@ var ControlBar = class {
944
1018
  this.controls.progressTooltipTime.textContent = TimeUtils.formatTime(time);
945
1019
  this.controls.progressTooltip.style.left = `${left}px`;
946
1020
  this.controls.progressTooltip.style.display = "block";
947
- this.updatePreviewThumbnail(time);
1021
+ if (this.previewSupported) {
1022
+ this.updatePreviewThumbnail(time);
1023
+ }
948
1024
  }
949
1025
  });
950
1026
  progress.addEventListener("mouseleave", () => {
@@ -2231,6 +2307,10 @@ var ControlBar = class {
2231
2307
  this.updateDuration();
2232
2308
  this.ensureQualityButton();
2233
2309
  this.updateQualityIndicator();
2310
+ this.updatePreviewVideoSource();
2311
+ });
2312
+ this.player.on("sourcechange", () => {
2313
+ this.updatePreviewVideoSource();
2234
2314
  });
2235
2315
  this.player.on("volumechange", () => this.updateVolumeDisplay());
2236
2316
  this.player.on("progress", () => this.updateBuffered());
@@ -2698,6 +2778,37 @@ var ControlBar = class {
2698
2778
  hide() {
2699
2779
  this.element.style.display = "none";
2700
2780
  }
2781
+ /**
2782
+ * Update preview video source when player source changes (for playlists)
2783
+ * Also re-initializes if preview wasn't set up initially
2784
+ */
2785
+ updatePreviewVideoSource() {
2786
+ const renderer = this.player.renderer;
2787
+ if (!renderer || !renderer.media || renderer.media.tagName !== "VIDEO") {
2788
+ return;
2789
+ }
2790
+ if (!this.previewSupported && !this.previewVideo) {
2791
+ this.initPreviewThumbnail();
2792
+ }
2793
+ if (!this.previewSupported || !this.previewVideo) {
2794
+ return;
2795
+ }
2796
+ const mainVideo = renderer.media;
2797
+ const newSrc = mainVideo.src || mainVideo.querySelector("source")?.src;
2798
+ if (newSrc && this.previewVideo.src !== newSrc) {
2799
+ this.previewThumbnailCache.clear();
2800
+ this.previewVideoReady = false;
2801
+ this.previewVideo.src = newSrc;
2802
+ if (mainVideo.crossOrigin) {
2803
+ this.previewVideo.crossOrigin = mainVideo.crossOrigin;
2804
+ }
2805
+ this.previewVideo.addEventListener("loadedmetadata", () => {
2806
+ this.previewVideoReady = true;
2807
+ }, { once: true });
2808
+ } else if (newSrc && !this.previewVideoReady && this.previewVideo.readyState >= 1) {
2809
+ this.previewVideoReady = true;
2810
+ }
2811
+ }
2701
2812
  /**
2702
2813
  * Cleanup preview thumbnail resources
2703
2814
  */