@obipascal/player 1.0.5 → 1.0.7

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.
@@ -1,29 +1,29 @@
1
1
  var V = Object.defineProperty;
2
- var N = (l, t, e) => t in l ? V(l, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[t] = e;
3
- var o = (l, t, e) => N(l, typeof t != "symbol" ? t + "" : t, e);
2
+ var N = (c, t, e) => t in c ? V(c, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[t] = e;
3
+ var r = (c, t, e) => N(c, typeof t != "symbol" ? t + "" : t, e);
4
4
  import p from "hls.js";
5
5
  import { jsx as B } from "react/jsx-runtime";
6
- import * as $ from "react";
7
- import { useRef as w, useEffect as f, useState as b } from "react";
6
+ import * as R from "react";
7
+ import { useRef as y, useEffect as f, useState as b } from "react";
8
8
  class _ {
9
9
  constructor(t) {
10
- o(this, "config");
11
- o(this, "sessionId");
12
- o(this, "events", []);
13
- o(this, "sessionStartTime");
14
- o(this, "playbackStartTime", null);
15
- o(this, "totalPlayTime", 0);
16
- o(this, "totalBufferTime", 0);
17
- o(this, "bufferStartTime", null);
18
- o(this, "rebufferCount", 0);
19
- o(this, "seekCount", 0);
10
+ r(this, "config");
11
+ r(this, "sessionId");
12
+ r(this, "events", []);
13
+ r(this, "sessionStartTime");
14
+ r(this, "playbackStartTime", null);
15
+ r(this, "totalPlayTime", 0);
16
+ r(this, "totalBufferTime", 0);
17
+ r(this, "bufferStartTime", null);
18
+ r(this, "rebufferCount", 0);
19
+ r(this, "seekCount", 0);
20
20
  var e;
21
21
  this.config = t, this.sessionId = (t == null ? void 0 : t.sessionId) || this.generateSessionId(), this.sessionStartTime = Date.now(), (e = this.config) != null && e.enabled && this.trackEvent("session_start", this.getSessionData());
22
22
  }
23
23
  trackEvent(t, e = {}) {
24
- var i;
25
- if (!((i = this.config) != null && i.enabled)) return;
26
- const n = {
24
+ var s;
25
+ if (!((s = this.config) != null && s.enabled)) return;
26
+ const i = {
27
27
  eventType: t,
28
28
  timestamp: Date.now(),
29
29
  sessionId: this.sessionId,
@@ -34,7 +34,7 @@ class _ {
34
34
  ...this.getQoEMetrics()
35
35
  }
36
36
  };
37
- this.events.push(n), this.updateMetrics(t, e), this.config.endpoint && this.sendEvent(n), process.env.NODE_ENV === "development" && console.log("[Analytics]", t, n.data);
37
+ this.events.push(i), this.updateMetrics(t, e), this.config.endpoint && this.sendEvent(i), process.env.NODE_ENV === "development" && console.log("[Analytics]", t, i.data);
38
38
  }
39
39
  updateMetrics(t, e) {
40
40
  switch (t) {
@@ -97,8 +97,8 @@ class _ {
97
97
  },
98
98
  body: JSON.stringify(t)
99
99
  });
100
- } catch (n) {
101
- console.error("Failed to send analytics event:", n);
100
+ } catch (i) {
101
+ console.error("Failed to send analytics event:", i);
102
102
  }
103
103
  }
104
104
  generateSessionId() {
@@ -121,40 +121,43 @@ class _ {
121
121
  }
122
122
  class Q {
123
123
  constructor(t, e) {
124
- o(this, "container");
125
- o(this, "player");
126
- o(this, "controlsContainer");
127
- o(this, "progressContainer");
128
- o(this, "progressBar");
129
- o(this, "playButton");
130
- o(this, "skipBackwardButton");
131
- o(this, "skipForwardButton");
132
- o(this, "volumeButton");
133
- o(this, "fullscreenButton");
134
- o(this, "settingsButton");
124
+ r(this, "container");
125
+ r(this, "player");
126
+ r(this, "controlsContainer");
127
+ r(this, "progressContainer");
128
+ r(this, "progressBar");
129
+ r(this, "playButton");
130
+ r(this, "skipBackwardButton");
131
+ r(this, "skipForwardButton");
132
+ r(this, "volumeButton");
133
+ r(this, "volumeContainer");
134
+ r(this, "fullscreenButton");
135
+ r(this, "pipButton");
136
+ r(this, "settingsButton");
135
137
  // private timeDisplay: HTMLElement
136
- o(this, "volumeSlider");
137
- o(this, "progressInput");
138
+ r(this, "volumeSlider");
139
+ r(this, "progressInput");
138
140
  // private controlsVisible = true
139
- o(this, "hideControlsTimeout", null);
140
- o(this, "stickyControls", !1);
141
- this.container = t, this.player = e, this.injectStyles(), this.createProgressBar(), this.controlsContainer = this.createControls(), this.container.appendChild(this.controlsContainer), this.playButton = this.controlsContainer.querySelector(".wontum-play-btn"), this.skipBackwardButton = this.controlsContainer.querySelector(".wontum-skip-backward-btn"), this.skipForwardButton = this.controlsContainer.querySelector(".wontum-skip-forward-btn"), this.volumeButton = this.controlsContainer.querySelector(".wontum-volume-btn"), this.fullscreenButton = this.controlsContainer.querySelector(".wontum-fullscreen-btn"), this.settingsButton = this.controlsContainer.querySelector(".wontum-settings-btn"), this.volumeSlider = this.controlsContainer.querySelector(".wontum-volume-slider"), this.progressInput = this.container.querySelector(".wontum-progress-input"), this.progressBar = this.container.querySelector(".wontum-progress-filled"), this.stickyControls = this.player.config.stickyControls || !1, this.stickyControls && this.controlsContainer.classList.add("sticky"), this.setupEventListeners(), this.setupPlayerEventListeners();
141
+ r(this, "hideControlsTimeout", null);
142
+ r(this, "stickyControls", !1);
143
+ r(this, "isVolumeSliderActive", !1);
144
+ this.container = t, this.player = e, this.injectStyles(), this.createProgressBar(), this.controlsContainer = this.createControls(), this.container.appendChild(this.controlsContainer), this.playButton = this.controlsContainer.querySelector(".wontum-play-btn"), this.skipBackwardButton = this.controlsContainer.querySelector(".wontum-skip-backward-btn"), this.skipForwardButton = this.controlsContainer.querySelector(".wontum-skip-forward-btn"), this.volumeButton = this.controlsContainer.querySelector(".wontum-volume-btn"), this.volumeContainer = this.controlsContainer.querySelector(".wontum-volume-container"), this.fullscreenButton = this.controlsContainer.querySelector(".wontum-fullscreen-btn"), this.pipButton = this.controlsContainer.querySelector(".wontum-pip-btn"), this.settingsButton = this.controlsContainer.querySelector(".wontum-settings-btn"), this.volumeSlider = this.controlsContainer.querySelector(".wontum-volume-slider"), this.progressInput = this.container.querySelector(".wontum-progress-input"), this.progressBar = this.container.querySelector(".wontum-progress-filled"), this.stickyControls = this.player.config.stickyControls || !1, this.stickyControls && this.controlsContainer.classList.add("sticky"), this.setupEventListeners(), this.setupPlayerEventListeners();
142
145
  }
143
146
  injectStyles() {
144
147
  const t = "wontum-player-styles";
145
148
  if (document.getElementById(t)) return;
146
- const e = this.player.config.theme || {}, n = e.primaryColor || "#3b82f6", i = e.accentColor || "#2563eb", s = e.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", a = e.controlsBackground || "linear-gradient(to top, rgba(0,0,0,0.8), transparent)", r = e.buttonHoverBg || "rgba(255, 255, 255, 0.1)", c = e.progressHeight || "6px", u = e.borderRadius || "4px", m = document.createElement("style");
149
+ const e = this.player.config.theme || {}, i = e.primaryColor || "#3b82f6", s = e.accentColor || "#2563eb", n = e.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", a = e.controlsBackground || "linear-gradient(to top, rgba(0,0,0,0.8), transparent)", o = e.buttonHoverBg || "rgba(255, 255, 255, 0.1)", l = e.progressHeight || "6px", u = e.borderRadius || "4px", m = document.createElement("style");
147
150
  m.id = t, m.textContent = `
148
151
  .wontum-player-container {
149
152
  position: relative;
150
153
  background: #000;
151
- font-family: ${s};
154
+ font-family: ${n};
152
155
  overflow: hidden;
153
- --primary-color: ${n};
154
- --accent-color: ${i};
156
+ --primary-color: ${i};
157
+ --accent-color: ${s};
155
158
  --controls-bg: ${a};
156
- --button-hover: ${r};
157
- --progress-height: ${c};
159
+ --button-hover: ${o};
160
+ --progress-height: ${l};
158
161
  --border-radius: ${u};
159
162
  }
160
163
 
@@ -246,18 +249,20 @@ class Q {
246
249
  .wontum-progress-container {
247
250
  position: absolute;
248
251
  bottom: 58px;
249
- left: 0;
250
- right: 0;
252
+ left: 50%;
253
+ transform: translateX(-50%);
254
+ width: 70%;
255
+ max-width: 600px;
251
256
  height: 5px;
252
257
  cursor: pointer;
253
258
  z-index: 12;
254
- padding: 0 20px;
259
+ padding: 0;
255
260
  transition: height 0.2s ease, opacity 0.3s ease, transform 0.3s ease;
256
261
  }
257
262
 
258
263
  .wontum-progress-container.hidden {
259
264
  opacity: 0;
260
- transform: translateY(100%);
265
+ transform: translateX(-50%) translateY(100%);
261
266
  pointer-events: none;
262
267
  }
263
268
 
@@ -343,6 +348,7 @@ class Q {
343
348
  position: relative;
344
349
  display: flex;
345
350
  align-items: center;
351
+ gap: 0;
346
352
  }
347
353
 
348
354
  .wontum-volume-slider-wrapper {
@@ -354,18 +360,31 @@ class Q {
354
360
  backdrop-filter: blur(10px);
355
361
  padding: 12px 8px;
356
362
  border-radius: 6px;
357
- margin-bottom: 8px;
363
+ margin-bottom: 5px;
358
364
  opacity: 0;
359
365
  pointer-events: none;
360
366
  transition: opacity 0.2s ease;
361
367
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
368
+ z-index: 20;
362
369
  }
363
370
 
364
- .wontum-volume-container:hover .wontum-volume-slider-wrapper {
371
+ .wontum-volume-container:hover .wontum-volume-slider-wrapper,
372
+ .wontum-volume-slider-wrapper:hover {
365
373
  opacity: 1;
366
374
  pointer-events: all;
367
375
  }
368
376
 
377
+ /* Add a bridge area between button and slider to prevent gap */
378
+ .wontum-volume-container::before {
379
+ content: '';
380
+ position: absolute;
381
+ bottom: 100%;
382
+ left: 0;
383
+ right: 0;
384
+ height: 10px;
385
+ background: transparent;
386
+ }
387
+
369
388
  .wontum-volume-slider {
370
389
  -webkit-appearance: slider-vertical;
371
390
  appearance: slider-vertical;
@@ -688,6 +707,10 @@ class Q {
688
707
  </div>
689
708
  </div>
690
709
 
710
+ <button class="wontum-btn wontum-pip-btn" aria-label="Picture-in-Picture">
711
+ ${this.getPipIcon()}
712
+ </button>
713
+
691
714
  <button class="wontum-btn wontum-fullscreen-btn" aria-label="Fullscreen">
692
715
  ${this.getFullscreenIcon()}
693
716
  </button>
@@ -705,27 +728,43 @@ class Q {
705
728
  this.player.skipBackward(10);
706
729
  }), this.skipForwardButton.addEventListener("click", () => {
707
730
  this.player.skipForward(10);
708
- }), this.progressInput.addEventListener("input", (n) => {
709
- const i = n.target, s = parseFloat(i.value), a = this.player.getState(), r = s / 100 * a.duration;
710
- this.player.seek(r);
711
- }), this.volumeSlider.addEventListener("input", (n) => {
712
- const i = n.target, s = parseFloat(i.value) / 100;
713
- this.player.setVolume(s);
731
+ }), this.progressInput.addEventListener("input", (i) => {
732
+ const s = i.target, n = parseFloat(s.value), a = this.player.getState(), o = n / 100 * a.duration;
733
+ this.player.seek(o);
734
+ }), this.volumeSlider.addEventListener("input", (i) => {
735
+ const s = i.target, n = parseFloat(s.value) / 100;
736
+ this.player.setVolume(n);
714
737
  }), this.volumeButton.addEventListener("click", () => {
715
738
  this.player.getState().muted ? this.player.unmute() : this.player.mute();
739
+ }), this.volumeContainer.addEventListener("mouseenter", () => {
740
+ this.isVolumeSliderActive = !0;
741
+ }), this.volumeContainer.addEventListener("mouseleave", () => {
742
+ this.isVolumeSliderActive = !1;
743
+ }), this.volumeSlider.addEventListener("input", () => {
744
+ this.isVolumeSliderActive = !0, this.resetHideControlsTimeout();
745
+ }), this.volumeSlider.addEventListener("change", () => {
746
+ setTimeout(() => {
747
+ this.isVolumeSliderActive = !1;
748
+ }, 500);
716
749
  }), this.fullscreenButton.addEventListener("click", () => {
717
750
  this.player.getState().fullscreen ? this.player.exitFullscreen() : this.player.enterFullscreen();
751
+ }), this.pipButton.addEventListener("click", async () => {
752
+ try {
753
+ await this.player.togglePictureInPicture();
754
+ } catch (i) {
755
+ console.error("PiP error:", i);
756
+ }
718
757
  }), this.settingsButton.addEventListener("click", () => {
719
- const n = this.controlsContainer.querySelector(".wontum-settings-panel");
720
- n.classList.toggle("active"), n.classList.contains("active") && (this.updateSettingsMenu(), this.updateQualityMenu(), this.updateSpeedMenu(), this.updateSubtitleMenu());
758
+ const i = this.controlsContainer.querySelector(".wontum-settings-panel");
759
+ i.classList.toggle("active"), i.classList.contains("active") && (this.updateSettingsMenu(), this.updateQualityMenu(), this.updateSpeedMenu(), this.updateSubtitleMenu());
721
760
  });
722
761
  const t = this.controlsContainer.querySelectorAll(".wontum-tab");
723
- t.forEach((n) => {
724
- n.addEventListener("click", (i) => {
725
- const s = i.currentTarget, a = s.getAttribute("data-tab");
726
- t.forEach((u) => u.classList.remove("active")), s.classList.add("active"), this.controlsContainer.querySelectorAll(".wontum-tab-panel").forEach((u) => u.classList.remove("active"));
727
- const c = this.controlsContainer.querySelector(`[data-panel="${a}"]`);
728
- c == null || c.classList.add("active");
762
+ t.forEach((i) => {
763
+ i.addEventListener("click", (s) => {
764
+ const n = s.currentTarget, a = n.getAttribute("data-tab");
765
+ t.forEach((u) => u.classList.remove("active")), n.classList.add("active"), this.controlsContainer.querySelectorAll(".wontum-tab-panel").forEach((u) => u.classList.remove("active"));
766
+ const l = this.controlsContainer.querySelector(`[data-panel="${a}"]`);
767
+ l == null || l.classList.add("active");
729
768
  });
730
769
  }), this.player.getVideoElement().addEventListener("click", () => {
731
770
  this.player.getState().playing ? this.player.pause() : this.player.play();
@@ -741,19 +780,19 @@ class Q {
741
780
  }), this.player.on("pause", () => {
742
781
  this.playButton.innerHTML = this.getPlayIcon();
743
782
  }), this.player.on("timeupdate", (t) => {
744
- const { currentTime: e } = t.data, n = this.player.getState();
745
- if (n.duration > 0) {
746
- const s = e / n.duration * 100;
747
- this.progressBar.style.width = `${s}%`, this.progressInput.value = s.toString();
783
+ const { currentTime: e } = t.data, i = this.player.getState();
784
+ if (i.duration > 0) {
785
+ const n = e / i.duration * 100;
786
+ this.progressBar.style.width = `${n}%`, this.progressInput.value = n.toString();
748
787
  }
749
- const i = this.controlsContainer.querySelector(".wontum-current-time");
750
- i.textContent = this.formatTime(e);
788
+ const s = this.controlsContainer.querySelector(".wontum-current-time");
789
+ s.textContent = this.formatTime(e);
751
790
  }), this.player.on("loadedmetadata", (t) => {
752
- const { duration: e } = t.data, n = this.controlsContainer.querySelector(".wontum-duration");
753
- n.textContent = this.formatTime(e), t.data.qualities && this.updateQualityMenu(t.data.qualities);
791
+ const { duration: e } = t.data, i = this.controlsContainer.querySelector(".wontum-duration");
792
+ i.textContent = this.formatTime(e), t.data.qualities && this.updateQualityMenu(t.data.qualities);
754
793
  }), this.player.on("volumechange", (t) => {
755
- const { volume: e, muted: n } = t.data;
756
- this.volumeSlider.value = (e * 100).toString(), this.volumeButton.innerHTML = n ? this.getMutedIcon() : this.getVolumeIcon();
794
+ const { volume: e, muted: i } = t.data;
795
+ this.volumeSlider.value = (e * 100).toString(), this.volumeButton.innerHTML = i ? this.getMutedIcon() : this.getVolumeIcon();
757
796
  }), this.player.on("waiting", () => {
758
797
  const t = this.controlsContainer.querySelector(".wontum-loading");
759
798
  t.style.display = "block";
@@ -768,35 +807,35 @@ class Q {
768
807
  t.innerHTML = '<div class="wontum-subtitle-option">No subtitles available</div>';
769
808
  return;
770
809
  }
771
- const n = e.findIndex((i) => i.mode === "showing");
810
+ const i = e.findIndex((s) => s.mode === "showing");
772
811
  t.innerHTML = `
773
- <div class="wontum-subtitle-option ${n === -1 ? "active" : ""}" data-track="-1">Off</div>
812
+ <div class="wontum-subtitle-option ${i === -1 ? "active" : ""}" data-track="-1">Off</div>
774
813
  ${e.map(
775
- (i, s) => `
776
- <div class="wontum-subtitle-option ${s === n ? "active" : ""}" data-track="${s}">
777
- ${i.label || i.language || `Track ${s + 1}`}
814
+ (s, n) => `
815
+ <div class="wontum-subtitle-option ${n === i ? "active" : ""}" data-track="${n}">
816
+ ${s.label || s.language || `Track ${n + 1}`}
778
817
  </div>
779
818
  `
780
819
  ).join("")}
781
- `, t.querySelectorAll(".wontum-subtitle-option").forEach((i) => {
782
- i.addEventListener("click", (s) => {
783
- const a = s.target, r = parseInt(a.dataset.track || "-1");
784
- r === -1 ? this.player.disableSubtitles() : this.player.enableSubtitles(r), t.querySelectorAll(".wontum-subtitle-option").forEach((c) => c.classList.remove("active")), a.classList.add("active");
820
+ `, t.querySelectorAll(".wontum-subtitle-option").forEach((s) => {
821
+ s.addEventListener("click", (n) => {
822
+ const a = n.target, o = parseInt(a.dataset.track || "-1");
823
+ o === -1 ? this.player.disableSubtitles() : this.player.enableSubtitles(o), t.querySelectorAll(".wontum-subtitle-option").forEach((l) => l.classList.remove("active")), a.classList.add("active");
785
824
  });
786
825
  });
787
826
  }
788
827
  updateSpeedMenu() {
789
- const t = this.controlsContainer.querySelector(".wontum-speed-menu"), n = this.player.getState().playbackRate || 1, i = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
790
- t.innerHTML = i.map(
791
- (s) => `
792
- <div class="wontum-speed-option ${n === s ? "active" : ""}" data-speed="${s}">
793
- ${s === 1 ? "Normal" : s + "x"}
828
+ const t = this.controlsContainer.querySelector(".wontum-speed-menu"), i = this.player.getState().playbackRate || 1, s = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
829
+ t.innerHTML = s.map(
830
+ (n) => `
831
+ <div class="wontum-speed-option ${i === n ? "active" : ""}" data-speed="${n}">
832
+ ${n === 1 ? "Normal" : n + "x"}
794
833
  </div>
795
834
  `
796
- ).join(""), t.querySelectorAll(".wontum-speed-option").forEach((s) => {
797
- s.addEventListener("click", (a) => {
798
- const r = a.target, c = parseFloat(r.dataset.speed || "1");
799
- this.player.setPlaybackRate(c), t.querySelectorAll(".wontum-speed-option").forEach((u) => u.classList.remove("active")), r.classList.add("active");
835
+ ).join(""), t.querySelectorAll(".wontum-speed-option").forEach((n) => {
836
+ n.addEventListener("click", (a) => {
837
+ const o = a.target, l = parseFloat(o.dataset.speed || "1");
838
+ this.player.setPlaybackRate(l), t.querySelectorAll(".wontum-speed-option").forEach((u) => u.classList.remove("active")), o.classList.add("active");
800
839
  });
801
840
  });
802
841
  }
@@ -814,22 +853,22 @@ class Q {
814
853
  });
815
854
  }
816
855
  updateQualityMenu(t) {
817
- const e = this.controlsContainer.querySelector(".wontum-quality-menu"), n = t || this.player.getQualities();
818
- if (!n || n.length === 0) {
856
+ const e = this.controlsContainer.querySelector(".wontum-quality-menu"), i = t || this.player.getQualities();
857
+ if (!i || i.length === 0) {
819
858
  e.innerHTML = '<div class="wontum-quality-option">No qualities available</div>';
820
859
  return;
821
860
  }
822
861
  e.innerHTML = `
823
862
  <div class="wontum-quality-option active" data-quality="-1">Auto</div>
824
- ${n.map(
825
- (i, s) => `
826
- <div class="wontum-quality-option" data-quality="${s}">${i.name}</div>
863
+ ${i.map(
864
+ (s, n) => `
865
+ <div class="wontum-quality-option" data-quality="${n}">${s.name}</div>
827
866
  `
828
867
  ).join("")}
829
- `, e.querySelectorAll(".wontum-quality-option").forEach((i) => {
830
- i.addEventListener("click", (s) => {
831
- const a = s.target, r = parseInt(a.dataset.quality || "-1");
832
- this.player.setQuality(r), e.querySelectorAll(".wontum-quality-option").forEach((c) => c.classList.remove("active")), a.classList.add("active");
868
+ `, e.querySelectorAll(".wontum-quality-option").forEach((s) => {
869
+ s.addEventListener("click", (n) => {
870
+ const a = n.target, o = parseInt(a.dataset.quality || "-1");
871
+ this.player.setQuality(o), e.querySelectorAll(".wontum-quality-option").forEach((l) => l.classList.remove("active")), a.classList.add("active");
833
872
  });
834
873
  });
835
874
  }
@@ -837,7 +876,7 @@ class Q {
837
876
  this.controlsContainer.classList.remove("hidden"), this.progressContainer.classList.remove("hidden");
838
877
  }
839
878
  hideControls() {
840
- if (this.stickyControls) return;
879
+ if (this.stickyControls || this.isVolumeSliderActive) return;
841
880
  this.player.getState().playing && (this.controlsContainer.classList.add("hidden"), this.progressContainer.classList.add("hidden"));
842
881
  }
843
882
  resetHideControlsTimeout() {
@@ -847,8 +886,8 @@ class Q {
847
886
  }
848
887
  formatTime(t) {
849
888
  if (isNaN(t)) return "0:00";
850
- const e = Math.floor(t / 60), n = Math.floor(t % 60);
851
- return `${e}:${n.toString().padStart(2, "0")}`;
889
+ const e = Math.floor(t / 60), i = Math.floor(t % 60);
890
+ return `${e}:${i.toString().padStart(2, "0")}`;
852
891
  }
853
892
  // SVG Icons
854
893
  getPlayIcon() {
@@ -866,6 +905,9 @@ class Q {
866
905
  getFullscreenIcon() {
867
906
  return '<svg viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>';
868
907
  }
908
+ getPipIcon() {
909
+ return '<svg viewBox="0 0 24 24"><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/></svg>';
910
+ }
869
911
  getSkipBackwardIcon() {
870
912
  return `<svg viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
871
913
  <circle cx="30" cy="30" r="28" stroke="white" stroke-width="2"/>
@@ -893,9 +935,9 @@ class Q {
893
935
  }
894
936
  class j {
895
937
  constructor(t) {
896
- o(this, "config");
897
- o(this, "urlCache", /* @__PURE__ */ new Map());
898
- o(this, "signedUrls", /* @__PURE__ */ new Set());
938
+ r(this, "config");
939
+ r(this, "urlCache", /* @__PURE__ */ new Map());
940
+ r(this, "signedUrls", /* @__PURE__ */ new Set());
899
941
  this.config = t;
900
942
  }
901
943
  /**
@@ -912,8 +954,8 @@ class j {
912
954
  if (!((e = this.config) != null && e.cloudFrontDomains) || this.config.cloudFrontDomains.length === 0)
913
955
  return !1;
914
956
  try {
915
- const n = new URL(t);
916
- return this.config.cloudFrontDomains.some((i) => n.hostname.includes(i));
957
+ const i = new URL(t);
958
+ return this.config.cloudFrontDomains.some((s) => i.hostname.includes(s));
917
959
  } catch {
918
960
  return !1;
919
961
  }
@@ -929,20 +971,20 @@ class j {
929
971
  * The endpoint should set signed cookies and return the URL
930
972
  */
931
973
  async signCloudFrontUrl(t, e = 0) {
932
- var s, a;
974
+ var n, a;
933
975
  if (this.signedUrls.has(t))
934
976
  return t;
935
- if ((s = this.config) != null && s.signUrl)
977
+ if ((n = this.config) != null && n.signUrl)
936
978
  try {
937
- const r = await this.config.signUrl(t);
938
- return this.signedUrls.add(t), r;
939
- } catch (r) {
940
- const c = (r == null ? void 0 : r.name) === "AbortError" || ((a = r == null ? void 0 : r.message) == null ? void 0 : a.includes("aborted"));
941
- if (c && e < 2)
979
+ const o = await this.config.signUrl(t);
980
+ return this.signedUrls.add(t), o;
981
+ } catch (o) {
982
+ const l = (o == null ? void 0 : o.name) === "AbortError" || ((a = o == null ? void 0 : o.message) == null ? void 0 : a.includes("aborted"));
983
+ if (l && e < 2)
942
984
  return console.warn(`Sign URL aborted, retrying (${e + 1}/2)...`), await new Promise((u) => setTimeout(u, 300)), this.signCloudFrontUrl(t, e + 1);
943
- throw console.error("Failed to sign CloudFront URL:", r), c ? new Error(
985
+ throw console.error("Failed to sign CloudFront URL:", o), l ? new Error(
944
986
  "Failed to sign CloudFront URL: Request was aborted. If using Apollo Client or other GraphQL clients, consider moving the query outside component lifecycle or using useQuery with skip option."
945
- ) : new Error(`Failed to sign CloudFront URL: ${(r == null ? void 0 : r.message) || "Unknown error"}`);
987
+ ) : new Error(`Failed to sign CloudFront URL: ${(o == null ? void 0 : o.message) || "Unknown error"}`);
946
988
  }
947
989
  return console.warn("No signUrl function provided. CloudFront cookies may not be set."), t;
948
990
  }
@@ -955,34 +997,34 @@ class j {
955
997
  const e = t.match(/s3[.-]([^.]+)\.amazonaws\.com\/(.+)/);
956
998
  if (e)
957
999
  return e[2];
958
- const n = t.match(/([^.]+)\.s3\.amazonaws\.com\/(.+)/);
959
- return n ? n[2] : t;
1000
+ const i = t.match(/([^.]+)\.s3\.amazonaws\.com\/(.+)/);
1001
+ return i ? i[2] : t;
960
1002
  }
961
1003
  /**
962
1004
  * Get presigned URL from cache or generate new one
963
1005
  */
964
1006
  async getPresignedUrl(t) {
965
- var i;
966
- const e = this.extractS3Key(t), n = this.urlCache.get(e);
967
- if (n && n.expiresAt > Date.now())
968
- return n.url;
969
- if ((i = this.config) != null && i.getPresignedUrl)
1007
+ var s;
1008
+ const e = this.extractS3Key(t), i = this.urlCache.get(e);
1009
+ if (i && i.expiresAt > Date.now())
1010
+ return i.url;
1011
+ if ((s = this.config) != null && s.getPresignedUrl)
970
1012
  try {
971
- const s = await this.config.getPresignedUrl(e);
1013
+ const n = await this.config.getPresignedUrl(e);
972
1014
  return this.urlCache.set(e, {
973
- url: s,
1015
+ url: n,
974
1016
  expiresAt: Date.now() + 50 * 60 * 1e3
975
- }), s;
976
- } catch (s) {
977
- throw console.error("Failed to generate presigned URL:", s), new Error("Failed to generate presigned URL for S3 object");
1017
+ }), n;
1018
+ } catch (n) {
1019
+ throw console.error("Failed to generate presigned URL:", n), new Error("Failed to generate presigned URL for S3 object");
978
1020
  }
979
1021
  return console.warn("No getPresignedUrl function provided. Using direct S3 URL (requires public bucket)"), t;
980
1022
  }
981
1023
  /**
982
1024
  * Helper to construct S3 URL from bucket and key
983
1025
  */
984
- static constructS3Url(t, e, n = "us-east-1") {
985
- return `https://${t}.s3.${n}.amazonaws.com/${e}`;
1026
+ static constructS3Url(t, e, i = "us-east-1") {
1027
+ return `https://${t}.s3.${i}.amazonaws.com/${e}`;
986
1028
  }
987
1029
  /**
988
1030
  * Helper to parse S3 URI (s3://bucket/key)
@@ -990,8 +1032,8 @@ class j {
990
1032
  static parseS3Uri(t) {
991
1033
  if (!t.startsWith("s3://"))
992
1034
  return null;
993
- const e = t.replace("s3://", "").split("/"), n = e[0], i = e.slice(1).join("/");
994
- return { bucket: n, key: i };
1035
+ const e = t.replace("s3://", "").split("/"), i = e[0], s = e.slice(1).join("/");
1036
+ return { bucket: i, key: s };
995
1037
  }
996
1038
  /**
997
1039
  * Clear URL cache and signed URLs
@@ -1000,18 +1042,18 @@ class j {
1000
1042
  this.urlCache.clear(), this.signedUrls.clear();
1001
1043
  }
1002
1044
  }
1003
- class F {
1045
+ class A {
1004
1046
  constructor(t) {
1005
- o(this, "container");
1006
- o(this, "videoElement");
1007
- o(this, "hls", null);
1008
- o(this, "config");
1009
- o(this, "eventListeners", /* @__PURE__ */ new Map());
1010
- o(this, "analytics");
1011
- o(this, "s3Handler");
1012
- o(this, "uiController");
1013
- o(this, "qualities", []);
1014
- o(this, "state", {
1047
+ r(this, "container");
1048
+ r(this, "videoElement");
1049
+ r(this, "hls", null);
1050
+ r(this, "config");
1051
+ r(this, "eventListeners", /* @__PURE__ */ new Map());
1052
+ r(this, "analytics");
1053
+ r(this, "s3Handler");
1054
+ r(this, "uiController");
1055
+ r(this, "qualities", []);
1056
+ r(this, "state", {
1015
1057
  playing: !1,
1016
1058
  paused: !0,
1017
1059
  ended: !1,
@@ -1031,13 +1073,13 @@ class F {
1031
1073
  }
1032
1074
  addSubtitleTracks(t) {
1033
1075
  t.forEach((e) => {
1034
- const n = document.createElement("track");
1035
- n.kind = "subtitles", n.label = e.label, n.src = e.src, n.srclang = e.srclang, e.default && (n.default = !0), this.videoElement.appendChild(n);
1076
+ const i = document.createElement("track");
1077
+ i.kind = "subtitles", i.label = e.label, i.src = e.src, i.srclang = e.srclang, e.default && (i.default = !0), this.videoElement.appendChild(i);
1036
1078
  });
1037
1079
  }
1038
1080
  createVideoElement() {
1039
1081
  const t = document.createElement("video");
1040
- return t.className = "wontum-player-video", t.style.width = "100%", t.style.height = "100%", t.playsInline = !0, t;
1082
+ return t.className = "wontum-player-video", t.style.width = "100%", t.style.height = "100%", t.playsInline = !0, t.crossOrigin = "use-credentials", t;
1041
1083
  }
1042
1084
  setupVideoListeners() {
1043
1085
  this.videoElement.addEventListener("play", () => {
@@ -1093,25 +1135,33 @@ class F {
1093
1135
  });
1094
1136
  }
1095
1137
  async loadSource(t) {
1138
+ var e;
1096
1139
  try {
1097
- const e = await this.s3Handler.processUrl(t);
1098
- if (p.isSupported())
1099
- this.hls = new p(this.config.hlsConfig), this.hls.loadSource(e), this.hls.attachMedia(this.videoElement), this.hls.on(p.Events.MANIFEST_PARSED, (n, i) => {
1100
- const s = this.extractQualities(i.levels);
1101
- this.qualities = s;
1102
- }), this.hls.on(p.Events.LEVEL_SWITCHED, (n, i) => {
1103
- var a;
1104
- const s = (a = this.hls) == null ? void 0 : a.levels[i.level];
1105
- s && (this.state.quality = `${s.height}p`, this.emit("qualitychange", { quality: this.state.quality }));
1106
- }), this.hls.on(p.Events.ERROR, (n, i) => {
1107
- i.fatal && this.handleHlsError(i);
1140
+ const i = await this.s3Handler.processUrl(t);
1141
+ if (p.isSupported()) {
1142
+ const s = ((e = this.config.s3Config) == null ? void 0 : e.withCredentials) ?? !1, n = {
1143
+ ...this.config.hlsConfig,
1144
+ xhrSetup: (a, o) => {
1145
+ var l;
1146
+ s && (a.withCredentials = !0), (l = this.config.hlsConfig) != null && l.xhrSetup && this.config.hlsConfig.xhrSetup(a, o);
1147
+ }
1148
+ };
1149
+ this.hls = new p(n), this.hls.loadSource(i), this.hls.attachMedia(this.videoElement), this.hls.on(p.Events.MANIFEST_PARSED, (a, o) => {
1150
+ const l = this.extractQualities(o.levels);
1151
+ this.qualities = l;
1152
+ }), this.hls.on(p.Events.LEVEL_SWITCHED, (a, o) => {
1153
+ var u;
1154
+ const l = (u = this.hls) == null ? void 0 : u.levels[o.level];
1155
+ l && (this.state.quality = `${l.height}p`, this.emit("qualitychange", { quality: this.state.quality }));
1156
+ }), this.hls.on(p.Events.ERROR, (a, o) => {
1157
+ o.fatal && this.handleHlsError(o);
1108
1158
  });
1109
- else if (this.videoElement.canPlayType("application/vnd.apple.mpegurl"))
1110
- this.videoElement.src = e;
1159
+ } else if (this.videoElement.canPlayType("application/vnd.apple.mpegurl"))
1160
+ this.videoElement.src = i;
1111
1161
  else
1112
1162
  throw new Error("HLS is not supported in this browser");
1113
- } catch (e) {
1114
- console.error("Failed to load video source:", e), this.emit("error", { error: e });
1163
+ } catch (i) {
1164
+ console.error("Failed to load video source:", i), this.emit("error", { error: i });
1115
1165
  }
1116
1166
  }
1117
1167
  extractQualities(t) {
@@ -1123,13 +1173,13 @@ class F {
1123
1173
  }));
1124
1174
  }
1125
1175
  handleHlsError(t) {
1126
- var e, n;
1176
+ var e, i;
1127
1177
  switch (t.type) {
1128
1178
  case p.ErrorTypes.NETWORK_ERROR:
1129
1179
  console.error("Network error occurred"), (e = this.hls) == null || e.startLoad();
1130
1180
  break;
1131
1181
  case p.ErrorTypes.MEDIA_ERROR:
1132
- console.error("Media error occurred"), (n = this.hls) == null || n.recoverMediaError();
1182
+ console.error("Media error occurred"), (i = this.hls) == null || i.recoverMediaError();
1133
1183
  break;
1134
1184
  default:
1135
1185
  console.error("Fatal error occurred:", t), this.destroy();
@@ -1188,6 +1238,25 @@ class F {
1188
1238
  exitFullscreen() {
1189
1239
  document.exitFullscreen && (document.exitFullscreen(), this.state.fullscreen = !1, this.emit("fullscreenchange", { fullscreen: !1 }));
1190
1240
  }
1241
+ async enterPictureInPicture() {
1242
+ if (document.pictureInPictureEnabled && !this.videoElement.disablePictureInPicture)
1243
+ try {
1244
+ await this.videoElement.requestPictureInPicture(), this.emit("pictureinpictureenter", {});
1245
+ } catch (t) {
1246
+ throw console.error("Failed to enter Picture-in-Picture:", t), t;
1247
+ }
1248
+ }
1249
+ async exitPictureInPicture() {
1250
+ if (document.pictureInPictureElement)
1251
+ try {
1252
+ await document.exitPictureInPicture(), this.emit("pictureinpictureexit", {});
1253
+ } catch (t) {
1254
+ throw console.error("Failed to exit Picture-in-Picture:", t), t;
1255
+ }
1256
+ }
1257
+ async togglePictureInPicture() {
1258
+ document.pictureInPictureElement ? await this.exitPictureInPicture() : await this.enterPictureInPicture();
1259
+ }
1191
1260
  getState() {
1192
1261
  return { ...this.state };
1193
1262
  }
@@ -1199,8 +1268,8 @@ class F {
1199
1268
  */
1200
1269
  enableSubtitles(t) {
1201
1270
  const e = this.videoElement.textTracks;
1202
- for (let n = 0; n < e.length; n++)
1203
- e[n].mode = n === t ? "showing" : "hidden";
1271
+ for (let i = 0; i < e.length; i++)
1272
+ e[i].mode = i === t ? "showing" : "hidden";
1204
1273
  }
1205
1274
  /**
1206
1275
  * Disable all subtitles
@@ -1215,7 +1284,7 @@ class F {
1215
1284
  */
1216
1285
  toggleSubtitles() {
1217
1286
  const t = this.videoElement.textTracks;
1218
- return Array.from(t).some((n) => n.mode === "showing") ? (this.disableSubtitles(), !1) : t.length > 0 ? (this.enableSubtitles(0), !0) : !1;
1287
+ return Array.from(t).some((i) => i.mode === "showing") ? (this.disableSubtitles(), !1) : t.length > 0 ? (this.enableSubtitles(0), !0) : !1;
1219
1288
  }
1220
1289
  /**
1221
1290
  * Get available subtitle tracks
@@ -1234,38 +1303,38 @@ class F {
1234
1303
  this.eventListeners.has(t) || this.eventListeners.set(t, /* @__PURE__ */ new Set()), this.eventListeners.get(t).add(e);
1235
1304
  }
1236
1305
  off(t, e) {
1237
- var n;
1238
- (n = this.eventListeners.get(t)) == null || n.delete(e);
1306
+ var i;
1307
+ (i = this.eventListeners.get(t)) == null || i.delete(e);
1239
1308
  }
1240
1309
  emit(t, e) {
1241
- var i;
1242
- const n = {
1310
+ var s;
1311
+ const i = {
1243
1312
  type: t,
1244
1313
  data: e,
1245
1314
  timestamp: Date.now()
1246
1315
  };
1247
- (i = this.eventListeners.get(t)) == null || i.forEach((s) => {
1248
- s(n);
1316
+ (s = this.eventListeners.get(t)) == null || s.forEach((n) => {
1317
+ n(i);
1249
1318
  });
1250
1319
  }
1251
1320
  destroy() {
1252
1321
  this.hls && (this.hls.destroy(), this.hls = null), this.uiController.destroy(), this.videoElement.remove(), this.eventListeners.clear(), this.analytics.destroy();
1253
1322
  }
1254
1323
  }
1255
- const K = (l) => {
1324
+ const K = (c) => {
1256
1325
  const {
1257
1326
  src: t,
1258
1327
  autoplay: e,
1259
- muted: n,
1260
- controls: i = !0,
1261
- poster: s,
1328
+ muted: i,
1329
+ controls: s = !0,
1330
+ poster: n,
1262
1331
  preload: a,
1263
- theme: r,
1264
- s3Config: c,
1332
+ theme: o,
1333
+ s3Config: l,
1265
1334
  analytics: u,
1266
1335
  hlsConfig: m,
1267
- subtitles: P,
1268
- stickyControls: U,
1336
+ subtitles: $,
1337
+ stickyControls: z,
1269
1338
  onReady: k,
1270
1339
  onPlay: E,
1271
1340
  onPause: x,
@@ -1274,90 +1343,90 @@ const K = (l) => {
1274
1343
  onVolumeChange: L,
1275
1344
  onError: T,
1276
1345
  onLoadedMetadata: q,
1277
- onQualityChange: M,
1278
- style: z,
1279
- className: D,
1346
+ onQualityChange: P,
1347
+ style: U,
1348
+ className: H,
1280
1349
  width: g = "100%",
1281
1350
  height: v = "500px"
1282
- } = l, y = w(null), R = w(null);
1351
+ } = c, w = y(null), I = y(null);
1283
1352
  return f(() => {
1284
- if (!y.current) return;
1285
- const H = {
1353
+ if (!w.current) return;
1354
+ const D = {
1286
1355
  src: t,
1287
- container: y.current,
1356
+ container: w.current,
1288
1357
  autoplay: e,
1289
- muted: n,
1290
- controls: i,
1291
- poster: s,
1358
+ muted: i,
1359
+ controls: s,
1360
+ poster: n,
1292
1361
  preload: a,
1293
- theme: r,
1294
- s3Config: c,
1362
+ theme: o,
1363
+ s3Config: l,
1295
1364
  analytics: u,
1296
1365
  hlsConfig: m,
1297
- subtitles: P,
1298
- stickyControls: U
1299
- }, d = new F(H);
1300
- return R.current = d, E && d.on("play", E), x && d.on("pause", x), S && d.on("ended", S), T && d.on("error", (h) => {
1301
- var I;
1302
- return T((I = h.data) == null ? void 0 : I.error);
1303
- }), q && d.on("loadedmetadata", q), M && d.on("qualitychange", (h) => M(h.data.level)), C && d.on("timeupdate", (h) => C(h.data.currentTime)), L && d.on("volumechange", (h) => L(h.data.volume, h.data.muted)), k && k(d), () => {
1304
- d.destroy(), R.current = null;
1366
+ subtitles: $,
1367
+ stickyControls: z
1368
+ }, d = new A(D);
1369
+ return I.current = d, E && d.on("play", E), x && d.on("pause", x), S && d.on("ended", S), T && d.on("error", (h) => {
1370
+ var M;
1371
+ return T((M = h.data) == null ? void 0 : M.error);
1372
+ }), q && d.on("loadedmetadata", q), P && d.on("qualitychange", (h) => P(h.data.level)), C && d.on("timeupdate", (h) => C(h.data.currentTime)), L && d.on("volumechange", (h) => L(h.data.volume, h.data.muted)), k && k(d), () => {
1373
+ d.destroy(), I.current = null;
1305
1374
  };
1306
1375
  }, [t]), /* @__PURE__ */ B(
1307
1376
  "div",
1308
1377
  {
1309
- ref: y,
1310
- className: D,
1378
+ ref: w,
1379
+ className: H,
1311
1380
  style: {
1312
1381
  width: typeof g == "number" ? `${g}px` : g,
1313
1382
  height: typeof v == "number" ? `${v}px` : v,
1314
- ...z
1383
+ ...U
1315
1384
  }
1316
1385
  }
1317
1386
  );
1318
- }, G = (l) => {
1319
- const [t, e] = b(null), [n, i] = b(null), s = w(null);
1387
+ }, G = (c) => {
1388
+ const [t, e] = b(null), [i, s] = b(null), n = y(null);
1320
1389
  return f(() => {
1321
- if (!s.current) return;
1322
- const a = new F({
1323
- ...l,
1324
- container: s.current
1390
+ if (!n.current) return;
1391
+ const a = new A({
1392
+ ...c,
1393
+ container: n.current
1325
1394
  });
1326
1395
  e(a);
1327
- const r = () => {
1328
- i(a.getState());
1396
+ const o = () => {
1397
+ s(a.getState());
1329
1398
  };
1330
- return a.on("play", r), a.on("pause", r), a.on("timeupdate", r), a.on("volumechange", r), a.on("loadedmetadata", r), () => {
1399
+ return a.on("play", o), a.on("pause", o), a.on("timeupdate", o), a.on("volumechange", o), a.on("loadedmetadata", o), () => {
1331
1400
  a.destroy();
1332
1401
  };
1333
- }, [l.src]), {
1334
- containerRef: s,
1402
+ }, [c.src]), {
1403
+ containerRef: n,
1335
1404
  player: t,
1336
- state: n
1405
+ state: i
1337
1406
  };
1338
- }, A = $.createContext({
1407
+ }, F = R.createContext({
1339
1408
  player: null,
1340
1409
  state: null
1341
- }), J = (l) => {
1342
- const { player: t, children: e } = l, [n, i] = b(t.getState());
1410
+ }), J = (c) => {
1411
+ const { player: t, children: e } = c, [i, s] = b(t.getState());
1343
1412
  return f(() => {
1344
- const s = () => {
1345
- i(t.getState());
1413
+ const n = () => {
1414
+ s(t.getState());
1346
1415
  };
1347
- return t.on("play", s), t.on("pause", s), t.on("timeupdate", s), t.on("volumechange", s), t.on("loadedmetadata", s), () => {
1416
+ return t.on("play", n), t.on("pause", n), t.on("timeupdate", n), t.on("volumechange", n), t.on("loadedmetadata", n), () => {
1348
1417
  };
1349
- }, [t]), /* @__PURE__ */ B(A.Provider, { value: { player: t, state: n }, children: e });
1418
+ }, [t]), /* @__PURE__ */ B(F.Provider, { value: { player: t, state: i }, children: e });
1350
1419
  }, Z = () => {
1351
- const l = $.useContext(A);
1352
- if (!l.player)
1420
+ const c = R.useContext(F);
1421
+ if (!c.player)
1353
1422
  throw new Error("useWontumPlayerContext must be used within WontumPlayerProvider");
1354
- return l;
1423
+ return c;
1355
1424
  };
1356
1425
  export {
1357
1426
  _ as Analytics,
1358
1427
  j as S3Handler,
1359
1428
  Q as UIController,
1360
- F as WontumPlayer,
1429
+ A as WontumPlayer,
1361
1430
  J as WontumPlayerProvider,
1362
1431
  K as WontumPlayerReact,
1363
1432
  G as useWontumPlayer,