vidply 1.0.30 → 1.0.32

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.
@@ -5194,13 +5194,19 @@
5194
5194
  }
5195
5195
  getQualities() {
5196
5196
  if (this.hls && this.hls.levels) {
5197
- return this.hls.levels.map((level, index) => ({
5198
- index,
5199
- height: level.height,
5200
- width: level.width,
5201
- bitrate: level.bitrate,
5202
- name: "".concat(level.height, "p")
5203
- }));
5197
+ return this.hls.levels.map((level, index) => {
5198
+ const height = Number(level.height) || 0;
5199
+ const bitrate = Number(level.bitrate) || 0;
5200
+ const kb = bitrate > 0 ? Math.round(bitrate / 1e3) : 0;
5201
+ const name = height > 0 ? "".concat(height, "p") : kb > 0 ? "".concat(kb, " kb") : "Auto";
5202
+ return {
5203
+ index,
5204
+ height: level.height,
5205
+ width: level.width,
5206
+ bitrate: level.bitrate,
5207
+ name
5208
+ };
5209
+ });
5204
5210
  }
5205
5211
  return [];
5206
5212
  }
@@ -5647,9 +5653,16 @@
5647
5653
  const timeDiff = Math.abs(video.currentTime - time);
5648
5654
  if (timeDiff < 0.1 && video.readyState >= 2) {
5649
5655
  captureFrame();
5650
- } else {
5656
+ } else if (video.readyState >= 1) {
5651
5657
  video.addEventListener("seeked", onSeeked);
5652
5658
  video.currentTime = time;
5659
+ } else {
5660
+ const onLoadedMetadata = () => {
5661
+ video.removeEventListener("loadedmetadata", onLoadedMetadata);
5662
+ video.addEventListener("seeked", onSeeked);
5663
+ video.currentTime = time;
5664
+ };
5665
+ video.addEventListener("loadedmetadata", onLoadedMetadata);
5653
5666
  }
5654
5667
  });
5655
5668
  }
@@ -6084,6 +6097,7 @@
6084
6097
  });
6085
6098
  }
6086
6099
  createControls() {
6100
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
6087
6101
  const progressTimeWrapper = DOMUtils.createElement("div", {
6088
6102
  className: "".concat(this.player.options.classPrefix, "-progress-time-wrapper")
6089
6103
  });
@@ -6149,7 +6163,11 @@
6149
6163
  btn.dataset.overflowPriorityMobile = "3";
6150
6164
  this.rightButtons.appendChild(btn);
6151
6165
  }
