@shortkitsdk/web 0.3.1 → 0.3.2

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/shortkit.cjs CHANGED
@@ -33544,16 +33544,35 @@ class PlayerPool {
33544
33544
  player._skCurrentUrl = streamingUrl;
33545
33545
  }
33546
33546
  /**
33547
- * Promote a preload player to active: raise buffer limits and uncap ABR.
33548
- * Safe to call even if the HLS instance was already created with active config.
33547
+ * Promote a preload player to active: raise buffer limits, uncap ABR, and
33548
+ * force an immediate quality upgrade.
33549
+ *
33550
+ * Preload streams start at level 0 (lowest rendition) to conserve bandwidth.
33551
+ * When attachStream() is called again with isActive=true for the same URL it
33552
+ * early-returns, so the HLS instance keeps its startLevel:0 config. The
33553
+ * already-buffered low-quality segments play through before any higher-quality
33554
+ * segments arrive — causing a visible low-res start even on fast connections.
33555
+ *
33556
+ * Fix: lock HLS to the highest level, then seek-in-place to flush the
33557
+ * low-quality buffer so the player immediately re-fetches at high quality.
33558
+ * Re-enable ABR after a few seconds so slow connections self-correct.
33549
33559
  */
33550
33560
  promoteToActive(itemId) {
33551
33561
  const hls = this.hlsInstances.get(itemId);
33552
- if (hls) {
33553
- hls.config.maxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
33554
- hls.config.maxMaxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
33555
- hls.autoLevelCapping = -1;
33556
- hls.nextAutoLevel = -1;
33562
+ if (!hls) return;
33563
+ hls.config.maxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
33564
+ hls.config.maxMaxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
33565
+ hls.autoLevelCapping = -1;
33566
+ const top = hls.levels ? hls.levels.length - 1 : -1;
33567
+ if (top > 0 && hls.currentLevel < top) {
33568
+ hls.currentLevel = top;
33569
+ const player = this.assignments.get(itemId);
33570
+ if (player) {
33571
+ player.currentTime = player.currentTime;
33572
+ }
33573
+ setTimeout(() => {
33574
+ if (!hls.destroyed) hls.currentLevel = -1;
33575
+ }, 4e3);
33557
33576
  }
33558
33577
  }
33559
33578
  /** Pause, destroy HLS, and remove player from DOM and pool tracking. */
@@ -34586,6 +34605,7 @@ class EmbeddedFeedManager {
34586
34605
  this._setupScrollEnd();
34587
34606
  this._setupVisibilityHandler();
34588
34607
  this._setupCellHeightObserver();
34608
+ this._setupDebugPanel();
34589
34609
  if (startIndex > 0 && startIndex < items.length) {
34590
34610
  const targetEl = this.itemEls.get(items[startIndex].id);
34591
34611
  if (targetEl) targetEl.scrollIntoView({ behavior: "instant", block: "start" });
@@ -34608,6 +34628,11 @@ class EmbeddedFeedManager {
34608
34628
  this._resizeObserver = null;
34609
34629
  }
34610
34630
  if (this._visHandler) document.removeEventListener("visibilitychange", this._visHandler);
34631
+ if (this._debugKeyHandler) document.removeEventListener("keydown", this._debugKeyHandler);
34632
+ if (this._debugPanel) {
34633
+ this._debugPanel.remove();
34634
+ this._debugPanel = null;
34635
+ }
34611
34636
  this.container.innerHTML = "";
34612
34637
  this.itemEls.clear();
34613
34638
  this.items = [];
@@ -34941,12 +34966,55 @@ class EmbeddedFeedManager {
34941
34966
  this._stopTimeLoop(id);
34942
34967
  this._detachOverlay(id);
34943
34968
  }
34969
+ // --- Debug panel (toggle with 'D' key) ---
34970
+ _setupDebugPanel() {
34971
+ const panel = document.createElement("div");
34972
+ panel.id = "sk-debug-panel";
34973
+ panel.style.cssText = "position:fixed;top:12px;right:12px;z-index:99999;background:rgba(0,0,0,.85);color:#0f0;font:12px/1.5 monospace;padding:10px 14px;border-radius:8px;pointer-events:none;display:none;min-width:220px;";
34974
+ document.body.appendChild(panel);
34975
+ this._debugPanel = panel;
34976
+ this._debugVisible = false;
34977
+ this._debugKeyHandler = (e) => {
34978
+ if (e.key === "d" || e.key === "D") {
34979
+ this._debugVisible = !this._debugVisible;
34980
+ panel.style.display = this._debugVisible ? "block" : "none";
34981
+ }
34982
+ };
34983
+ document.addEventListener("keydown", this._debugKeyHandler);
34984
+ }
34985
+ _updateDebugPanel() {
34986
+ if (!this._debugVisible || !this._debugPanel) return;
34987
+ const hls = this.pool.hlsInstances.get(this.activeItemId);
34988
+ if (!hls || !hls.levels || !hls.levels.length) {
34989
+ this._debugPanel.innerHTML = "HLS: no levels loaded";
34990
+ return;
34991
+ }
34992
+ const level = hls.levels[hls.currentLevel] || {};
34993
+ const bw = hls.bandwidthEstimate ? (hls.bandwidthEstimate / 1e6).toFixed(2) : "?";
34994
+ const lines = [
34995
+ `<b style="color:#fff">HLS Debug</b>`,
34996
+ `Level: ${hls.currentLevel} / ${hls.levels.length - 1}`,
34997
+ `Resolution: ${level.width || "?"}x${level.height || "?"}`,
34998
+ `Bitrate: ${level.bitrate ? (level.bitrate / 1e3).toFixed(0) + " kbps" : "?"}`,
34999
+ `BW estimate: ${bw} Mbps`,
35000
+ `Auto cap: ${hls.autoLevelCapping}`,
35001
+ `Next level: ${hls.nextLevel}`,
35002
+ `<span style="color:#888">Levels:</span>`
35003
+ ];
35004
+ for (let i = 0; i < hls.levels.length; i++) {
35005
+ const l = hls.levels[i];
35006
+ const marker = i === hls.currentLevel ? ' <b style="color:#0ff">◀</b>' : "";
35007
+ lines.push(` ${i}: ${l.width}x${l.height} @ ${(l.bitrate / 1e3).toFixed(0)}k${marker}`);
35008
+ }
35009
+ this._debugPanel.innerHTML = lines.join("<br>");
35010
+ }
34944
35011
  // --- Time loop (state emission only, no DOM updates) ---
34945
35012
  _startTimeLoop(id, el, player) {
34946
35013
  this._stopTimeLoop(id);
34947
35014
  const tick = () => {
34948
35015
  if (this._destroyed) return;
34949
35016
  if (!player || player.paused) {
35017
+ this._updateDebugPanel();
34950
35018
  this.rafIds.set(id, requestAnimationFrame(tick));
34951
35019
  return;
34952
35020
  }
@@ -34972,6 +35040,7 @@ class EmbeddedFeedManager {
34972
35040
  player.play().catch(() => {
34973
35041
  });
34974
35042
  }
35043
+ this._updateDebugPanel();
34975
35044
  this.rafIds.set(id, requestAnimationFrame(tick));
34976
35045
  };
34977
35046
  this.rafIds.set(id, requestAnimationFrame(tick));
@@ -36037,11 +36106,20 @@ class WidgetPlayerPool {
36037
36106
  }
36038
36107
  promoteToActive(itemId) {
36039
36108
  const hls = this.hlsInstances.get(itemId);
36040
- if (hls) {
36041
- hls.config.maxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
36042
- hls.config.maxMaxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
36043
- hls.autoLevelCapping = -1;
36044
- hls.nextAutoLevel = -1;
36109
+ if (!hls) return;
36110
+ hls.config.maxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
36111
+ hls.config.maxMaxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
36112
+ hls.autoLevelCapping = -1;
36113
+ const top = hls.levels ? hls.levels.length - 1 : -1;
36114
+ if (top > 0 && hls.currentLevel < top) {
36115
+ hls.currentLevel = top;
36116
+ const player = this.assignments.get(itemId);
36117
+ if (player) {
36118
+ player.currentTime = player.currentTime;
36119
+ }
36120
+ setTimeout(() => {
36121
+ if (!hls.destroyed) hls.currentLevel = -1;
36122
+ }, 4e3);
36045
36123
  }
36046
36124
  }
36047
36125
  _destroyHls(itemId) {
package/dist/shortkit.js CHANGED
@@ -33546,16 +33546,35 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
33546
33546
  player._skCurrentUrl = streamingUrl;
33547
33547
  }
33548
33548
  /**
33549
- * Promote a preload player to active: raise buffer limits and uncap ABR.
33550
- * Safe to call even if the HLS instance was already created with active config.
33549
+ * Promote a preload player to active: raise buffer limits, uncap ABR, and
33550
+ * force an immediate quality upgrade.
33551
+ *
33552
+ * Preload streams start at level 0 (lowest rendition) to conserve bandwidth.
33553
+ * When attachStream() is called again with isActive=true for the same URL it
33554
+ * early-returns, so the HLS instance keeps its startLevel:0 config. The
33555
+ * already-buffered low-quality segments play through before any higher-quality
33556
+ * segments arrive — causing a visible low-res start even on fast connections.
33557
+ *
33558
+ * Fix: lock HLS to the highest level, then seek-in-place to flush the
33559
+ * low-quality buffer so the player immediately re-fetches at high quality.
33560
+ * Re-enable ABR after a few seconds so slow connections self-correct.
33551
33561
  */
33552
33562
  promoteToActive(itemId) {
33553
33563
  const hls = this.hlsInstances.get(itemId);
33554
- if (hls) {
33555
- hls.config.maxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
33556
- hls.config.maxMaxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
33557
- hls.autoLevelCapping = -1;
33558
- hls.nextAutoLevel = -1;
33564
+ if (!hls) return;
33565
+ hls.config.maxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
33566
+ hls.config.maxMaxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
33567
+ hls.autoLevelCapping = -1;
33568
+ const top = hls.levels ? hls.levels.length - 1 : -1;
33569
+ if (top > 0 && hls.currentLevel < top) {
33570
+ hls.currentLevel = top;
33571
+ const player = this.assignments.get(itemId);
33572
+ if (player) {
33573
+ player.currentTime = player.currentTime;
33574
+ }
33575
+ setTimeout(() => {
33576
+ if (!hls.destroyed) hls.currentLevel = -1;
33577
+ }, 4e3);
33559
33578
  }
33560
33579
  }
33561
33580
  /** Pause, destroy HLS, and remove player from DOM and pool tracking. */
@@ -34588,6 +34607,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
34588
34607
  this._setupScrollEnd();
34589
34608
  this._setupVisibilityHandler();
34590
34609
  this._setupCellHeightObserver();
34610
+ this._setupDebugPanel();
34591
34611
  if (startIndex > 0 && startIndex < items.length) {
34592
34612
  const targetEl = this.itemEls.get(items[startIndex].id);
34593
34613
  if (targetEl) targetEl.scrollIntoView({ behavior: "instant", block: "start" });
@@ -34610,6 +34630,11 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
34610
34630
  this._resizeObserver = null;
34611
34631
  }
34612
34632
  if (this._visHandler) document.removeEventListener("visibilitychange", this._visHandler);
34633
+ if (this._debugKeyHandler) document.removeEventListener("keydown", this._debugKeyHandler);
34634
+ if (this._debugPanel) {
34635
+ this._debugPanel.remove();
34636
+ this._debugPanel = null;
34637
+ }
34613
34638
  this.container.innerHTML = "";
34614
34639
  this.itemEls.clear();
34615
34640
  this.items = [];
@@ -34943,12 +34968,55 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
34943
34968
  this._stopTimeLoop(id);
34944
34969
  this._detachOverlay(id);
34945
34970
  }
34971
+ // --- Debug panel (toggle with 'D' key) ---
34972
+ _setupDebugPanel() {
34973
+ const panel = document.createElement("div");
34974
+ panel.id = "sk-debug-panel";
34975
+ panel.style.cssText = "position:fixed;top:12px;right:12px;z-index:99999;background:rgba(0,0,0,.85);color:#0f0;font:12px/1.5 monospace;padding:10px 14px;border-radius:8px;pointer-events:none;display:none;min-width:220px;";
34976
+ document.body.appendChild(panel);
34977
+ this._debugPanel = panel;
34978
+ this._debugVisible = false;
34979
+ this._debugKeyHandler = (e) => {
34980
+ if (e.key === "d" || e.key === "D") {
34981
+ this._debugVisible = !this._debugVisible;
34982
+ panel.style.display = this._debugVisible ? "block" : "none";
34983
+ }
34984
+ };
34985
+ document.addEventListener("keydown", this._debugKeyHandler);
34986
+ }
34987
+ _updateDebugPanel() {
34988
+ if (!this._debugVisible || !this._debugPanel) return;
34989
+ const hls = this.pool.hlsInstances.get(this.activeItemId);
34990
+ if (!hls || !hls.levels || !hls.levels.length) {
34991
+ this._debugPanel.innerHTML = "HLS: no levels loaded";
34992
+ return;
34993
+ }
34994
+ const level = hls.levels[hls.currentLevel] || {};
34995
+ const bw = hls.bandwidthEstimate ? (hls.bandwidthEstimate / 1e6).toFixed(2) : "?";
34996
+ const lines = [
34997
+ `<b style="color:#fff">HLS Debug</b>`,
34998
+ `Level: ${hls.currentLevel} / ${hls.levels.length - 1}`,
34999
+ `Resolution: ${level.width || "?"}x${level.height || "?"}`,
35000
+ `Bitrate: ${level.bitrate ? (level.bitrate / 1e3).toFixed(0) + " kbps" : "?"}`,
35001
+ `BW estimate: ${bw} Mbps`,
35002
+ `Auto cap: ${hls.autoLevelCapping}`,
35003
+ `Next level: ${hls.nextLevel}`,
35004
+ `<span style="color:#888">Levels:</span>`
35005
+ ];
35006
+ for (let i = 0; i < hls.levels.length; i++) {
35007
+ const l = hls.levels[i];
35008
+ const marker = i === hls.currentLevel ? ' <b style="color:#0ff">◀</b>' : "";
35009
+ lines.push(` ${i}: ${l.width}x${l.height} @ ${(l.bitrate / 1e3).toFixed(0)}k${marker}`);
35010
+ }
35011
+ this._debugPanel.innerHTML = lines.join("<br>");
35012
+ }
34946
35013
  // --- Time loop (state emission only, no DOM updates) ---
34947
35014
  _startTimeLoop(id, el, player) {
34948
35015
  this._stopTimeLoop(id);
34949
35016
  const tick = () => {
34950
35017
  if (this._destroyed) return;
34951
35018
  if (!player || player.paused) {
35019
+ this._updateDebugPanel();
34952
35020
  this.rafIds.set(id, requestAnimationFrame(tick));
34953
35021
  return;
34954
35022
  }
@@ -34974,6 +35042,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
34974
35042
  player.play().catch(() => {
34975
35043
  });
34976
35044
  }
35045
+ this._updateDebugPanel();
34977
35046
  this.rafIds.set(id, requestAnimationFrame(tick));
34978
35047
  };
34979
35048
  this.rafIds.set(id, requestAnimationFrame(tick));
@@ -36039,11 +36108,20 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
36039
36108
  }
36040
36109
  promoteToActive(itemId) {
36041
36110
  const hls = this.hlsInstances.get(itemId);
36042
- if (hls) {
36043
- hls.config.maxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
36044
- hls.config.maxMaxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
36045
- hls.autoLevelCapping = -1;
36046
- hls.nextAutoLevel = -1;
36111
+ if (!hls) return;
36112
+ hls.config.maxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
36113
+ hls.config.maxMaxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
36114
+ hls.autoLevelCapping = -1;
36115
+ const top = hls.levels ? hls.levels.length - 1 : -1;
36116
+ if (top > 0 && hls.currentLevel < top) {
36117
+ hls.currentLevel = top;
36118
+ const player = this.assignments.get(itemId);
36119
+ if (player) {
36120
+ player.currentTime = player.currentTime;
36121
+ }
36122
+ setTimeout(() => {
36123
+ if (!hls.destroyed) hls.currentLevel = -1;
36124
+ }, 4e3);
36047
36125
  }
36048
36126
  }
36049
36127
  _destroyHls(itemId) {