@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.
- package/README.md +70 -4
- package/dist/src/player.d.ts +3 -0
- package/dist/src/types.d.ts +3 -1
- package/dist/src/ui-controller.d.ts +4 -0
- package/dist/wontum-player.cjs.js +42 -22
- package/dist/wontum-player.esm.js +295 -226
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -240,6 +240,7 @@ function VideoPlayer({ videoUrl }: VideoPlayerProps) {
|
|
|
240
240
|
|
|
241
241
|
const s3config: S3Config = {
|
|
242
242
|
cloudFrontDomains: [url.hostname],
|
|
243
|
+
withCredentials: true, // Enable cookies for CloudFront signed cookies
|
|
243
244
|
signUrl: async (resourceUrl: string) => {
|
|
244
245
|
try {
|
|
245
246
|
const { data } = await refetch({
|
|
@@ -336,6 +337,7 @@ async function initializePlayer() {
|
|
|
336
337
|
container: "#player",
|
|
337
338
|
s3Config: {
|
|
338
339
|
cloudFrontDomains: ["media.yourdomain.com"], // Your CloudFront domain
|
|
340
|
+
withCredentials: true, // Enable cookies for all HLS requests (required for CloudFront signed cookies)
|
|
339
341
|
signUrl: async (url) => {
|
|
340
342
|
// This function is called when player needs to access a video
|
|
341
343
|
// Call your backend to refresh/set cookies if needed
|
|
@@ -734,9 +736,19 @@ interface WontumPlayerConfig {
|
|
|
734
736
|
poster?: string // Poster image URL
|
|
735
737
|
preload?: "none" | "metadata" | "auto" // Preload strategy
|
|
736
738
|
theme?: PlayerTheme // Custom theme
|
|
737
|
-
s3Config?: S3Config // S3 configuration
|
|
739
|
+
s3Config?: S3Config // S3/CloudFront configuration
|
|
738
740
|
analytics?: AnalyticsConfig // Analytics configuration
|
|
739
741
|
hlsConfig?: Partial<any> // HLS.js config override
|
|
742
|
+
subtitles?: SubtitleTrack[] // Subtitle tracks
|
|
743
|
+
stickyControls?: boolean // Keep controls always visible
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
interface S3Config {
|
|
747
|
+
signUrl?: (url: string) => Promise<string> // Sign URL and set cookies
|
|
748
|
+
cloudFrontDomains?: string[] // CloudFront domains (e.g., ['media.example.com'])
|
|
749
|
+
withCredentials?: boolean // Enable cookies for HLS requests (default: false, required for CloudFront signed cookies)
|
|
750
|
+
region?: string // S3 region
|
|
751
|
+
endpoint?: string // Custom S3 endpoint
|
|
740
752
|
}
|
|
741
753
|
```
|
|
742
754
|
|
|
@@ -758,11 +770,17 @@ player.setPlaybackRate(rate: number): void // 0.5, 1, 1.5, 2, etc.
|
|
|
758
770
|
|
|
759
771
|
// Quality control
|
|
760
772
|
player.setQuality(qualityIndex: number): void
|
|
773
|
+
player.getQualities(): QualityLevel[]
|
|
761
774
|
|
|
762
775
|
// Fullscreen
|
|
763
776
|
player.enterFullscreen(): void
|
|
764
777
|
player.exitFullscreen(): void
|
|
765
778
|
|
|
779
|
+
// Picture-in-Picture
|
|
780
|
+
player.enterPictureInPicture(): Promise<void>
|
|
781
|
+
player.exitPictureInPicture(): Promise<void>
|
|
782
|
+
player.togglePictureInPicture(): Promise<void>
|
|
783
|
+
|
|
766
784
|
// State
|
|
767
785
|
player.getState(): PlayerState
|
|
768
786
|
|
|
@@ -792,6 +810,8 @@ type PlayerEventType =
|
|
|
792
810
|
| "error"
|
|
793
811
|
| "qualitychange"
|
|
794
812
|
| "fullscreenchange"
|
|
813
|
+
| "pictureinpictureenter"
|
|
814
|
+
| "pictureinpictureexit"
|
|
795
815
|
```
|
|
796
816
|
|
|
797
817
|
### React Components
|
|
@@ -994,6 +1014,15 @@ player.on("qualitychange", (event) => {
|
|
|
994
1014
|
console.log("Quality changed to:", event.data.quality)
|
|
995
1015
|
})
|
|
996
1016
|
|
|
1017
|
+
// Picture-in-Picture events
|
|
1018
|
+
player.on("pictureinpictureenter", () => {
|
|
1019
|
+
console.log("Entered Picture-in-Picture mode")
|
|
1020
|
+
})
|
|
1021
|
+
|
|
1022
|
+
player.on("pictureinpictureexit", () => {
|
|
1023
|
+
console.log("Exited Picture-in-Picture mode")
|
|
1024
|
+
})
|
|
1025
|
+
|
|
997
1026
|
// Buffer events
|
|
998
1027
|
player.on("waiting", () => console.log("Buffering..."))
|
|
999
1028
|
player.on("canplay", () => console.log("Ready to play"))
|
|
@@ -1065,10 +1094,46 @@ player.setQuality(2) // Set to quality index 2
|
|
|
1065
1094
|
player.enterFullscreen()
|
|
1066
1095
|
player.exitFullscreen()
|
|
1067
1096
|
|
|
1097
|
+
// Picture-in-Picture
|
|
1098
|
+
await player.enterPictureInPicture()
|
|
1099
|
+
await player.exitPictureInPicture()
|
|
1100
|
+
await player.togglePictureInPicture()
|
|
1101
|
+
|
|
1068
1102
|
// Cleanup
|
|
1069
1103
|
player.destroy() // Remove player and clean up resources
|
|
1070
1104
|
```
|
|
1071
1105
|
|
|
1106
|
+
### Picture-in-Picture Mode
|
|
1107
|
+
|
|
1108
|
+
Enable floating video that stays on top while users work in other apps:
|
|
1109
|
+
|
|
1110
|
+
```typescript
|
|
1111
|
+
const player = new WontumPlayer({
|
|
1112
|
+
src: "https://example.com/video.m3u8",
|
|
1113
|
+
container: "#player",
|
|
1114
|
+
})
|
|
1115
|
+
|
|
1116
|
+
// Enter PiP mode
|
|
1117
|
+
await player.enterPictureInPicture()
|
|
1118
|
+
|
|
1119
|
+
// Listen for PiP events
|
|
1120
|
+
player.on("pictureinpictureenter", () => {
|
|
1121
|
+
console.log("Video is now floating!")
|
|
1122
|
+
})
|
|
1123
|
+
|
|
1124
|
+
player.on("pictureinpictureexit", () => {
|
|
1125
|
+
console.log("Back to normal mode")
|
|
1126
|
+
})
|
|
1127
|
+
|
|
1128
|
+
// Custom button to toggle PiP
|
|
1129
|
+
const pipButton = document.getElementById("pip-btn")
|
|
1130
|
+
pipButton.addEventListener("click", async () => {
|
|
1131
|
+
await player.togglePictureInPicture()
|
|
1132
|
+
})
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
**Note:** Picture-in-Picture is supported in most modern browsers. The player includes a built-in PiP button in the controls.
|
|
1136
|
+
|
|
1072
1137
|
## 📋 Complete API Reference
|
|
1073
1138
|
|
|
1074
1139
|
For detailed API documentation including all methods, events, types, and configuration options, see **[API-REFERENCE.md](./API-REFERENCE.md)**.
|
|
@@ -1083,6 +1148,7 @@ For detailed API documentation including all methods, events, types, and configu
|
|
|
1083
1148
|
- **Quality:** `setQuality(index)`, `getQualities()`
|
|
1084
1149
|
- **Playback Rate:** `setPlaybackRate(rate)`
|
|
1085
1150
|
- **Fullscreen:** `enterFullscreen()`, `exitFullscreen()`
|
|
1151
|
+
- **Picture-in-Picture:** `enterPictureInPicture()`, `exitPictureInPicture()`, `togglePictureInPicture()`
|
|
1086
1152
|
- **State:** `getState()`, `getCurrentTime()`, `getDuration()`
|
|
1087
1153
|
- **Lifecycle:** `destroy()`
|
|
1088
1154
|
|
|
@@ -1129,9 +1195,9 @@ Contributions are welcome! Please follow these steps:
|
|
|
1129
1195
|
|
|
1130
1196
|
## 💬 Support
|
|
1131
1197
|
|
|
1132
|
-
- **Issues:** [GitHub Issues](https://github.com/
|
|
1133
|
-
- **Discussions:** [GitHub Discussions](https://github.com/
|
|
1134
|
-
- **Email:**
|
|
1198
|
+
- **Issues:** [GitHub Issues](https://github.com/obipascal/wontum-player/issues)
|
|
1199
|
+
- **Discussions:** [GitHub Discussions](https://github.com/obipascal/wontum-player/discussions)
|
|
1200
|
+
- **Email:** pascalobi83@gmail.com
|
|
1135
1201
|
|
|
1136
1202
|
## 🙏 Acknowledgments
|
|
1137
1203
|
|
package/dist/src/player.d.ts
CHANGED
|
@@ -36,6 +36,9 @@ export declare class WontumPlayer {
|
|
|
36
36
|
getQualities(): QualityLevel[];
|
|
37
37
|
enterFullscreen(): void;
|
|
38
38
|
exitFullscreen(): void;
|
|
39
|
+
enterPictureInPicture(): Promise<void>;
|
|
40
|
+
exitPictureInPicture(): Promise<void>;
|
|
41
|
+
togglePictureInPicture(): Promise<void>;
|
|
39
42
|
getState(): PlayerState;
|
|
40
43
|
getVideoElement(): HTMLVideoElement;
|
|
41
44
|
/**
|
package/dist/src/types.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface S3Config {
|
|
|
45
45
|
getPresignedUrl?: (key: string) => Promise<string>;
|
|
46
46
|
/** CloudFront domain patterns to match (e.g., ['media.domain.com']) */
|
|
47
47
|
cloudFrontDomains?: string[];
|
|
48
|
+
/** Enable credentials (cookies) for HLS requests - required for CloudFront signed cookies */
|
|
49
|
+
withCredentials?: boolean;
|
|
48
50
|
/** S3 bucket region */
|
|
49
51
|
region?: string;
|
|
50
52
|
/** Custom S3 endpoint */
|
|
@@ -82,7 +84,7 @@ export interface PlayerState {
|
|
|
82
84
|
/**
|
|
83
85
|
* Player events (compatible with Mux Player and HTML5 MediaElement events)
|
|
84
86
|
*/
|
|
85
|
-
export type PlayerEventType = "play" | "pause" | "playing" | "ended" | "timeupdate" | "volumechange" | "ratechange" | "seeked" | "seeking" | "waiting" | "loadstart" | "loadeddata" | "loadedmetadata" | "canplay" | "canplaythrough" | "durationchange" | "progress" | "error" | "abort" | "emptied" | "stalled" | "suspend" | "qualitychange" | "fullscreenchange" | "resize";
|
|
87
|
+
export type PlayerEventType = "play" | "pause" | "playing" | "ended" | "timeupdate" | "volumechange" | "ratechange" | "seeked" | "seeking" | "waiting" | "loadstart" | "loadeddata" | "loadedmetadata" | "canplay" | "canplaythrough" | "durationchange" | "progress" | "error" | "abort" | "emptied" | "stalled" | "suspend" | "qualitychange" | "fullscreenchange" | "pictureinpictureenter" | "pictureinpictureexit" | "resize";
|
|
86
88
|
export interface PlayerEvent {
|
|
87
89
|
type: PlayerEventType;
|
|
88
90
|
data?: any;
|
|
@@ -13,12 +13,15 @@ export declare class UIController {
|
|
|
13
13
|
private skipBackwardButton;
|
|
14
14
|
private skipForwardButton;
|
|
15
15
|
private volumeButton;
|
|
16
|
+
private volumeContainer;
|
|
16
17
|
private fullscreenButton;
|
|
18
|
+
private pipButton;
|
|
17
19
|
private settingsButton;
|
|
18
20
|
private volumeSlider;
|
|
19
21
|
private progressInput;
|
|
20
22
|
private hideControlsTimeout;
|
|
21
23
|
private stickyControls;
|
|
24
|
+
private isVolumeSliderActive;
|
|
22
25
|
constructor(container: HTMLElement, player: WontumPlayer);
|
|
23
26
|
private injectStyles;
|
|
24
27
|
private createProgressBar;
|
|
@@ -38,6 +41,7 @@ export declare class UIController {
|
|
|
38
41
|
private getVolumeIcon;
|
|
39
42
|
private getMutedIcon;
|
|
40
43
|
private getFullscreenIcon;
|
|
44
|
+
private getPipIcon;
|
|
41
45
|
private getSkipBackwardIcon;
|
|
42
46
|
private getSkipForwardIcon;
|
|
43
47
|
private getSettingsIcon;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var j=Object.defineProperty;var W=(l,t,e)=>t in l?j(l,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):l[t]=e;var r=(l,t,e)=>W(l,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("hls.js"),M=require("react/jsx-runtime"),h=require("react");function _(l){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(l){for(const e in l)if(e!=="default"){const n=Object.getOwnPropertyDescriptor(l,e);Object.defineProperty(t,e,n.get?n:{enumerable:!0,get:()=>l[e]})}}return t.default=l,Object.freeze(t)}const R=_(h);class B{constructor(t){r(this,"config");r(this,"sessionId");r(this,"events",[]);r(this,"sessionStartTime");r(this,"playbackStartTime",null);r(this,"totalPlayTime",0);r(this,"totalBufferTime",0);r(this,"bufferStartTime",null);r(this,"rebufferCount",0);r(this,"seekCount",0);var e;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())}trackEvent(t,e={}){var s;if(!((s=this.config)!=null&&s.enabled))return;const n={eventType:t,timestamp:Date.now(),sessionId:this.sessionId,videoId:this.config.videoId,userId:this.config.userId,data:{...e,...this.getQoEMetrics()}};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)}updateMetrics(t,e){switch(t){case"play":this.playbackStartTime=Date.now();break;case"pause":case"ended":this.playbackStartTime&&(this.totalPlayTime+=Date.now()-this.playbackStartTime,this.playbackStartTime=null);break;case"buffering_start":this.bufferStartTime=Date.now(),this.rebufferCount++;break;case"buffering_end":this.bufferStartTime&&(this.totalBufferTime+=Date.now()-this.bufferStartTime,this.bufferStartTime=null);break;case"seeked":this.seekCount++;break}}getQoEMetrics(){const t=Date.now()-this.sessionStartTime,e=this.totalPlayTime>0?this.totalBufferTime/this.totalPlayTime:0;return{sessionDuration:t,totalPlayTime:this.totalPlayTime,totalBufferTime:this.totalBufferTime,bufferingRatio:Math.round(e*1e3)/1e3,rebufferCount:this.rebufferCount,seekCount:this.seekCount}}getSessionData(){return{userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language,screenResolution:`${screen.width}x${screen.height}`,viewport:`${window.innerWidth}x${window.innerHeight}`,connection:this.getConnectionInfo()}}getConnectionInfo(){const t=navigator,e=t.connection||t.mozConnection||t.webkitConnection;return e?{effectiveType:e.effectiveType,downlink:e.downlink,rtt:e.rtt,saveData:e.saveData}:null}async sendEvent(t){var e;if((e=this.config)!=null&&e.endpoint)try{await fetch(this.config.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}catch(n){console.error("Failed to send analytics event:",n)}}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}getEvents(){return[...this.events]}getMetrics(){return{sessionId:this.sessionId,...this.getQoEMetrics(),eventCount:this.events.length}}destroy(){var t;(t=this.config)!=null&&t.enabled&&this.trackEvent("session_end",this.getSessionData()),this.events=[]}}class A{constructor(t,e){r(this,"container");r(this,"player");r(this,"controlsContainer");r(this,"progressContainer");r(this,"progressBar");r(this,"playButton");r(this,"skipBackwardButton");r(this,"skipForwardButton");r(this,"volumeButton");r(this,"volumeContainer");r(this,"fullscreenButton");r(this,"pipButton");r(this,"settingsButton");r(this,"volumeSlider");r(this,"progressInput");r(this,"hideControlsTimeout",null);r(this,"stickyControls",!1);r(this,"isVolumeSliderActive",!1);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()}injectStyles(){const t="wontum-player-styles";if(document.getElementById(t))return;const e=this.player.config.theme||{},n=e.primaryColor||"#3b82f6",s=e.accentColor||"#2563eb",i=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)",c=e.progressHeight||"6px",u=e.borderRadius||"4px",g=document.createElement("style");g.id=t,g.textContent=`
|
|
2
2
|
.wontum-player-container {
|
|
3
3
|
position: relative;
|
|
4
4
|
background: #000;
|
|
5
|
-
font-family: ${
|
|
5
|
+
font-family: ${i};
|
|
6
6
|
overflow: hidden;
|
|
7
7
|
--primary-color: ${n};
|
|
8
|
-
--accent-color: ${
|
|
8
|
+
--accent-color: ${s};
|
|
9
9
|
--controls-bg: ${a};
|
|
10
|
-
--button-hover: ${
|
|
10
|
+
--button-hover: ${o};
|
|
11
11
|
--progress-height: ${c};
|
|
12
12
|
--border-radius: ${u};
|
|
13
13
|
}
|
|
@@ -100,18 +100,20 @@
|
|
|
100
100
|
.wontum-progress-container {
|
|
101
101
|
position: absolute;
|
|
102
102
|
bottom: 58px;
|
|
103
|
-
left:
|
|
104
|
-
|
|
103
|
+
left: 50%;
|
|
104
|
+
transform: translateX(-50%);
|
|
105
|
+
width: 70%;
|
|
106
|
+
max-width: 600px;
|
|
105
107
|
height: 5px;
|
|
106
108
|
cursor: pointer;
|
|
107
109
|
z-index: 12;
|
|
108
|
-
padding: 0
|
|
110
|
+
padding: 0;
|
|
109
111
|
transition: height 0.2s ease, opacity 0.3s ease, transform 0.3s ease;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
.wontum-progress-container.hidden {
|
|
113
115
|
opacity: 0;
|
|
114
|
-
transform: translateY(100%);
|
|
116
|
+
transform: translateX(-50%) translateY(100%);
|
|
115
117
|
pointer-events: none;
|
|
116
118
|
}
|
|
117
119
|
|
|
@@ -197,6 +199,7 @@
|
|
|
197
199
|
position: relative;
|
|
198
200
|
display: flex;
|
|
199
201
|
align-items: center;
|
|
202
|
+
gap: 0;
|
|
200
203
|
}
|
|
201
204
|
|
|
202
205
|
.wontum-volume-slider-wrapper {
|
|
@@ -208,18 +211,31 @@
|
|
|
208
211
|
backdrop-filter: blur(10px);
|
|
209
212
|
padding: 12px 8px;
|
|
210
213
|
border-radius: 6px;
|
|
211
|
-
margin-bottom:
|
|
214
|
+
margin-bottom: 5px;
|
|
212
215
|
opacity: 0;
|
|
213
216
|
pointer-events: none;
|
|
214
217
|
transition: opacity 0.2s ease;
|
|
215
218
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
219
|
+
z-index: 20;
|
|
216
220
|
}
|
|
217
221
|
|
|
218
|
-
.wontum-volume-container:hover .wontum-volume-slider-wrapper
|
|
222
|
+
.wontum-volume-container:hover .wontum-volume-slider-wrapper,
|
|
223
|
+
.wontum-volume-slider-wrapper:hover {
|
|
219
224
|
opacity: 1;
|
|
220
225
|
pointer-events: all;
|
|
221
226
|
}
|
|
222
227
|
|
|
228
|
+
/* Add a bridge area between button and slider to prevent gap */
|
|
229
|
+
.wontum-volume-container::before {
|
|
230
|
+
content: '';
|
|
231
|
+
position: absolute;
|
|
232
|
+
bottom: 100%;
|
|
233
|
+
left: 0;
|
|
234
|
+
right: 0;
|
|
235
|
+
height: 10px;
|
|
236
|
+
background: transparent;
|
|
237
|
+
}
|
|
238
|
+
|
|
223
239
|
.wontum-volume-slider {
|
|
224
240
|
-webkit-appearance: slider-vertical;
|
|
225
241
|
appearance: slider-vertical;
|
|
@@ -534,6 +550,10 @@
|
|
|
534
550
|
</div>
|
|
535
551
|
</div>
|
|
536
552
|
|
|
553
|
+
<button class="wontum-btn wontum-pip-btn" aria-label="Picture-in-Picture">
|
|
554
|
+
${this.getPipIcon()}
|
|
555
|
+
</button>
|
|
556
|
+
|
|
537
557
|
<button class="wontum-btn wontum-fullscreen-btn" aria-label="Fullscreen">
|
|
538
558
|
${this.getFullscreenIcon()}
|
|
539
559
|
</button>
|
|
@@ -542,28 +562,28 @@
|
|
|
542
562
|
<div class="wontum-loading" style="display: none;">
|
|
543
563
|
<div class="wontum-spinner"></div>
|
|
544
564
|
</div>
|
|
545
|
-
`,t}setupEventListeners(){this.playButton.addEventListener("click",()=>{this.player.getState().playing?this.player.pause():this.player.play()}),this.skipBackwardButton.addEventListener("click",()=>{this.player.skipBackward(10)}),this.skipForwardButton.addEventListener("click",()=>{this.player.skipForward(10)}),this.progressInput.addEventListener("input",n=>{const
|
|
565
|
+
`,t}setupEventListeners(){this.playButton.addEventListener("click",()=>{this.player.getState().playing?this.player.pause():this.player.play()}),this.skipBackwardButton.addEventListener("click",()=>{this.player.skipBackward(10)}),this.skipForwardButton.addEventListener("click",()=>{this.player.skipForward(10)}),this.progressInput.addEventListener("input",n=>{const s=n.target,i=parseFloat(s.value),a=this.player.getState(),o=i/100*a.duration;this.player.seek(o)}),this.volumeSlider.addEventListener("input",n=>{const s=n.target,i=parseFloat(s.value)/100;this.player.setVolume(i)}),this.volumeButton.addEventListener("click",()=>{this.player.getState().muted?this.player.unmute():this.player.mute()}),this.volumeContainer.addEventListener("mouseenter",()=>{this.isVolumeSliderActive=!0}),this.volumeContainer.addEventListener("mouseleave",()=>{this.isVolumeSliderActive=!1}),this.volumeSlider.addEventListener("input",()=>{this.isVolumeSliderActive=!0,this.resetHideControlsTimeout()}),this.volumeSlider.addEventListener("change",()=>{setTimeout(()=>{this.isVolumeSliderActive=!1},500)}),this.fullscreenButton.addEventListener("click",()=>{this.player.getState().fullscreen?this.player.exitFullscreen():this.player.enterFullscreen()}),this.pipButton.addEventListener("click",async()=>{try{await this.player.togglePictureInPicture()}catch(n){console.error("PiP error:",n)}}),this.settingsButton.addEventListener("click",()=>{const n=this.controlsContainer.querySelector(".wontum-settings-panel");n.classList.toggle("active"),n.classList.contains("active")&&(this.updateSettingsMenu(),this.updateQualityMenu(),this.updateSpeedMenu(),this.updateSubtitleMenu())});const t=this.controlsContainer.querySelectorAll(".wontum-tab");t.forEach(n=>{n.addEventListener("click",s=>{const i=s.currentTarget,a=i.getAttribute("data-tab");t.forEach(u=>u.classList.remove("active")),i.classList.add("active"),this.controlsContainer.querySelectorAll(".wontum-tab-panel").forEach(u=>u.classList.remove("active"));const c=this.controlsContainer.querySelector(`[data-panel="${a}"]`);c==null||c.classList.add("active")})}),this.player.getVideoElement().addEventListener("click",()=>{this.player.getState().playing?this.player.pause():this.player.play()}),this.container.addEventListener("mousemove",()=>{this.showControls(),this.resetHideControlsTimeout()}),this.container.addEventListener("mouseleave",()=>{this.hideControls()})}setupPlayerEventListeners(){this.player.on("play",()=>{this.playButton.innerHTML=this.getPauseIcon()}),this.player.on("pause",()=>{this.playButton.innerHTML=this.getPlayIcon()}),this.player.on("timeupdate",t=>{const{currentTime:e}=t.data,n=this.player.getState();if(n.duration>0){const i=e/n.duration*100;this.progressBar.style.width=`${i}%`,this.progressInput.value=i.toString()}const s=this.controlsContainer.querySelector(".wontum-current-time");s.textContent=this.formatTime(e)}),this.player.on("loadedmetadata",t=>{const{duration:e}=t.data,n=this.controlsContainer.querySelector(".wontum-duration");n.textContent=this.formatTime(e),t.data.qualities&&this.updateQualityMenu(t.data.qualities)}),this.player.on("volumechange",t=>{const{volume:e,muted:n}=t.data;this.volumeSlider.value=(e*100).toString(),this.volumeButton.innerHTML=n?this.getMutedIcon():this.getVolumeIcon()}),this.player.on("waiting",()=>{const t=this.controlsContainer.querySelector(".wontum-loading");t.style.display="block"}),this.player.on("canplay",()=>{const t=this.controlsContainer.querySelector(".wontum-loading");t.style.display="none"})}updateSubtitleMenu(){const t=this.controlsContainer.querySelector(".wontum-subtitle-menu"),e=this.player.getSubtitleTracks();if(e.length===0){t.innerHTML='<div class="wontum-subtitle-option">No subtitles available</div>';return}const n=e.findIndex(s=>s.mode==="showing");t.innerHTML=`
|
|
546
566
|
<div class="wontum-subtitle-option ${n===-1?"active":""}" data-track="-1">Off</div>
|
|
547
|
-
${e.map((i
|
|
548
|
-
<div class="wontum-subtitle-option ${
|
|
549
|
-
${
|
|
567
|
+
${e.map((s,i)=>`
|
|
568
|
+
<div class="wontum-subtitle-option ${i===n?"active":""}" data-track="${i}">
|
|
569
|
+
${s.label||s.language||`Track ${i+1}`}
|
|
550
570
|
</div>
|
|
551
571
|
`).join("")}
|
|
552
|
-
`,t.querySelectorAll(".wontum-subtitle-option").forEach(
|
|
553
|
-
<div class="wontum-speed-option ${n===
|
|
554
|
-
${
|
|
572
|
+
`,t.querySelectorAll(".wontum-subtitle-option").forEach(s=>{s.addEventListener("click",i=>{const a=i.target,o=parseInt(a.dataset.track||"-1");o===-1?this.player.disableSubtitles():this.player.enableSubtitles(o),t.querySelectorAll(".wontum-subtitle-option").forEach(c=>c.classList.remove("active")),a.classList.add("active")})})}updateSpeedMenu(){const t=this.controlsContainer.querySelector(".wontum-speed-menu"),n=this.player.getState().playbackRate||1,s=[.25,.5,.75,1,1.25,1.5,1.75,2];t.innerHTML=s.map(i=>`
|
|
573
|
+
<div class="wontum-speed-option ${n===i?"active":""}" data-speed="${i}">
|
|
574
|
+
${i===1?"Normal":i+"x"}
|
|
555
575
|
</div>
|
|
556
|
-
`).join(""),t.querySelectorAll(".wontum-speed-option").forEach(
|
|
576
|
+
`).join(""),t.querySelectorAll(".wontum-speed-option").forEach(i=>{i.addEventListener("click",a=>{const o=a.target,c=parseFloat(o.dataset.speed||"1");this.player.setPlaybackRate(c),t.querySelectorAll(".wontum-speed-option").forEach(u=>u.classList.remove("active")),o.classList.add("active")})})}updateSettingsMenu(){const t=this.controlsContainer.querySelector(".wontum-settings-menu");t.innerHTML=`
|
|
557
577
|
<div class="wontum-settings-option" data-setting="sticky-controls">
|
|
558
578
|
<span>Sticky Controls</span>
|
|
559
579
|
<div class="wontum-toggle-switch ${this.stickyControls?"active":""}"></div>
|
|
560
580
|
</div>
|
|
561
581
|
`;const e=t.querySelector('[data-setting="sticky-controls"]');e.addEventListener("click",()=>{this.stickyControls=!this.stickyControls,e.querySelector(".wontum-toggle-switch").classList.toggle("active"),this.stickyControls?(this.controlsContainer.classList.add("sticky"),this.progressContainer.classList.add("sticky")):(this.controlsContainer.classList.remove("sticky"),this.progressContainer.classList.remove("sticky"))})}updateQualityMenu(t){const e=this.controlsContainer.querySelector(".wontum-quality-menu"),n=t||this.player.getQualities();if(!n||n.length===0){e.innerHTML='<div class="wontum-quality-option">No qualities available</div>';return}e.innerHTML=`
|
|
562
582
|
<div class="wontum-quality-option active" data-quality="-1">Auto</div>
|
|
563
|
-
${n.map((i
|
|
564
|
-
<div class="wontum-quality-option" data-quality="${
|
|
583
|
+
${n.map((s,i)=>`
|
|
584
|
+
<div class="wontum-quality-option" data-quality="${i}">${s.name}</div>
|
|
565
585
|
`).join("")}
|
|
566
|
-
`,e.querySelectorAll(".wontum-quality-option").forEach(
|
|
586
|
+
`,e.querySelectorAll(".wontum-quality-option").forEach(s=>{s.addEventListener("click",i=>{const a=i.target,o=parseInt(a.dataset.quality||"-1");this.player.setQuality(o),e.querySelectorAll(".wontum-quality-option").forEach(c=>c.classList.remove("active")),a.classList.add("active")})})}showControls(){this.controlsContainer.classList.remove("hidden"),this.progressContainer.classList.remove("hidden")}hideControls(){if(this.stickyControls||this.isVolumeSliderActive)return;this.player.getState().playing&&(this.controlsContainer.classList.add("hidden"),this.progressContainer.classList.add("hidden"))}resetHideControlsTimeout(){this.stickyControls||(this.hideControlsTimeout&&clearTimeout(this.hideControlsTimeout),this.hideControlsTimeout=window.setTimeout(()=>{this.hideControls()},1e4))}formatTime(t){if(isNaN(t))return"0:00";const e=Math.floor(t/60),n=Math.floor(t%60);return`${e}:${n.toString().padStart(2,"0")}`}getPlayIcon(){return'<svg viewBox="0 0 24 24"><path fill="white" d="M8 5v14l11-7z"/></svg>'}getPauseIcon(){return'<svg viewBox="0 0 24 24"><path fill="white" d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/></svg>'}getVolumeIcon(){return'<svg viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/></svg>'}getMutedIcon(){return'<svg viewBox="0 0 24 24"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg>'}getFullscreenIcon(){return'<svg viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>'}getPipIcon(){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>'}getSkipBackwardIcon(){return`<svg viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
567
587
|
<circle cx="30" cy="30" r="28" stroke="white" stroke-width="2"/>
|
|
568
588
|
<!-- Circular arrow backward -->
|
|
569
589
|
<path d="M30 12 A18 18 0 1 0 30 48" stroke="white" stroke-width="2.5" stroke-linecap="round" fill="none"/>
|
|
@@ -575,4 +595,4 @@
|
|
|
575
595
|
<path d="M30 12 A18 18 0 1 1 30 48" stroke="white" stroke-width="2.5" stroke-linecap="round" fill="none"/>
|
|
576
596
|
<path d="M35 12 L30 12 L30 17" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
577
597
|
<text x="30" y="35" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="white" text-anchor="middle">10</text>
|
|
578
|
-
</svg>`}getSettingsIcon(){return'<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>'}destroy(){this.hideControlsTimeout&&clearTimeout(this.hideControlsTimeout),this.controlsContainer.remove()}}class F{constructor(t){o(this,"config");o(this,"urlCache",new Map);o(this,"signedUrls",new Set);this.config=t}async processUrl(t){return this.isCloudFrontUrl(t)?this.signCloudFrontUrl(t):this.isS3Url(t)?this.getPresignedUrl(t):t}isCloudFrontUrl(t){var e;if(!((e=this.config)!=null&&e.cloudFrontDomains)||this.config.cloudFrontDomains.length===0)return!1;try{const n=new URL(t);return this.config.cloudFrontDomains.some(i=>n.hostname.includes(i))}catch{return!1}}isS3Url(t){return t.includes(".s3.")||t.includes("s3.amazonaws.com")||t.startsWith("s3://")}async signCloudFrontUrl(t,e=0){var s,a;if(this.signedUrls.has(t))return t;if((s=this.config)!=null&&s.signUrl)try{const r=await this.config.signUrl(t);return this.signedUrls.add(t),r}catch(r){const c=(r==null?void 0:r.name)==="AbortError"||((a=r==null?void 0:r.message)==null?void 0:a.includes("aborted"));if(c&&e<2)return console.warn(`Sign URL aborted, retrying (${e+1}/2)...`),await new Promise(u=>setTimeout(u,300)),this.signCloudFrontUrl(t,e+1);throw console.error("Failed to sign CloudFront URL:",r),c?new Error("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."):new Error(`Failed to sign CloudFront URL: ${(r==null?void 0:r.message)||"Unknown error"}`)}return console.warn("No signUrl function provided. CloudFront cookies may not be set."),t}extractS3Key(t){if(t.startsWith("s3://"))return t.replace("s3://","").split("/").slice(1).join("/");const e=t.match(/s3[.-]([^.]+)\.amazonaws\.com\/(.+)/);if(e)return e[2];const n=t.match(/([^.]+)\.s3\.amazonaws\.com\/(.+)/);return n?n[2]:t}async getPresignedUrl(t){var i;const e=this.extractS3Key(t),n=this.urlCache.get(e);if(n&&n.expiresAt>Date.now())return n.url;if((i=this.config)!=null&&i.getPresignedUrl)try{const s=await this.config.getPresignedUrl(e);return this.urlCache.set(e,{url:s,expiresAt:Date.now()+50*60*1e3}),s}catch(s){throw console.error("Failed to generate presigned URL:",s),new Error("Failed to generate presigned URL for S3 object")}return console.warn("No getPresignedUrl function provided. Using direct S3 URL (requires public bucket)"),t}static constructS3Url(t,e,n="us-east-1"){return`https://${t}.s3.${n}.amazonaws.com/${e}`}static parseS3Uri(t){if(!t.startsWith("s3://"))return null;const e=t.replace("s3://","").split("/"),n=e[0],i=e.slice(1).join("/");return{bucket:n,key:i}}clearCache(){this.urlCache.clear(),this.signedUrls.clear()}}class b{constructor(t){o(this,"container");o(this,"videoElement");o(this,"hls",null);o(this,"config");o(this,"eventListeners",new Map);o(this,"analytics");o(this,"s3Handler");o(this,"uiController");o(this,"qualities",[]);o(this,"state",{playing:!1,paused:!0,ended:!1,buffering:!1,currentTime:0,duration:0,volume:1,muted:!1,playbackRate:1,quality:"auto",availableQualities:[],fullscreen:!1});if(this.config=t,this.container=typeof t.container=="string"?document.querySelector(t.container):t.container,!this.container)throw new Error("Container element not found");this.analytics=new P(t.analytics),this.s3Handler=new F(t.s3Config),this.videoElement=this.createVideoElement(),this.container.appendChild(this.videoElement),this.uiController=new $(this.container,this),this.setupVideoListeners(),this.loadSource(t.src),t.autoplay&&(this.videoElement.autoplay=!0),t.muted&&this.mute(),t.poster&&(this.videoElement.poster=t.poster),t.preload&&(this.videoElement.preload=t.preload),t.subtitles&&this.addSubtitleTracks(t.subtitles)}addSubtitleTracks(t){t.forEach(e=>{const n=document.createElement("track");n.kind="subtitles",n.label=e.label,n.src=e.src,n.srclang=e.srclang,e.default&&(n.default=!0),this.videoElement.appendChild(n)})}createVideoElement(){const t=document.createElement("video");return t.className="wontum-player-video",t.style.width="100%",t.style.height="100%",t.playsInline=!0,t}setupVideoListeners(){this.videoElement.addEventListener("play",()=>{this.state.playing=!0,this.state.paused=!1,this.emit("play"),this.analytics.trackEvent("play",this.getAnalyticsData())}),this.videoElement.addEventListener("pause",()=>{this.state.playing=!1,this.state.paused=!0,this.emit("pause"),this.analytics.trackEvent("pause",this.getAnalyticsData())}),this.videoElement.addEventListener("ended",()=>{this.state.ended=!0,this.state.playing=!1,this.emit("ended"),this.analytics.trackEvent("ended",this.getAnalyticsData())}),this.videoElement.addEventListener("timeupdate",()=>{this.state.currentTime=this.videoElement.currentTime,this.emit("timeupdate",{currentTime:this.state.currentTime})}),this.videoElement.addEventListener("loadedmetadata",()=>{this.state.duration=this.videoElement.duration,this.emit("loadedmetadata",{duration:this.state.duration}),this.analytics.trackEvent("loadedmetadata",this.getAnalyticsData())}),this.videoElement.addEventListener("volumechange",()=>{this.state.volume=this.videoElement.volume,this.state.muted=this.videoElement.muted,this.emit("volumechange",{volume:this.state.volume,muted:this.state.muted})}),this.videoElement.addEventListener("ratechange",()=>{this.state.playbackRate=this.videoElement.playbackRate,this.emit("ratechange",{playbackRate:this.state.playbackRate})}),this.videoElement.addEventListener("waiting",()=>{this.state.buffering=!0,this.emit("waiting"),this.analytics.trackEvent("buffering_start",this.getAnalyticsData())}),this.videoElement.addEventListener("canplay",()=>{this.state.buffering=!1,this.emit("canplay"),this.analytics.trackEvent("buffering_end",this.getAnalyticsData())}),this.videoElement.addEventListener("seeking",()=>{this.emit("seeking")}),this.videoElement.addEventListener("seeked",()=>{this.emit("seeked",{currentTime:this.state.currentTime}),this.analytics.trackEvent("seeked",this.getAnalyticsData())}),this.videoElement.addEventListener("error",t=>{const e=this.videoElement.error;this.emit("error",{error:e}),this.analytics.trackEvent("error",{...this.getAnalyticsData(),error:e==null?void 0:e.message})}),this.videoElement.addEventListener("loadstart",()=>{this.emit("loadstart")}),this.videoElement.addEventListener("loadeddata",()=>{this.emit("loadeddata")}),this.videoElement.addEventListener("canplaythrough",()=>{this.emit("canplaythrough")}),this.videoElement.addEventListener("playing",()=>{this.state.playing=!0,this.state.buffering=!1,this.emit("playing")}),this.videoElement.addEventListener("durationchange",()=>{this.state.duration=this.videoElement.duration,this.emit("durationchange",{duration:this.state.duration})}),this.videoElement.addEventListener("progress",()=>{this.emit("progress",{buffered:this.videoElement.buffered})}),this.videoElement.addEventListener("stalled",()=>{this.emit("stalled")}),this.videoElement.addEventListener("suspend",()=>{this.emit("suspend")}),this.videoElement.addEventListener("abort",()=>{this.emit("abort")}),this.videoElement.addEventListener("emptied",()=>{this.emit("emptied")}),this.videoElement.addEventListener("resize",()=>{this.emit("resize",{videoWidth:this.videoElement.videoWidth,videoHeight:this.videoElement.videoHeight})})}async loadSource(t){try{const e=await this.s3Handler.processUrl(t);if(m.isSupported())this.hls=new m(this.config.hlsConfig),this.hls.loadSource(e),this.hls.attachMedia(this.videoElement),this.hls.on(m.Events.MANIFEST_PARSED,(n,i)=>{const s=this.extractQualities(i.levels);this.qualities=s}),this.hls.on(m.Events.LEVEL_SWITCHED,(n,i)=>{var a;const s=(a=this.hls)==null?void 0:a.levels[i.level];s&&(this.state.quality=`${s.height}p`,this.emit("qualitychange",{quality:this.state.quality}))}),this.hls.on(m.Events.ERROR,(n,i)=>{i.fatal&&this.handleHlsError(i)});else if(this.videoElement.canPlayType("application/vnd.apple.mpegurl"))this.videoElement.src=e;else throw new Error("HLS is not supported in this browser")}catch(e){console.error("Failed to load video source:",e),this.emit("error",{error:e})}}extractQualities(t){return t.map(e=>({height:e.height,width:e.width,bitrate:e.bitrate,name:`${e.height}p`}))}handleHlsError(t){var e,n;switch(t.type){case m.ErrorTypes.NETWORK_ERROR:console.error("Network error occurred"),(e=this.hls)==null||e.startLoad();break;case m.ErrorTypes.MEDIA_ERROR:console.error("Media error occurred"),(n=this.hls)==null||n.recoverMediaError();break;default:console.error("Fatal error occurred:",t),this.destroy();break}}getAnalyticsData(){return{currentTime:this.state.currentTime,duration:this.state.duration,quality:this.state.quality,playbackRate:this.state.playbackRate,volume:this.state.volume,muted:this.state.muted}}play(){return this.videoElement.play()}pause(){this.videoElement.pause()}seek(t){this.videoElement.currentTime=t}skipForward(t=10){const e=Math.min(this.state.currentTime+t,this.state.duration);this.seek(e)}skipBackward(t=10){const e=Math.max(this.state.currentTime-t,0);this.seek(e)}setVolume(t){this.videoElement.volume=Math.max(0,Math.min(1,t))}mute(){this.videoElement.muted=!0}unmute(){this.videoElement.muted=!1}setPlaybackRate(t){this.videoElement.playbackRate=t}setQuality(t){this.hls&&(this.hls.currentLevel=t)}getQualities(){return this.qualities}enterFullscreen(){this.container.requestFullscreen&&(this.container.requestFullscreen(),this.state.fullscreen=!0,this.emit("fullscreenchange",{fullscreen:!0}))}exitFullscreen(){document.exitFullscreen&&(document.exitFullscreen(),this.state.fullscreen=!1,this.emit("fullscreenchange",{fullscreen:!1}))}getState(){return{...this.state}}getVideoElement(){return this.videoElement}enableSubtitles(t){const e=this.videoElement.textTracks;for(let n=0;n<e.length;n++)e[n].mode=n===t?"showing":"hidden"}disableSubtitles(){const t=this.videoElement.textTracks;for(let e=0;e<t.length;e++)t[e].mode="hidden"}toggleSubtitles(){const t=this.videoElement.textTracks;return Array.from(t).some(n=>n.mode==="showing")?(this.disableSubtitles(),!1):t.length>0?(this.enableSubtitles(0),!0):!1}getSubtitleTracks(){return Array.from(this.videoElement.textTracks)}areSubtitlesEnabled(){const t=this.videoElement.textTracks;return Array.from(t).some(e=>e.mode==="showing")}on(t,e){this.eventListeners.has(t)||this.eventListeners.set(t,new Set),this.eventListeners.get(t).add(e)}off(t,e){var n;(n=this.eventListeners.get(t))==null||n.delete(e)}emit(t,e){var i;const n={type:t,data:e,timestamp:Date.now()};(i=this.eventListeners.get(t))==null||i.forEach(s=>{s(n)})}destroy(){this.hls&&(this.hls.destroy(),this.hls=null),this.uiController.destroy(),this.videoElement.remove(),this.eventListeners.clear(),this.analytics.destroy()}}const N=l=>{const{src:t,autoplay:e,muted:n,controls:i=!0,poster:s,preload:a,theme:r,s3Config:c,analytics:u,hlsConfig:g,subtitles:U,stickyControls:D,onReady:f,onPlay:k,onPause:E,onEnded:x,onTimeUpdate:S,onVolumeChange:C,onError:L,onLoadedMetadata:T,onQualityChange:q,style:z,className:H,width:v="100%",height:y="500px"}=l,w=h.useRef(null),M=h.useRef(null);return h.useEffect(()=>{if(!w.current)return;const j={src:t,container:w.current,autoplay:e,muted:n,controls:i,poster:s,preload:a,theme:r,s3Config:c,analytics:u,hlsConfig:g,subtitles:U,stickyControls:D},d=new b(j);return M.current=d,k&&d.on("play",k),E&&d.on("pause",E),x&&d.on("ended",x),L&&d.on("error",p=>{var R;return L((R=p.data)==null?void 0:R.error)}),T&&d.on("loadedmetadata",T),q&&d.on("qualitychange",p=>q(p.data.level)),S&&d.on("timeupdate",p=>S(p.data.currentTime)),C&&d.on("volumechange",p=>C(p.data.volume,p.data.muted)),f&&f(d),()=>{d.destroy(),M.current=null}},[t]),I.jsx("div",{ref:w,className:H,style:{width:typeof v=="number"?`${v}px`:v,height:typeof y=="number"?`${y}px`:y,...z}})},Q=l=>{const[t,e]=h.useState(null),[n,i]=h.useState(null),s=h.useRef(null);return h.useEffect(()=>{if(!s.current)return;const a=new b({...l,container:s.current});e(a);const r=()=>{i(a.getState())};return a.on("play",r),a.on("pause",r),a.on("timeupdate",r),a.on("volumechange",r),a.on("loadedmetadata",r),()=>{a.destroy()}},[l.src]),{containerRef:s,player:t,state:n}},A=B.createContext({player:null,state:null}),O=l=>{const{player:t,children:e}=l,[n,i]=h.useState(t.getState());return h.useEffect(()=>{const s=()=>{i(t.getState())};return t.on("play",s),t.on("pause",s),t.on("timeupdate",s),t.on("volumechange",s),t.on("loadedmetadata",s),()=>{}},[t]),I.jsx(A.Provider,{value:{player:t,state:n},children:e})},Y=()=>{const l=B.useContext(A);if(!l.player)throw new Error("useWontumPlayerContext must be used within WontumPlayerProvider");return l};exports.Analytics=P;exports.S3Handler=F;exports.UIController=$;exports.WontumPlayer=b;exports.WontumPlayerProvider=O;exports.WontumPlayerReact=N;exports.useWontumPlayer=Q;exports.useWontumPlayerContext=Y;
|
|
598
|
+
</svg>`}getSettingsIcon(){return'<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>'}destroy(){this.hideControlsTimeout&&clearTimeout(this.hideControlsTimeout),this.controlsContainer.remove()}}class F{constructor(t){r(this,"config");r(this,"urlCache",new Map);r(this,"signedUrls",new Set);this.config=t}async processUrl(t){return this.isCloudFrontUrl(t)?this.signCloudFrontUrl(t):this.isS3Url(t)?this.getPresignedUrl(t):t}isCloudFrontUrl(t){var e;if(!((e=this.config)!=null&&e.cloudFrontDomains)||this.config.cloudFrontDomains.length===0)return!1;try{const n=new URL(t);return this.config.cloudFrontDomains.some(s=>n.hostname.includes(s))}catch{return!1}}isS3Url(t){return t.includes(".s3.")||t.includes("s3.amazonaws.com")||t.startsWith("s3://")}async signCloudFrontUrl(t,e=0){var i,a;if(this.signedUrls.has(t))return t;if((i=this.config)!=null&&i.signUrl)try{const o=await this.config.signUrl(t);return this.signedUrls.add(t),o}catch(o){const c=(o==null?void 0:o.name)==="AbortError"||((a=o==null?void 0:o.message)==null?void 0:a.includes("aborted"));if(c&&e<2)return console.warn(`Sign URL aborted, retrying (${e+1}/2)...`),await new Promise(u=>setTimeout(u,300)),this.signCloudFrontUrl(t,e+1);throw console.error("Failed to sign CloudFront URL:",o),c?new Error("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."):new Error(`Failed to sign CloudFront URL: ${(o==null?void 0:o.message)||"Unknown error"}`)}return console.warn("No signUrl function provided. CloudFront cookies may not be set."),t}extractS3Key(t){if(t.startsWith("s3://"))return t.replace("s3://","").split("/").slice(1).join("/");const e=t.match(/s3[.-]([^.]+)\.amazonaws\.com\/(.+)/);if(e)return e[2];const n=t.match(/([^.]+)\.s3\.amazonaws\.com\/(.+)/);return n?n[2]:t}async getPresignedUrl(t){var s;const e=this.extractS3Key(t),n=this.urlCache.get(e);if(n&&n.expiresAt>Date.now())return n.url;if((s=this.config)!=null&&s.getPresignedUrl)try{const i=await this.config.getPresignedUrl(e);return this.urlCache.set(e,{url:i,expiresAt:Date.now()+50*60*1e3}),i}catch(i){throw console.error("Failed to generate presigned URL:",i),new Error("Failed to generate presigned URL for S3 object")}return console.warn("No getPresignedUrl function provided. Using direct S3 URL (requires public bucket)"),t}static constructS3Url(t,e,n="us-east-1"){return`https://${t}.s3.${n}.amazonaws.com/${e}`}static parseS3Uri(t){if(!t.startsWith("s3://"))return null;const e=t.replace("s3://","").split("/"),n=e[0],s=e.slice(1).join("/");return{bucket:n,key:s}}clearCache(){this.urlCache.clear(),this.signedUrls.clear()}}class b{constructor(t){r(this,"container");r(this,"videoElement");r(this,"hls",null);r(this,"config");r(this,"eventListeners",new Map);r(this,"analytics");r(this,"s3Handler");r(this,"uiController");r(this,"qualities",[]);r(this,"state",{playing:!1,paused:!0,ended:!1,buffering:!1,currentTime:0,duration:0,volume:1,muted:!1,playbackRate:1,quality:"auto",availableQualities:[],fullscreen:!1});if(this.config=t,this.container=typeof t.container=="string"?document.querySelector(t.container):t.container,!this.container)throw new Error("Container element not found");this.analytics=new B(t.analytics),this.s3Handler=new F(t.s3Config),this.videoElement=this.createVideoElement(),this.container.appendChild(this.videoElement),this.uiController=new A(this.container,this),this.setupVideoListeners(),this.loadSource(t.src),t.autoplay&&(this.videoElement.autoplay=!0),t.muted&&this.mute(),t.poster&&(this.videoElement.poster=t.poster),t.preload&&(this.videoElement.preload=t.preload),t.subtitles&&this.addSubtitleTracks(t.subtitles)}addSubtitleTracks(t){t.forEach(e=>{const n=document.createElement("track");n.kind="subtitles",n.label=e.label,n.src=e.src,n.srclang=e.srclang,e.default&&(n.default=!0),this.videoElement.appendChild(n)})}createVideoElement(){const t=document.createElement("video");return t.className="wontum-player-video",t.style.width="100%",t.style.height="100%",t.playsInline=!0,t.crossOrigin="use-credentials",t}setupVideoListeners(){this.videoElement.addEventListener("play",()=>{this.state.playing=!0,this.state.paused=!1,this.emit("play"),this.analytics.trackEvent("play",this.getAnalyticsData())}),this.videoElement.addEventListener("pause",()=>{this.state.playing=!1,this.state.paused=!0,this.emit("pause"),this.analytics.trackEvent("pause",this.getAnalyticsData())}),this.videoElement.addEventListener("ended",()=>{this.state.ended=!0,this.state.playing=!1,this.emit("ended"),this.analytics.trackEvent("ended",this.getAnalyticsData())}),this.videoElement.addEventListener("timeupdate",()=>{this.state.currentTime=this.videoElement.currentTime,this.emit("timeupdate",{currentTime:this.state.currentTime})}),this.videoElement.addEventListener("loadedmetadata",()=>{this.state.duration=this.videoElement.duration,this.emit("loadedmetadata",{duration:this.state.duration}),this.analytics.trackEvent("loadedmetadata",this.getAnalyticsData())}),this.videoElement.addEventListener("volumechange",()=>{this.state.volume=this.videoElement.volume,this.state.muted=this.videoElement.muted,this.emit("volumechange",{volume:this.state.volume,muted:this.state.muted})}),this.videoElement.addEventListener("ratechange",()=>{this.state.playbackRate=this.videoElement.playbackRate,this.emit("ratechange",{playbackRate:this.state.playbackRate})}),this.videoElement.addEventListener("waiting",()=>{this.state.buffering=!0,this.emit("waiting"),this.analytics.trackEvent("buffering_start",this.getAnalyticsData())}),this.videoElement.addEventListener("canplay",()=>{this.state.buffering=!1,this.emit("canplay"),this.analytics.trackEvent("buffering_end",this.getAnalyticsData())}),this.videoElement.addEventListener("seeking",()=>{this.emit("seeking")}),this.videoElement.addEventListener("seeked",()=>{this.emit("seeked",{currentTime:this.state.currentTime}),this.analytics.trackEvent("seeked",this.getAnalyticsData())}),this.videoElement.addEventListener("error",t=>{const e=this.videoElement.error;this.emit("error",{error:e}),this.analytics.trackEvent("error",{...this.getAnalyticsData(),error:e==null?void 0:e.message})}),this.videoElement.addEventListener("loadstart",()=>{this.emit("loadstart")}),this.videoElement.addEventListener("loadeddata",()=>{this.emit("loadeddata")}),this.videoElement.addEventListener("canplaythrough",()=>{this.emit("canplaythrough")}),this.videoElement.addEventListener("playing",()=>{this.state.playing=!0,this.state.buffering=!1,this.emit("playing")}),this.videoElement.addEventListener("durationchange",()=>{this.state.duration=this.videoElement.duration,this.emit("durationchange",{duration:this.state.duration})}),this.videoElement.addEventListener("progress",()=>{this.emit("progress",{buffered:this.videoElement.buffered})}),this.videoElement.addEventListener("stalled",()=>{this.emit("stalled")}),this.videoElement.addEventListener("suspend",()=>{this.emit("suspend")}),this.videoElement.addEventListener("abort",()=>{this.emit("abort")}),this.videoElement.addEventListener("emptied",()=>{this.emit("emptied")}),this.videoElement.addEventListener("resize",()=>{this.emit("resize",{videoWidth:this.videoElement.videoWidth,videoHeight:this.videoElement.videoHeight})})}async loadSource(t){var e;try{const n=await this.s3Handler.processUrl(t);if(m.isSupported()){const s=((e=this.config.s3Config)==null?void 0:e.withCredentials)??!1,i={...this.config.hlsConfig,xhrSetup:(a,o)=>{var c;s&&(a.withCredentials=!0),(c=this.config.hlsConfig)!=null&&c.xhrSetup&&this.config.hlsConfig.xhrSetup(a,o)}};this.hls=new m(i),this.hls.loadSource(n),this.hls.attachMedia(this.videoElement),this.hls.on(m.Events.MANIFEST_PARSED,(a,o)=>{const c=this.extractQualities(o.levels);this.qualities=c}),this.hls.on(m.Events.LEVEL_SWITCHED,(a,o)=>{var u;const c=(u=this.hls)==null?void 0:u.levels[o.level];c&&(this.state.quality=`${c.height}p`,this.emit("qualitychange",{quality:this.state.quality}))}),this.hls.on(m.Events.ERROR,(a,o)=>{o.fatal&&this.handleHlsError(o)})}else if(this.videoElement.canPlayType("application/vnd.apple.mpegurl"))this.videoElement.src=n;else throw new Error("HLS is not supported in this browser")}catch(n){console.error("Failed to load video source:",n),this.emit("error",{error:n})}}extractQualities(t){return t.map(e=>({height:e.height,width:e.width,bitrate:e.bitrate,name:`${e.height}p`}))}handleHlsError(t){var e,n;switch(t.type){case m.ErrorTypes.NETWORK_ERROR:console.error("Network error occurred"),(e=this.hls)==null||e.startLoad();break;case m.ErrorTypes.MEDIA_ERROR:console.error("Media error occurred"),(n=this.hls)==null||n.recoverMediaError();break;default:console.error("Fatal error occurred:",t),this.destroy();break}}getAnalyticsData(){return{currentTime:this.state.currentTime,duration:this.state.duration,quality:this.state.quality,playbackRate:this.state.playbackRate,volume:this.state.volume,muted:this.state.muted}}play(){return this.videoElement.play()}pause(){this.videoElement.pause()}seek(t){this.videoElement.currentTime=t}skipForward(t=10){const e=Math.min(this.state.currentTime+t,this.state.duration);this.seek(e)}skipBackward(t=10){const e=Math.max(this.state.currentTime-t,0);this.seek(e)}setVolume(t){this.videoElement.volume=Math.max(0,Math.min(1,t))}mute(){this.videoElement.muted=!0}unmute(){this.videoElement.muted=!1}setPlaybackRate(t){this.videoElement.playbackRate=t}setQuality(t){this.hls&&(this.hls.currentLevel=t)}getQualities(){return this.qualities}enterFullscreen(){this.container.requestFullscreen&&(this.container.requestFullscreen(),this.state.fullscreen=!0,this.emit("fullscreenchange",{fullscreen:!0}))}exitFullscreen(){document.exitFullscreen&&(document.exitFullscreen(),this.state.fullscreen=!1,this.emit("fullscreenchange",{fullscreen:!1}))}async enterPictureInPicture(){if(document.pictureInPictureEnabled&&!this.videoElement.disablePictureInPicture)try{await this.videoElement.requestPictureInPicture(),this.emit("pictureinpictureenter",{})}catch(t){throw console.error("Failed to enter Picture-in-Picture:",t),t}}async exitPictureInPicture(){if(document.pictureInPictureElement)try{await document.exitPictureInPicture(),this.emit("pictureinpictureexit",{})}catch(t){throw console.error("Failed to exit Picture-in-Picture:",t),t}}async togglePictureInPicture(){document.pictureInPictureElement?await this.exitPictureInPicture():await this.enterPictureInPicture()}getState(){return{...this.state}}getVideoElement(){return this.videoElement}enableSubtitles(t){const e=this.videoElement.textTracks;for(let n=0;n<e.length;n++)e[n].mode=n===t?"showing":"hidden"}disableSubtitles(){const t=this.videoElement.textTracks;for(let e=0;e<t.length;e++)t[e].mode="hidden"}toggleSubtitles(){const t=this.videoElement.textTracks;return Array.from(t).some(n=>n.mode==="showing")?(this.disableSubtitles(),!1):t.length>0?(this.enableSubtitles(0),!0):!1}getSubtitleTracks(){return Array.from(this.videoElement.textTracks)}areSubtitlesEnabled(){const t=this.videoElement.textTracks;return Array.from(t).some(e=>e.mode==="showing")}on(t,e){this.eventListeners.has(t)||this.eventListeners.set(t,new Set),this.eventListeners.get(t).add(e)}off(t,e){var n;(n=this.eventListeners.get(t))==null||n.delete(e)}emit(t,e){var s;const n={type:t,data:e,timestamp:Date.now()};(s=this.eventListeners.get(t))==null||s.forEach(i=>{i(n)})}destroy(){this.hls&&(this.hls.destroy(),this.hls=null),this.uiController.destroy(),this.videoElement.remove(),this.eventListeners.clear(),this.analytics.destroy()}}const N=l=>{const{src:t,autoplay:e,muted:n,controls:s=!0,poster:i,preload:a,theme:o,s3Config:c,analytics:u,hlsConfig:g,subtitles:z,stickyControls:U,onReady:f,onPlay:k,onPause:E,onEnded:x,onTimeUpdate:S,onVolumeChange:C,onError:L,onLoadedMetadata:T,onQualityChange:P,style:H,className:D,width:v="100%",height:y="500px"}=l,w=h.useRef(null),q=h.useRef(null);return h.useEffect(()=>{if(!w.current)return;const V={src:t,container:w.current,autoplay:e,muted:n,controls:s,poster:i,preload:a,theme:o,s3Config:c,analytics:u,hlsConfig:g,subtitles:z,stickyControls:U},d=new b(V);return q.current=d,k&&d.on("play",k),E&&d.on("pause",E),x&&d.on("ended",x),L&&d.on("error",p=>{var I;return L((I=p.data)==null?void 0:I.error)}),T&&d.on("loadedmetadata",T),P&&d.on("qualitychange",p=>P(p.data.level)),S&&d.on("timeupdate",p=>S(p.data.currentTime)),C&&d.on("volumechange",p=>C(p.data.volume,p.data.muted)),f&&f(d),()=>{d.destroy(),q.current=null}},[t]),M.jsx("div",{ref:w,className:D,style:{width:typeof v=="number"?`${v}px`:v,height:typeof y=="number"?`${y}px`:y,...H}})},O=l=>{const[t,e]=h.useState(null),[n,s]=h.useState(null),i=h.useRef(null);return h.useEffect(()=>{if(!i.current)return;const a=new b({...l,container:i.current});e(a);const o=()=>{s(a.getState())};return a.on("play",o),a.on("pause",o),a.on("timeupdate",o),a.on("volumechange",o),a.on("loadedmetadata",o),()=>{a.destroy()}},[l.src]),{containerRef:i,player:t,state:n}},$=R.createContext({player:null,state:null}),Q=l=>{const{player:t,children:e}=l,[n,s]=h.useState(t.getState());return h.useEffect(()=>{const i=()=>{s(t.getState())};return t.on("play",i),t.on("pause",i),t.on("timeupdate",i),t.on("volumechange",i),t.on("loadedmetadata",i),()=>{}},[t]),M.jsx($.Provider,{value:{player:t,state:n},children:e})},Y=()=>{const l=R.useContext($);if(!l.player)throw new Error("useWontumPlayerContext must be used within WontumPlayerProvider");return l};exports.Analytics=B;exports.S3Handler=F;exports.UIController=A;exports.WontumPlayer=b;exports.WontumPlayerProvider=Q;exports.WontumPlayerReact=N;exports.useWontumPlayer=O;exports.useWontumPlayerContext=Y;
|