6152
- if (this.player.options.speedButton) {
6166
+ const src = this.player.currentSource || ((_b = (_a = this.player.element) == null ? void 0 : _a.getAttribute) == null ? void 0 : _b.call(_a, "src")) || ((_c = this.player.element) == null ? void 0 : _c.currentSrc) || ((_d = this.player.element) == null ? void 0 : _d.src) || ((_h = (_g = (_f = (_e = this.player.element) == null ? void 0 : _e.querySelector) == null ? void 0 : _f.call(_e, "source")) == null ? void 0 : _g.getAttribute) == null ? void 0 : _h.call(_g, "src")) || ((_k = (_j = (_i = this.player.element) == null ? void 0 : _i.querySelector) == null ? void 0 : _j.call(_i, "source")) == null ? void 0 : _k.src) || "";
6167
+ const isHlsSource = typeof src === "string" && src.includes(".m3u8");
6168
+ const isVideoElement = ((_m = (_l = this.player.element) == null ? void 0 : _l.tagName) == null ? void 0 : _m.toLowerCase()) === "video";
6169
+ const hideSpeedForThisPlayer = !!this.player.options.hideSpeedForHls && isHlsSource || !!this.player.options.hideSpeedForHlsVideo && isHlsSource && isVideoElement;
6170
+ if (this.player.options.speedButton && !hideSpeedForThisPlayer) {
6153
6171
  const btn = this.createSpeedButton();
6154
6172
  btn.dataset.overflowPriority = "1";
6155
6173
  btn.dataset.overflowPriorityMobile = "3";
@@ -6325,33 +6343,48 @@
6325
6343
  }
6326
6344
  const renderer = this.player.renderer;
6327
6345
  const hasVideoMedia = renderer && renderer.media && renderer.media.tagName === "VIDEO";
6328
- const isHTML5Renderer = renderer && (renderer.constructor.name === "HTML5Renderer" || renderer.constructor.name === "HLSRenderer" && hasVideoMedia);
6346
+ const isHTML5Renderer = hasVideoMedia && renderer.media === this.player.element && !renderer.hls && typeof renderer.seek === "function";
6329
6347
  this.previewSupported = isHTML5Renderer && hasVideoMedia;
6330
6348
  if (this.previewSupported) {
6331
6349
  this.previewVideo = document.createElement("video");
6332
6350
  this.previewVideo.muted = true;
6333
- this.previewVideo.preload = "metadata";
6351
+ this.previewVideo.preload = "auto";
6352
+ this.previewVideo.playsInline = true;
6334
6353
  this.previewVideo.style.position = "absolute";
6335
6354
  this.previewVideo.style.visibility = "hidden";
6336
6355
  this.previewVideo.style.width = "1px";
6337
6356
  this.previewVideo.style.height = "1px";
6338
6357
  this.previewVideo.style.top = "-9999px";
6339
6358
  const mainVideo = renderer.media || this.player.element;
6359
+ let videoSrc = null;
6340
6360
  if (mainVideo.src) {
6341
- this.previewVideo.src = mainVideo.src;
6361
+ videoSrc = mainVideo.src;
6342
6362
  } else {
6343
6363
  const source = mainVideo.querySelector("source");
6344
6364
  if (source) {
6345
- this.previewVideo.src = source.src;
6365
+ videoSrc = source.src;
6346
6366
  }
6347
6367
  }
6348
- this.previewVideo.addEventListener("error", () => {
6349
- this.player.log("Preview video failed to load", "warn");
6368
+ if (!videoSrc) {
6369
+ this.player.log("No video source found for preview", "warn");
6370
+ this.previewSupported = false;
6371
+ return;
6372
+ }
6373
+ if (mainVideo.crossOrigin) {
6374
+ this.previewVideo.crossOrigin = mainVideo.crossOrigin;
6375
+ }
6376
+ this.previewVideo.addEventListener("error", (e) => {
6377
+ this.player.log("Preview video failed to load:", e, "warn");
6350
6378
  this.previewSupported = false;
6351
6379
  });
6380
+ this.previewVideo.addEventListener("loadedmetadata", () => {
6381
+ this.previewVideoReady = true;
6382
+ }, { once: true });
6352
6383
  if (this.player.container) {
6353
6384
  this.player.container.appendChild(this.previewVideo);
6354
6385
  }
6386
+ this.previewVideo.src = videoSrc;
6387
+ this.previewVideoReady = false;
6355
6388
  }
6356
6389
  }
6357
6390
  /**
@@ -6363,6 +6396,45 @@
6363
6396
  if (!this.previewSupported || !this.previewVideo) {
6364
6397
  return null;
6365
6398
  }
6399
+ if (!this.previewVideoReady) {
6400
+ if (this.previewVideo.readyState < 2) {
6401
+ await new Promise((resolve, reject) => {
6402
+ const timeout = setTimeout(() => {
6403
+ reject(new Error("Preview video data load timeout"));
6404
+ }, 1e4);
6405
+ const cleanup = () => {
6406
+ clearTimeout(timeout);
6407
+ this.previewVideo.removeEventListener("loadeddata", checkReady);
6408
+ this.previewVideo.removeEventListener("canplay", checkReady);
6409
+ this.previewVideo.removeEventListener("error", onError);
6410
+ };
6411
+ const checkReady = () => {
6412
+ if (this.previewVideo.readyState >= 2) {
6413
+ cleanup();
6414
+ this.previewVideoReady = true;
6415
+ resolve();
6416
+ }
6417
+ };
6418
+ const onError = () => {
6419
+ cleanup();
6420
+ reject(new Error("Preview video failed to load"));
6421
+ };
6422
+ if (this.previewVideo.readyState >= 1) {
6423
+ this.previewVideo.addEventListener("loadeddata", checkReady);
6424
+ }
6425
+ this.previewVideo.addEventListener("canplay", checkReady);
6426
+ this.previewVideo.addEventListener("error", onError);
6427
+ if (this.previewVideo.readyState >= 2) {
6428
+ checkReady();
6429
+ }
6430
+ }).catch(() => {
6431
+ this.previewSupported = false;
6432
+ return null;
6433
+ });
6434
+ } else {
6435
+ this.previewVideoReady = true;
6436
+ }
6437
+ }
6366
6438
  const cacheKey = Math.floor(time);
6367
6439
  if (this.previewThumbnailCache.has(cacheKey)) {
6368
6440
  return this.previewThumbnailCache.get(cacheKey);
@@ -6374,7 +6446,7 @@
6374
6446
  maxHeight: 90
6375
6447
  });
6376
6448
  if (dataURL) {
6377
- if (this.previewThumbnailCache.size > 20) {
6449
+ if (this.previewThumbnailCache.size >= 20) {
6378
6450
  const firstKey = this.previewThumbnailCache.keys().next().value;
6379
6451
  this.previewThumbnailCache.delete(firstKey);
6380
6452
  }
@@ -6387,19 +6459,32 @@
6387
6459
  * @param {number} time - Time in seconds
6388
6460
  */
6389
6461
  async updatePreviewThumbnail(time) {
6390
- if (!this.previewSupported) {
6462
+ if (!this.previewSupported || !this.controls.progressPreview) {
6391
6463
  return;
6392
6464
  }
6393
6465
  if (this.previewThumbnailTimeout) {
6394
6466
  clearTimeout(this.previewThumbnailTimeout);
6395
6467
  }
6396
6468
  this.previewThumbnailTimeout = setTimeout(async () => {
6397
- const thumbnail = await this.generatePreviewThumbnail(time);
6398
- if (thumbnail && this.controls.progressPreview) {
6399
- this.controls.progressPreview.style.backgroundImage = "url(".concat(thumbnail, ")");
6400
- this.controls.progressPreview.style.display = "block";
6469
+ try {
6470
+ const thumbnail = await this.generatePreviewThumbnail(time);
6471
+ if (thumbnail && this.controls.progressPreview) {
6472
+ this.controls.progressPreview.style.backgroundImage = 'url("'.concat(thumbnail, '")');
6473
+ this.controls.progressPreview.style.display = "block";
6474
+ this.controls.progressPreview.style.backgroundRepeat = "no-repeat";
6475
+ this.controls.progressPreview.style.backgroundPosition = "center";
6476
+ } else {
6477
+ if (this.controls.progressPreview) {
6478
+ this.controls.progressPreview.style.display = "none";
6479
+ }
6480
+ }
6481
+ this.currentPreviewTime = time;
6482
+ } catch (error) {
6483
+ this.player.log("Preview thumbnail update failed:", error, "warn");
6484
+ if (this.controls.progressPreview) {
6485
+ this.controls.progressPreview.style.display = "none";
6486
+ }
6401
6487
  }
6402
- this.currentPreviewTime = time;
6403
6488
  }, 100);
6404
6489
  }
6405
6490
  setupProgressBarEvents() {
@@ -6433,7 +6518,9 @@
6433
6518
  this.controls.progressTooltipTime.textContent = TimeUtils.formatTime(time);
6434
6519
  this.controls.progressTooltip.style.left = "".concat(left, "px");
6435
6520
  this.controls.progressTooltip.style.display = "block";
6436
- this.updatePreviewThumbnail(time);
6521
+ if (this.previewSupported) {
6522
+ this.updatePreviewThumbnail(time);
6523
+ }
6437
6524
  }
6438
6525
  });
6439
6526
  progress.addEventListener("mouseleave", () => {
@@ -7720,6 +7807,10 @@
7720
7807
  this.updateDuration();
7721
7808
  this.ensureQualityButton();
7722
7809
  this.updateQualityIndicator();
7810
+ this.updatePreviewVideoSource();
7811
+ });
7812
+ this.player.on("sourcechange", () => {
7813
+ this.updatePreviewVideoSource();
7723
7814
  });
7724
7815
  this.player.on("volumechange", () => this.updateVolumeDisplay());
7725
7816
  this.player.on("progress", () => this.updateBuffered());
@@ -8187,6 +8278,38 @@
8187
8278
  hide() {
8188
8279
  this.element.style.display = "none";
8189
8280
  }
8281
+ /**
8282
+ * Update preview video source when player source changes (for playlists)
8283
+ * Also re-initializes if preview wasn't set up initially
8284
+ */
8285
+ updatePreviewVideoSource() {
8286
+ var _a;
8287
+ const renderer = this.player.renderer;
8288
+ if (!renderer || !renderer.media || renderer.media.tagName !== "VIDEO") {
8289
+ return;
8290
+ }
8291
+ if (!this.previewSupported && !this.previewVideo) {
8292
+ this.initPreviewThumbnail();
8293
+ }
8294
+ if (!this.previewSupported || !this.previewVideo) {
8295
+ return;
8296
+ }
8297
+ const mainVideo = renderer.media;
8298
+ const newSrc = mainVideo.src || ((_a = mainVideo.querySelector("source")) == null ? void 0 : _a.src);
8299
+ if (newSrc && this.previewVideo.src !== newSrc) {
8300
+ this.previewThumbnailCache.clear();
8301
+ this.previewVideoReady = false;
8302
+ this.previewVideo.src = newSrc;
8303
+ if (mainVideo.crossOrigin) {
8304
+ this.previewVideo.crossOrigin = mainVideo.crossOrigin;
8305
+ }
8306
+ this.previewVideo.addEventListener("loadedmetadata", () => {
8307
+ this.previewVideoReady = true;
8308
+ }, { once: true });
8309
+ } else if (newSrc && !this.previewVideoReady && this.previewVideo.readyState >= 1) {
8310
+ this.previewVideoReady = true;
8311
+ }
8312
+ }
8190
8313
  /**
8191
8314
  * Cleanup preview thumbnail resources
8192
8315
  */
@@ -10281,6 +10404,11 @@
10281
10404
  qualityButton: true,
10282
10405
  captionStyleButton: true,
10283
10406
  speedButton: true,
10407
+ // When enabled, the playback speed UI is suppressed for ALL HLS streams (audio + video).
10408
+ hideSpeedForHls: false,
10409
+ // When enabled, the playback speed UI is suppressed for HLS *video* streams only.
10410
+ // This is useful for live streams where speed controls don't make sense.
10411
+ hideSpeedForHlsVideo: false,
10284
10412
  captionsButton: true,
10285
10413
  transcriptButton: true,
10286
10414
  fullscreenButton: true,