@obipascal/player 1.0.7 → 1.0.8

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 CHANGED
@@ -1134,6 +1134,92 @@ pipButton.addEventListener("click", async () => {
1134
1134
 
1135
1135
  **Note:** Picture-in-Picture is supported in most modern browsers. The player includes a built-in PiP button in the controls.
1136
1136
 
1137
+ ### File Information Utility
1138
+
1139
+ The SDK includes a `WontumFileInfo` utility class to extract metadata from video files before uploading or processing them.
1140
+
1141
+ ```typescript
1142
+ import { WontumFileInfo } from "@obipascal/player"
1143
+
1144
+ // Example: File input handling
1145
+ const fileInput = document.querySelector<HTMLInputElement>("#video-upload")
1146
+
1147
+ fileInput.addEventListener("change", async (event) => {
1148
+ const file = event.target.files?.[0]
1149
+ if (!file) return
1150
+
1151
+ try {
1152
+ // Create instance (validates it's a video file)
1153
+ const videoInfo = new WontumFileInfo(file)
1154
+
1155
+ // Extract metadata
1156
+ await videoInfo.extract()
1157
+
1158
+ // Access properties
1159
+ console.log("Video Information:")
1160
+ console.log("- Width:", videoInfo.width) // e.g., 1920
1161
+ console.log("- Height:", videoInfo.height) // e.g., 1080
1162
+ console.log("- Aspect Ratio:", videoInfo.aspectRatio) // e.g., "16:9"
1163
+ console.log("- Quality:", videoInfo.quality) // e.g., "Full HD (1080p)"
1164
+ console.log("- Duration (raw):", videoInfo.durationInSeconds, "seconds") // e.g., 125.5
1165
+ console.log("- Formatted Duration:", videoInfo.durationFormatted) // e.g., "02:05"
1166
+ console.log("- File Size (raw):", videoInfo.sizeInBytes, "bytes") // e.g., 52428800
1167
+ console.log("- Formatted Size:", videoInfo.sizeFormatted) // e.g., "50 MB"
1168
+ console.log("- MIME Type:", videoInfo.mimeType) // e.g., "video/mp4"
1169
+ console.log("- File Name:", videoInfo.fileName) // e.g., "my-video.mp4"
1170
+ console.log("- Extension:", videoInfo.fileExtension) // e.g., ".mp4"
1171
+ console.log("- Bitrate:", videoInfo.bitrate, "kbps") // e.g., 3500
1172
+
1173
+ // Get all info as object
1174
+ const allInfo = videoInfo.getInfo()
1175
+ console.log(allInfo)
1176
+
1177
+ // Clean up when done
1178
+ videoInfo.destroy()
1179
+ } catch (error) {
1180
+ console.error("Error extracting video info:", error.message)
1181
+ // Throws error if file is not a video
1182
+ }
1183
+ })
1184
+ ```
1185
+
1186
+ #### WontumFileInfo API
1187
+
1188
+ **Constructor:**
1189
+
1190
+ ```typescript
1191
+ new WontumFileInfo(file: File)
1192
+ ```
1193
+
1194
+ Throws an error if the file is not a valid video file.
1195
+
1196
+ **Methods:**
1197
+
1198
+ - `extract(): Promise<VideoFileInfo>` - Extracts metadata from the video file
1199
+ - `getInfo(): VideoFileInfo | null` - Returns the extracted information object
1200
+ - `destroy(): void` - Cleans up resources
1201
+
1202
+ **Properties (available after calling `extract()`):**
1203
+
1204
+ - `width: number` - Video width in pixels
1205
+ - `height: number` - Video height in pixels
1206
+ - `aspectRatio: string` - Aspect ratio (e.g., "16:9", "4:3", "21:9")
1207
+ - `quality: string` - Quality description (e.g., "4K (2160p)", "Full HD (1080p)")
1208
+ - `size: number` - File size in bytes (raw value for computation)
1209
+ - `sizeInBytes: number` - Alias for size (raw value for computation)
1210
+ - `sizeFormatted: string` - Human-readable size (e.g., "50 MB")
1211
+ - `duration: number` - Duration in seconds (raw value for computation)
1212
+ - `durationInSeconds: number` - Alias for duration (raw value for computation)
1213
+ - `durationFormatted: string` - Formatted duration (e.g., "01:23:45")
1214
+ - `mimeType: string` - MIME type (e.g., "video/mp4")
1215
+ - `fileName: string` - Original file name
1216
+ - `fileExtension: string` - File extension (e.g., ".mp4")
1217
+ - `bitrate: number | undefined` - Estimated bitrate in kbps
1218
+
1219
+ **Supported Video Formats:**
1220
+
1221
+ `.mp4`, `.webm`, `.ogg`, `.mov`, `.avi`, `.mkv`, `.flv`, `.wmv`, `.m4v`, `.3gp`, `.ts`, `.m3u8`
1222
+
1137
1223
  ## 📋 Complete API Reference
1138
1224
 
1139
1225
  For detailed API documentation including all methods, events, types, and configuration options, see **[API-REFERENCE.md](./API-REFERENCE.md)**.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Video file information extractor
3
+ * Extracts metadata from video files (width, height, duration, size, etc.)
4
+ */
5
+ export interface VideoFileInfo {
6
+ width: number;
7
+ height: number;
8
+ aspectRatio: string;
9
+ size: number;
10
+ sizeInBytes: number;
11
+ sizeFormatted: string;
12
+ duration: number;
13
+ durationInSeconds: number;
14
+ durationFormatted: string;
15
+ mimeType: string;
16
+ fileName: string;
17
+ fileExtension: string;
18
+ bitrate?: number;
19
+ frameRate?: number;
20
+ videoCodec?: string;
21
+ audioCodec?: string;
22
+ }
23
+ export declare class WontumFileInfo {
24
+ private file;
25
+ private videoElement;
26
+ private info;
27
+ constructor(file: File);
28
+ /**
29
+ * Check if the file is a valid video file
30
+ */
31
+ private isVideoFile;
32
+ /**
33
+ * Extract video metadata
34
+ */
35
+ extract(): Promise<VideoFileInfo>;
36
+ /**
37
+ * Calculate aspect ratio (e.g., "16:9", "4:3")
38
+ */
39
+ private calculateAspectRatio;
40
+ /**
41
+ * Get Greatest Common Divisor
42
+ */
43
+ private getGCD;
44
+ /**
45
+ * Format bytes to human-readable size
46
+ */
47
+ private formatBytes;
48
+ /**
49
+ * Format duration to HH:MM:SS
50
+ */
51
+ private formatDuration;
52
+ /**
53
+ * Get file extension
54
+ */
55
+ private getFileExtension;
56
+ get width(): number;
57
+ get height(): number;
58
+ get aspectRatio(): string;
59
+ get size(): number;
60
+ get sizeInBytes(): number;
61
+ get sizeFormatted(): string;
62
+ get duration(): number;
63
+ get durationInSeconds(): number;
64
+ get durationFormatted(): string;
65
+ get mimeType(): string;
66
+ get fileName(): string;
67
+ get fileExtension(): string;
68
+ get bitrate(): number | undefined;
69
+ get quality(): string;
70
+ /**
71
+ * Get all information as object
72
+ */
73
+ getInfo(): VideoFileInfo | null;
74
+ /**
75
+ * Clean up resources
76
+ */
77
+ destroy(): void;
78
+ }
@@ -2,6 +2,8 @@ export { WontumPlayer } from './player';
2
2
  export { Analytics } from './analytics';
3
3
  export { S3Handler } from './s3-handler';
4
4
  export { UIController } from './ui-controller';
5
+ export { WontumFileInfo } from './file-info';
5
6
  export { WontumPlayerReact, useWontumPlayer, WontumPlayerProvider, useWontumPlayerContext } from './react';
6
7
  export type { WontumPlayerConfig, PlayerTheme, S3Config, AnalyticsConfig, PlayerState, PlayerEvent, PlayerEventType, AnalyticsEvent, QualityLevel } from './types';
8
+ export type { VideoFileInfo } from './file-info';
7
9
  export type { WontumPlayerReactProps } from './react';
@@ -1,14 +1,14 @@
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=`
1
+ "use strict";var W=Object.defineProperty;var j=(c,t,e)=>t in c?W(c,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):c[t]=e;var o=(c,t,e)=>j(c,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("hls.js"),B=require("react/jsx-runtime"),h=require("react");function N(c){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(c){for(const e in c)if(e!=="default"){const i=Object.getOwnPropertyDescriptor(c,e);Object.defineProperty(t,e,i.get?i:{enumerable:!0,get:()=>c[e]})}}return t.default=c,Object.freeze(t)}const F=N(h);class ${constructor(t){o(this,"config");o(this,"sessionId");o(this,"events",[]);o(this,"sessionStartTime");o(this,"playbackStartTime",null);o(this,"totalPlayTime",0);o(this,"totalBufferTime",0);o(this,"bufferStartTime",null);o(this,"rebufferCount",0);o(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 n;if(!((n=this.config)!=null&&n.enabled))return;const i={eventType:t,timestamp:Date.now(),sessionId:this.sessionId,videoId:this.config.videoId,userId:this.config.userId,data:{...e,...this.getQoEMetrics()}};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)}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(i){console.error("Failed to send analytics event:",i)}}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){o(this,"container");o(this,"player");o(this,"controlsContainer");o(this,"progressContainer");o(this,"progressBar");o(this,"playButton");o(this,"skipBackwardButton");o(this,"skipForwardButton");o(this,"volumeButton");o(this,"volumeContainer");o(this,"fullscreenButton");o(this,"pipButton");o(this,"settingsButton");o(this,"volumeSlider");o(this,"progressInput");o(this,"hideControlsTimeout",null);o(this,"stickyControls",!1);o(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||{},i=e.primaryColor||"#3b82f6",n=e.accentColor||"#2563eb",s=e.fontFamily||"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",r=e.controlsBackground||"linear-gradient(to top, rgba(0,0,0,0.8), transparent)",a=e.buttonHoverBg||"rgba(255, 255, 255, 0.1)",l=e.progressHeight||"6px",u=e.borderRadius||"4px",p=document.createElement("style");p.id=t,p.textContent=`
2
2
  .wontum-player-container {
3
3
  position: relative;
4
4
  background: #000;
5
- font-family: ${i};
5
+ font-family: ${s};
6
6
  overflow: hidden;
7
- --primary-color: ${n};
8
- --accent-color: ${s};
9
- --controls-bg: ${a};
10
- --button-hover: ${o};
11
- --progress-height: ${c};
7
+ --primary-color: ${i};
8
+ --accent-color: ${n};
9
+ --controls-bg: ${r};
10
+ --button-hover: ${a};
11
+ --progress-height: ${l};
12
12
  --border-radius: ${u};
13
13
  }
14
14
 
@@ -486,7 +486,7 @@
486
486
  transform: translateY(0) !important;
487
487
  pointer-events: all !important;
488
488
  }
489
- `,document.head.appendChild(g),this.container.classList.add("wontum-player-container")}createProgressBar(){const t=document.createElement("div");t.className="wontum-progress-container",t.innerHTML=`
489
+ `,document.head.appendChild(p),this.container.classList.add("wontum-player-container")}createProgressBar(){const t=document.createElement("div");t.className="wontum-progress-container",t.innerHTML=`
490
490
  <div class="wontum-progress-track"></div>
491
491
  <div class="wontum-progress-filled"></div>
492
492
  <input type="range" class="wontum-progress-input" min="0" max="100" value="0" step="0.1">
@@ -562,28 +562,28 @@
562
562
  <div class="wontum-loading" style="display: none;">
563
563
  <div class="wontum-spinner"></div>
564
564
  </div>
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=`
566
- <div class="wontum-subtitle-option ${n===-1?"active":""}" data-track="-1">Off</div>
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}`}
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",i=>{const n=i.target,s=parseFloat(n.value),r=this.player.getState(),a=s/100*r.duration;this.player.seek(a)}),this.volumeSlider.addEventListener("input",i=>{const n=i.target,s=parseFloat(n.value)/100;this.player.setVolume(s)}),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(i){console.error("PiP error:",i)}}),this.settingsButton.addEventListener("click",()=>{const i=this.controlsContainer.querySelector(".wontum-settings-panel");i.classList.toggle("active"),i.classList.contains("active")&&(this.updateSettingsMenu(),this.updateQualityMenu(),this.updateSpeedMenu(),this.updateSubtitleMenu())});const t=this.controlsContainer.querySelectorAll(".wontum-tab");t.forEach(i=>{i.addEventListener("click",n=>{const s=n.currentTarget,r=s.getAttribute("data-tab");t.forEach(u=>u.classList.remove("active")),s.classList.add("active"),this.controlsContainer.querySelectorAll(".wontum-tab-panel").forEach(u=>u.classList.remove("active"));const l=this.controlsContainer.querySelector(`[data-panel="${r}"]`);l==null||l.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,i=this.player.getState();if(i.duration>0){const s=e/i.duration*100;this.progressBar.style.width=`${s}%`,this.progressInput.value=s.toString()}const n=this.controlsContainer.querySelector(".wontum-current-time");n.textContent=this.formatTime(e)}),this.player.on("loadedmetadata",t=>{const{duration:e}=t.data,i=this.controlsContainer.querySelector(".wontum-duration");i.textContent=this.formatTime(e),t.data.qualities&&this.updateQualityMenu(t.data.qualities)}),this.player.on("volumechange",t=>{const{volume:e,muted:i}=t.data;this.volumeSlider.value=(e*100).toString(),this.volumeButton.innerHTML=i?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 i=e.findIndex(n=>n.mode==="showing");t.innerHTML=`
566
+ <div class="wontum-subtitle-option ${i===-1?"active":""}" data-track="-1">Off</div>
567
+ ${e.map((n,s)=>`
568
+ <div class="wontum-subtitle-option ${s===i?"active":""}" data-track="${s}">
569
+ ${n.label||n.language||`Track ${s+1}`}
570
570
  </div>
571
571
  `).join("")}
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"}
572
+ `,t.querySelectorAll(".wontum-subtitle-option").forEach(n=>{n.addEventListener("click",s=>{const r=s.target,a=parseInt(r.dataset.track||"-1");a===-1?this.player.disableSubtitles():this.player.enableSubtitles(a),t.querySelectorAll(".wontum-subtitle-option").forEach(l=>l.classList.remove("active")),r.classList.add("active")})})}updateSpeedMenu(){const t=this.controlsContainer.querySelector(".wontum-speed-menu"),i=this.player.getState().playbackRate||1,n=[.25,.5,.75,1,1.25,1.5,1.75,2];t.innerHTML=n.map(s=>`
573
+ <div class="wontum-speed-option ${i===s?"active":""}" data-speed="${s}">
574
+ ${s===1?"Normal":s+"x"}
575
575
  </div>
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=`
576
+ `).join(""),t.querySelectorAll(".wontum-speed-option").forEach(s=>{s.addEventListener("click",r=>{const a=r.target,l=parseFloat(a.dataset.speed||"1");this.player.setPlaybackRate(l),t.querySelectorAll(".wontum-speed-option").forEach(u=>u.classList.remove("active")),a.classList.add("active")})})}updateSettingsMenu(){const t=this.controlsContainer.querySelector(".wontum-settings-menu");t.innerHTML=`
577
577
  <div class="wontum-settings-option" data-setting="sticky-controls">
578
578
  <span>Sticky Controls</span>
579
579
  <div class="wontum-toggle-switch ${this.stickyControls?"active":""}"></div>
580
580
  </div>
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=`
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"),i=t||this.player.getQualities();if(!i||i.length===0){e.innerHTML='<div class="wontum-quality-option">No qualities available</div>';return}e.innerHTML=`
582
582
  <div class="wontum-quality-option active" data-quality="-1">Auto</div>
583
- ${n.map((s,i)=>`
584
- <div class="wontum-quality-option" data-quality="${i}">${s.name}</div>
583
+ ${i.map((n,s)=>`
584
+ <div class="wontum-quality-option" data-quality="${s}">${n.name}</div>
585
585
  `).join("")}
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">
586
+ `,e.querySelectorAll(".wontum-quality-option").forEach(n=>{n.addEventListener("click",s=>{const r=s.target,a=parseInt(r.dataset.quality||"-1");this.player.setQuality(a),e.querySelectorAll(".wontum-quality-option").forEach(l=>l.classList.remove("active")),r.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),i=Math.floor(t%60);return`${e}:${i.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">
587
587
  <circle cx="30" cy="30" r="28" stroke="white" stroke-width="2"/>
588
588
  <!-- Circular arrow backward -->
589
589
  <path d="M30 12 A18 18 0 1 0 30 48" stroke="white" stroke-width="2.5" stroke-linecap="round" fill="none"/>
@@ -595,4 +595,4 @@
595
595
  <path d="M30 12 A18 18 0 1 1 30 48" stroke="white" stroke-width="2.5" stroke-linecap="round" fill="none"/>
596
596
  <path d="M35 12 L30 12 L30 17" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
597
597
  <text x="30" y="35" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="white" text-anchor="middle">10</text>
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;
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 z{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 i=new URL(t);return this.config.cloudFrontDomains.some(n=>i.hostname.includes(n))}catch{return!1}}isS3Url(t){return t.includes(".s3.")||t.includes("s3.amazonaws.com")||t.startsWith("s3://")}async signCloudFrontUrl(t,e=0){var s,r;if(this.signedUrls.has(t))return t;if((s=this.config)!=null&&s.signUrl)try{const a=await this.config.signUrl(t);return this.signedUrls.add(t),a}catch(a){const l=(a==null?void 0:a.name)==="AbortError"||((r=a==null?void 0:a.message)==null?void 0:r.includes("aborted"));if(l&&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:",a),l?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: ${(a==null?void 0:a.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 i=t.match(/([^.]+)\.s3\.amazonaws\.com\/(.+)/);return i?i[2]:t}async getPresignedUrl(t){var n;const e=this.extractS3Key(t),i=this.urlCache.get(e);if(i&&i.expiresAt>Date.now())return i.url;if((n=this.config)!=null&&n.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,i="us-east-1"){return`https://${t}.s3.${i}.amazonaws.com/${e}`}static parseS3Uri(t){if(!t.startsWith("s3://"))return null;const e=t.replace("s3://","").split("/"),i=e[0],n=e.slice(1).join("/");return{bucket:i,key:n}}clearCache(){this.urlCache.clear(),this.signedUrls.clear()}}class k{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 $(t.analytics),this.s3Handler=new z(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 i=document.createElement("track");i.kind="subtitles",i.label=e.label,i.src=e.src,i.srclang=e.srclang,e.default&&(i.default=!0),this.videoElement.appendChild(i)})}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 i=await this.s3Handler.processUrl(t);if(g.isSupported()){const n=((e=this.config.s3Config)==null?void 0:e.withCredentials)??!1,s={...this.config.hlsConfig,xhrSetup:(r,a)=>{var l;n&&(r.withCredentials=!0),(l=this.config.hlsConfig)!=null&&l.xhrSetup&&this.config.hlsConfig.xhrSetup(r,a)}};this.hls=new g(s),this.hls.loadSource(i),this.hls.attachMedia(this.videoElement),this.hls.on(g.Events.MANIFEST_PARSED,(r,a)=>{const l=this.extractQualities(a.levels);this.qualities=l}),this.hls.on(g.Events.LEVEL_SWITCHED,(r,a)=>{var u;const l=(u=this.hls)==null?void 0:u.levels[a.level];l&&(this.state.quality=`${l.height}p`,this.emit("qualitychange",{quality:this.state.quality}))}),this.hls.on(g.Events.ERROR,(r,a)=>{a.fatal&&this.handleHlsError(a)})}else if(this.videoElement.canPlayType("application/vnd.apple.mpegurl"))this.videoElement.src=i;else throw new Error("HLS is not supported in this browser")}catch(i){console.error("Failed to load video source:",i),this.emit("error",{error:i})}}extractQualities(t){return t.map(e=>({height:e.height,width:e.width,bitrate:e.bitrate,name:`${e.height}p`}))}handleHlsError(t){var e,i;switch(t.type){case g.ErrorTypes.NETWORK_ERROR:console.error("Network error occurred"),(e=this.hls)==null||e.startLoad();break;case g.ErrorTypes.MEDIA_ERROR:console.error("Media error occurred"),(i=this.hls)==null||i.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 i=0;i<e.length;i++)e[i].mode=i===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(i=>i.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 i;(i=this.eventListeners.get(t))==null||i.delete(e)}emit(t,e){var n;const i={type:t,data:e,timestamp:Date.now()};(n=this.eventListeners.get(t))==null||n.forEach(s=>{s(i)})}destroy(){this.hls&&(this.hls.destroy(),this.hls=null),this.uiController.destroy(),this.videoElement.remove(),this.eventListeners.clear(),this.analytics.destroy()}}class O{constructor(t){o(this,"file");o(this,"videoElement",null);o(this,"info",null);if(!this.isVideoFile(t))throw new Error(`Invalid file type: ${t.type}. Expected a video file.`);this.file=t}isVideoFile(t){if(t.type.startsWith("video/"))return!0;const e=[".mp4",".webm",".ogg",".mov",".avi",".mkv",".flv",".wmv",".m4v",".3gp",".ts",".m3u8"],i=t.name.toLowerCase();return e.some(n=>i.endsWith(n))}async extract(){return new Promise((t,e)=>{try{this.videoElement=document.createElement("video"),this.videoElement.preload="metadata",this.videoElement.muted=!0;const i=URL.createObjectURL(this.file);this.videoElement.onloadedmetadata=()=>{try{if(!this.videoElement){e(new Error("Video element not initialized"));return}const n=this.videoElement.videoWidth,s=this.videoElement.videoHeight,r=this.videoElement.duration,a=this.calculateAspectRatio(n,s),l=this.file.size,u=this.formatBytes(l),p=this.formatDuration(r),v=this.getFileExtension(this.file.name),f=r>0?Math.round(l*8/r/1e3):void 0;this.info={width:n,height:s,aspectRatio:a,size:l,sizeInBytes:l,sizeFormatted:u,duration:r,durationInSeconds:r,durationFormatted:p,mimeType:this.file.type||"video/unknown",fileName:this.file.name,fileExtension:v,bitrate:f},URL.revokeObjectURL(i),this.videoElement.remove(),t(this.info)}catch(n){URL.revokeObjectURL(i),e(n)}},this.videoElement.onerror=()=>{URL.revokeObjectURL(i),e(new Error(`Failed to load video file: ${this.file.name}`))},this.videoElement.src=i}catch(i){e(i)}})}calculateAspectRatio(t,e){const i=this.getGCD(t,e),n=t/i,s=e/i,r=n/s;return Math.abs(r-16/9)<.01?"16:9":Math.abs(r-4/3)<.01?"4:3":Math.abs(r-21/9)<.01?"21:9":Math.abs(r-1)<.01?"1:1":`${n}:${s}`}getGCD(t,e){return e===0?t:this.getGCD(e,t%e)}formatBytes(t){if(t===0)return"0 Bytes";const e=1024,i=["Bytes","KB","MB","GB","TB"],n=Math.floor(Math.log(t)/Math.log(e));return`${parseFloat((t/Math.pow(e,n)).toFixed(2))} ${i[n]}`}formatDuration(t){if(!isFinite(t)||t<0)return"00:00";const e=Math.floor(t/3600),i=Math.floor(t%3600/60),n=Math.floor(t%60);return e>0?`${e.toString().padStart(2,"0")}:${i.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}`:`${i.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}`}getFileExtension(t){const e=t.split(".");return e.length>1?`.${e[e.length-1].toLowerCase()}`:""}get width(){var t;return((t=this.info)==null?void 0:t.width)||0}get height(){var t;return((t=this.info)==null?void 0:t.height)||0}get aspectRatio(){var t;return((t=this.info)==null?void 0:t.aspectRatio)||"unknown"}get size(){var t;return((t=this.info)==null?void 0:t.size)||0}get sizeInBytes(){var t;return((t=this.info)==null?void 0:t.sizeInBytes)||0}get sizeFormatted(){var t;return((t=this.info)==null?void 0:t.sizeFormatted)||"0 Bytes"}get duration(){var t;return((t=this.info)==null?void 0:t.duration)||0}get durationInSeconds(){var t;return((t=this.info)==null?void 0:t.durationInSeconds)||0}get durationFormatted(){var t;return((t=this.info)==null?void 0:t.durationFormatted)||"00:00"}get mimeType(){var t;return((t=this.info)==null?void 0:t.mimeType)||this.file.type||"video/unknown"}get fileName(){return this.file.name}get fileExtension(){var t;return((t=this.info)==null?void 0:t.fileExtension)||""}get bitrate(){var t;return(t=this.info)==null?void 0:t.bitrate}get quality(){if(!this.info)return"unknown";const t=this.info.height;return t>=2160?"4K (2160p)":t>=1440?"2K (1440p)":t>=1080?"Full HD (1080p)":t>=720?"HD (720p)":t>=480?"SD (480p)":t>=360?"360p":"Low Quality"}getInfo(){return this.info}destroy(){this.videoElement&&(this.videoElement.remove(),this.videoElement=null),this.info=null}}const _=c=>{const{src:t,autoplay:e,muted:i,controls:n=!0,poster:s,preload:r,theme:a,s3Config:l,analytics:u,hlsConfig:p,subtitles:v,stickyControls:f,onReady:E,onPlay:x,onPause:S,onEnded:C,onTimeUpdate:L,onVolumeChange:T,onError:P,onLoadedMetadata:I,onQualityChange:M,style:D,className:H,width:y="100%",height:w="500px"}=c,b=h.useRef(null),q=h.useRef(null);return h.useEffect(()=>{if(!b.current)return;const V={src:t,container:b.current,autoplay:e,muted:i,controls:n,poster:s,preload:r,theme:a,s3Config:l,analytics:u,hlsConfig:p,subtitles:v,stickyControls:f},d=new k(V);return q.current=d,x&&d.on("play",x),S&&d.on("pause",S),C&&d.on("ended",C),P&&d.on("error",m=>{var R;return P((R=m.data)==null?void 0:R.error)}),I&&d.on("loadedmetadata",I),M&&d.on("qualitychange",m=>M(m.data.level)),L&&d.on("timeupdate",m=>L(m.data.currentTime)),T&&d.on("volumechange",m=>T(m.data.volume,m.data.muted)),E&&E(d),()=>{d.destroy(),q.current=null}},[t]),B.jsx("div",{ref:b,className:H,style:{width:typeof y=="number"?`${y}px`:y,height:typeof w=="number"?`${w}px`:w,...D}})},Q=c=>{const[t,e]=h.useState(null),[i,n]=h.useState(null),s=h.useRef(null);return h.useEffect(()=>{if(!s.current)return;const r=new k({...c,container:s.current});e(r);const a=()=>{n(r.getState())};return r.on("play",a),r.on("pause",a),r.on("timeupdate",a),r.on("volumechange",a),r.on("loadedmetadata",a),()=>{r.destroy()}},[c.src]),{containerRef:s,player:t,state:i}},U=F.createContext({player:null,state:null}),Y=c=>{const{player:t,children:e}=c,[i,n]=h.useState(t.getState());return h.useEffect(()=>{const s=()=>{n(t.getState())};return t.on("play",s),t.on("pause",s),t.on("timeupdate",s),t.on("volumechange",s),t.on("loadedmetadata",s),()=>{}},[t]),B.jsx(U.Provider,{value:{player:t,state:i},children:e})},X=()=>{const c=F.useContext(U);if(!c.player)throw new Error("useWontumPlayerContext must be used within WontumPlayerProvider");return c};exports.Analytics=$;exports.S3Handler=z;exports.UIController=A;exports.WontumFileInfo=O;exports.WontumPlayer=k;exports.WontumPlayerProvider=Y;exports.WontumPlayerReact=_;exports.useWontumPlayer=Q;exports.useWontumPlayerContext=X;
@@ -1,28 +1,28 @@
1
1
  var V = Object.defineProperty;
2
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);
3
+ var o = (c, t, e) => N(c, typeof t != "symbol" ? t + "" : t, e);
4
4
  import p from "hls.js";
5
- import { jsx as B } from "react/jsx-runtime";
6
- import * as R from "react";
7
- import { useRef as y, useEffect as f, useState as b } from "react";
8
- class _ {
5
+ import { jsx as F } from "react/jsx-runtime";
6
+ import * as $ from "react";
7
+ import { useRef as b, useEffect as E, useState as k } from "react";
8
+ class W {
9
9
  constructor(t) {
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);
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);
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 s;
25
- if (!((s = this.config) != null && s.enabled)) return;
24
+ var n;
25
+ if (!((n = this.config) != null && n.enabled)) return;
26
26
  const i = {
27
27
  eventType: t,
28
28
  timestamp: Date.now(),
@@ -119,44 +119,44 @@ class _ {
119
119
  (t = this.config) != null && t.enabled && this.trackEvent("session_end", this.getSessionData()), this.events = [];
120
120
  }
121
121
  }
122
- class Q {
122
+ class j {
123
123
  constructor(t, e) {
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");
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, "volumeContainer");
134
+ o(this, "fullscreenButton");
135
+ o(this, "pipButton");
136
+ o(this, "settingsButton");
137
137
  // private timeDisplay: HTMLElement
138
- r(this, "volumeSlider");
139
- r(this, "progressInput");
138
+ o(this, "volumeSlider");
139
+ o(this, "progressInput");
140
140
  // private controlsVisible = true
141
- r(this, "hideControlsTimeout", null);
142
- r(this, "stickyControls", !1);
143
- r(this, "isVolumeSliderActive", !1);
141
+ o(this, "hideControlsTimeout", null);
142
+ o(this, "stickyControls", !1);
143
+ o(this, "isVolumeSliderActive", !1);
144
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();
145
145
  }
146
146
  injectStyles() {
147
147
  const t = "wontum-player-styles";
148
148
  if (document.getElementById(t)) return;
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");
149
+ const e = this.player.config.theme || {}, i = e.primaryColor || "#3b82f6", n = e.accentColor || "#2563eb", s = e.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", r = e.controlsBackground || "linear-gradient(to top, rgba(0,0,0,0.8), transparent)", a = e.buttonHoverBg || "rgba(255, 255, 255, 0.1)", l = e.progressHeight || "6px", u = e.borderRadius || "4px", m = document.createElement("style");
150
150
  m.id = t, m.textContent = `
151
151
  .wontum-player-container {
152
152
  position: relative;
153
153
  background: #000;
154
- font-family: ${n};
154
+ font-family: ${s};
155
155
  overflow: hidden;
156
156
  --primary-color: ${i};
157
- --accent-color: ${s};
158
- --controls-bg: ${a};
159
- --button-hover: ${o};
157
+ --accent-color: ${n};
158
+ --controls-bg: ${r};
159
+ --button-hover: ${a};
160
160
  --progress-height: ${l};
161
161
  --border-radius: ${u};
162
162
  }
@@ -729,11 +729,11 @@ class Q {
729
729
  }), this.skipForwardButton.addEventListener("click", () => {
730
730
  this.player.skipForward(10);
731
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);
732
+ const n = i.target, s = parseFloat(n.value), r = this.player.getState(), a = s / 100 * r.duration;
733
+ this.player.seek(a);
734
734
  }), this.volumeSlider.addEventListener("input", (i) => {
735
- const s = i.target, n = parseFloat(s.value) / 100;
736
- this.player.setVolume(n);
735
+ const n = i.target, s = parseFloat(n.value) / 100;
736
+ this.player.setVolume(s);
737
737
  }), this.volumeButton.addEventListener("click", () => {
738
738
  this.player.getState().muted ? this.player.unmute() : this.player.mute();
739
739
  }), this.volumeContainer.addEventListener("mouseenter", () => {
@@ -760,10 +760,10 @@ class Q {
760
760
  });
761
761
  const t = this.controlsContainer.querySelectorAll(".wontum-tab");
762
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}"]`);
763
+ i.addEventListener("click", (n) => {
764
+ const s = n.currentTarget, r = s.getAttribute("data-tab");
765
+ t.forEach((u) => u.classList.remove("active")), s.classList.add("active"), this.controlsContainer.querySelectorAll(".wontum-tab-panel").forEach((u) => u.classList.remove("active"));
766
+ const l = this.controlsContainer.querySelector(`[data-panel="${r}"]`);
767
767
  l == null || l.classList.add("active");
768
768
  });
769
769
  }), this.player.getVideoElement().addEventListener("click", () => {
@@ -782,11 +782,11 @@ class Q {
782
782
  }), this.player.on("timeupdate", (t) => {
783
783
  const { currentTime: e } = t.data, i = this.player.getState();
784
784
  if (i.duration > 0) {
785
- const n = e / i.duration * 100;
786
- this.progressBar.style.width = `${n}%`, this.progressInput.value = n.toString();
785
+ const s = e / i.duration * 100;
786
+ this.progressBar.style.width = `${s}%`, this.progressInput.value = s.toString();
787
787
  }
788
- const s = this.controlsContainer.querySelector(".wontum-current-time");
789
- s.textContent = this.formatTime(e);
788
+ const n = this.controlsContainer.querySelector(".wontum-current-time");
789
+ n.textContent = this.formatTime(e);
790
790
  }), this.player.on("loadedmetadata", (t) => {
791
791
  const { duration: e } = t.data, i = this.controlsContainer.querySelector(".wontum-duration");
792
792
  i.textContent = this.formatTime(e), t.data.qualities && this.updateQualityMenu(t.data.qualities);
@@ -807,35 +807,35 @@ class Q {
807
807
  t.innerHTML = '<div class="wontum-subtitle-option">No subtitles available</div>';
808
808
  return;
809
809
  }
810
- const i = e.findIndex((s) => s.mode === "showing");
810
+ const i = e.findIndex((n) => n.mode === "showing");
811
811
  t.innerHTML = `
812
812
  <div class="wontum-subtitle-option ${i === -1 ? "active" : ""}" data-track="-1">Off</div>
813
813
  ${e.map(
814
- (s, n) => `
815
- <div class="wontum-subtitle-option ${n === i ? "active" : ""}" data-track="${n}">
816
- ${s.label || s.language || `Track ${n + 1}`}
814
+ (n, s) => `
815
+ <div class="wontum-subtitle-option ${s === i ? "active" : ""}" data-track="${s}">
816
+ ${n.label || n.language || `Track ${s + 1}`}
817
817
  </div>
818
818
  `
819
819
  ).join("")}
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");
820
+ `, t.querySelectorAll(".wontum-subtitle-option").forEach((n) => {
821
+ n.addEventListener("click", (s) => {
822
+ const r = s.target, a = parseInt(r.dataset.track || "-1");
823
+ a === -1 ? this.player.disableSubtitles() : this.player.enableSubtitles(a), t.querySelectorAll(".wontum-subtitle-option").forEach((l) => l.classList.remove("active")), r.classList.add("active");
824
824
  });
825
825
  });
826
826
  }
827
827
  updateSpeedMenu() {
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"}
828
+ const t = this.controlsContainer.querySelector(".wontum-speed-menu"), i = this.player.getState().playbackRate || 1, n = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
829
+ t.innerHTML = n.map(
830
+ (s) => `
831
+ <div class="wontum-speed-option ${i === s ? "active" : ""}" data-speed="${s}">
832
+ ${s === 1 ? "Normal" : s + "x"}
833
833
  </div>
834
834
  `
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");
835
+ ).join(""), t.querySelectorAll(".wontum-speed-option").forEach((s) => {
836
+ s.addEventListener("click", (r) => {
837
+ const a = r.target, l = parseFloat(a.dataset.speed || "1");
838
+ this.player.setPlaybackRate(l), t.querySelectorAll(".wontum-speed-option").forEach((u) => u.classList.remove("active")), a.classList.add("active");
839
839
  });
840
840
  });
841
841
  }
@@ -861,14 +861,14 @@ class Q {
861
861
  e.innerHTML = `
862
862
  <div class="wontum-quality-option active" data-quality="-1">Auto</div>
863
863
  ${i.map(
864
- (s, n) => `
865
- <div class="wontum-quality-option" data-quality="${n}">${s.name}</div>
864
+ (n, s) => `
865
+ <div class="wontum-quality-option" data-quality="${s}">${n.name}</div>
866
866
  `
867
867
  ).join("")}
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");
868
+ `, e.querySelectorAll(".wontum-quality-option").forEach((n) => {
869
+ n.addEventListener("click", (s) => {
870
+ const r = s.target, a = parseInt(r.dataset.quality || "-1");
871
+ this.player.setQuality(a), e.querySelectorAll(".wontum-quality-option").forEach((l) => l.classList.remove("active")), r.classList.add("active");
872
872
  });
873
873
  });
874
874
  }
@@ -933,11 +933,11 @@ class Q {
933
933
  this.hideControlsTimeout && clearTimeout(this.hideControlsTimeout), this.controlsContainer.remove();
934
934
  }
935
935
  }
936
- class j {
936
+ class Q {
937
937
  constructor(t) {
938
- r(this, "config");
939
- r(this, "urlCache", /* @__PURE__ */ new Map());
940
- r(this, "signedUrls", /* @__PURE__ */ new Set());
938
+ o(this, "config");
939
+ o(this, "urlCache", /* @__PURE__ */ new Map());
940
+ o(this, "signedUrls", /* @__PURE__ */ new Set());
941
941
  this.config = t;
942
942
  }
943
943
  /**
@@ -955,7 +955,7 @@ class j {
955
955
  return !1;
956
956
  try {
957
957
  const i = new URL(t);
958
- return this.config.cloudFrontDomains.some((s) => i.hostname.includes(s));
958
+ return this.config.cloudFrontDomains.some((n) => i.hostname.includes(n));
959
959
  } catch {
960
960
  return !1;
961
961
  }
@@ -971,20 +971,20 @@ class j {
971
971
  * The endpoint should set signed cookies and return the URL
972
972
  */
973
973
  async signCloudFrontUrl(t, e = 0) {
974
- var n, a;
974
+ var s, r;
975
975
  if (this.signedUrls.has(t))
976
976
  return t;
977
- if ((n = this.config) != null && n.signUrl)
977
+ if ((s = this.config) != null && s.signUrl)
978
978
  try {
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"));
979
+ const a = await this.config.signUrl(t);
980
+ return this.signedUrls.add(t), a;
981
+ } catch (a) {
982
+ const l = (a == null ? void 0 : a.name) === "AbortError" || ((r = a == null ? void 0 : a.message) == null ? void 0 : r.includes("aborted"));
983
983
  if (l && e < 2)
984
984
  return console.warn(`Sign URL aborted, retrying (${e + 1}/2)...`), await new Promise((u) => setTimeout(u, 300)), this.signCloudFrontUrl(t, e + 1);
985
- throw console.error("Failed to sign CloudFront URL:", o), l ? new Error(
985
+ throw console.error("Failed to sign CloudFront URL:", a), l ? new Error(
986
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."
987
- ) : new Error(`Failed to sign CloudFront URL: ${(o == null ? void 0 : o.message) || "Unknown error"}`);
987
+ ) : new Error(`Failed to sign CloudFront URL: ${(a == null ? void 0 : a.message) || "Unknown error"}`);
988
988
  }
989
989
  return console.warn("No signUrl function provided. CloudFront cookies may not be set."), t;
990
990
  }
@@ -1004,19 +1004,19 @@ class j {
1004
1004
  * Get presigned URL from cache or generate new one
1005
1005
  */
1006
1006
  async getPresignedUrl(t) {
1007
- var s;
1007
+ var n;
1008
1008
  const e = this.extractS3Key(t), i = this.urlCache.get(e);
1009
1009
  if (i && i.expiresAt > Date.now())
1010
1010
  return i.url;
1011
- if ((s = this.config) != null && s.getPresignedUrl)
1011
+ if ((n = this.config) != null && n.getPresignedUrl)
1012
1012
  try {
1013
- const n = await this.config.getPresignedUrl(e);
1013
+ const s = await this.config.getPresignedUrl(e);
1014
1014
  return this.urlCache.set(e, {
1015
- url: n,
1015
+ url: s,
1016
1016
  expiresAt: Date.now() + 50 * 60 * 1e3
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");
1017
+ }), s;
1018
+ } catch (s) {
1019
+ throw console.error("Failed to generate presigned URL:", s), new Error("Failed to generate presigned URL for S3 object");
1020
1020
  }
1021
1021
  return console.warn("No getPresignedUrl function provided. Using direct S3 URL (requires public bucket)"), t;
1022
1022
  }
@@ -1032,8 +1032,8 @@ class j {
1032
1032
  static parseS3Uri(t) {
1033
1033
  if (!t.startsWith("s3://"))
1034
1034
  return null;
1035
- const e = t.replace("s3://", "").split("/"), i = e[0], s = e.slice(1).join("/");
1036
- return { bucket: i, key: s };
1035
+ const e = t.replace("s3://", "").split("/"), i = e[0], n = e.slice(1).join("/");
1036
+ return { bucket: i, key: n };
1037
1037
  }
1038
1038
  /**
1039
1039
  * Clear URL cache and signed URLs
@@ -1044,16 +1044,16 @@ class j {
1044
1044
  }
1045
1045
  class A {
1046
1046
  constructor(t) {
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", {
1047
+ o(this, "container");
1048
+ o(this, "videoElement");
1049
+ o(this, "hls", null);
1050
+ o(this, "config");
1051
+ o(this, "eventListeners", /* @__PURE__ */ new Map());
1052
+ o(this, "analytics");
1053
+ o(this, "s3Handler");
1054
+ o(this, "uiController");
1055
+ o(this, "qualities", []);
1056
+ o(this, "state", {
1057
1057
  playing: !1,
1058
1058
  paused: !0,
1059
1059
  ended: !1,
@@ -1069,7 +1069,7 @@ class A {
1069
1069
  });
1070
1070
  if (this.config = t, this.container = typeof t.container == "string" ? document.querySelector(t.container) : t.container, !this.container)
1071
1071
  throw new Error("Container element not found");
1072
- this.analytics = new _(t.analytics), this.s3Handler = new j(t.s3Config), this.videoElement = this.createVideoElement(), this.container.appendChild(this.videoElement), this.uiController = new Q(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);
1072
+ this.analytics = new W(t.analytics), this.s3Handler = new Q(t.s3Config), this.videoElement = this.createVideoElement(), this.container.appendChild(this.videoElement), this.uiController = new j(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);
1073
1073
  }
1074
1074
  addSubtitleTracks(t) {
1075
1075
  t.forEach((e) => {
@@ -1139,22 +1139,22 @@ class A {
1139
1139
  try {
1140
1140
  const i = await this.s3Handler.processUrl(t);
1141
1141
  if (p.isSupported()) {
1142
- const s = ((e = this.config.s3Config) == null ? void 0 : e.withCredentials) ?? !1, n = {
1142
+ const n = ((e = this.config.s3Config) == null ? void 0 : e.withCredentials) ?? !1, s = {
1143
1143
  ...this.config.hlsConfig,
1144
- xhrSetup: (a, o) => {
1144
+ xhrSetup: (r, a) => {
1145
1145
  var l;
1146
- s && (a.withCredentials = !0), (l = this.config.hlsConfig) != null && l.xhrSetup && this.config.hlsConfig.xhrSetup(a, o);
1146
+ n && (r.withCredentials = !0), (l = this.config.hlsConfig) != null && l.xhrSetup && this.config.hlsConfig.xhrSetup(r, a);
1147
1147
  }
1148
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);
1149
+ this.hls = new p(s), this.hls.loadSource(i), this.hls.attachMedia(this.videoElement), this.hls.on(p.Events.MANIFEST_PARSED, (r, a) => {
1150
+ const l = this.extractQualities(a.levels);
1151
1151
  this.qualities = l;
1152
- }), this.hls.on(p.Events.LEVEL_SWITCHED, (a, o) => {
1152
+ }), this.hls.on(p.Events.LEVEL_SWITCHED, (r, a) => {
1153
1153
  var u;
1154
- const l = (u = this.hls) == null ? void 0 : u.levels[o.level];
1154
+ const l = (u = this.hls) == null ? void 0 : u.levels[a.level];
1155
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);
1156
+ }), this.hls.on(p.Events.ERROR, (r, a) => {
1157
+ a.fatal && this.handleHlsError(a);
1158
1158
  });
1159
1159
  } else if (this.videoElement.canPlayType("application/vnd.apple.mpegurl"))
1160
1160
  this.videoElement.src = i;
@@ -1307,128 +1307,296 @@ class A {
1307
1307
  (i = this.eventListeners.get(t)) == null || i.delete(e);
1308
1308
  }
1309
1309
  emit(t, e) {
1310
- var s;
1310
+ var n;
1311
1311
  const i = {
1312
1312
  type: t,
1313
1313
  data: e,
1314
1314
  timestamp: Date.now()
1315
1315
  };
1316
- (s = this.eventListeners.get(t)) == null || s.forEach((n) => {
1317
- n(i);
1316
+ (n = this.eventListeners.get(t)) == null || n.forEach((s) => {
1317
+ s(i);
1318
1318
  });
1319
1319
  }
1320
1320
  destroy() {
1321
1321
  this.hls && (this.hls.destroy(), this.hls = null), this.uiController.destroy(), this.videoElement.remove(), this.eventListeners.clear(), this.analytics.destroy();
1322
1322
  }
1323
1323
  }
1324
+ class G {
1325
+ constructor(t) {
1326
+ o(this, "file");
1327
+ o(this, "videoElement", null);
1328
+ o(this, "info", null);
1329
+ if (!this.isVideoFile(t))
1330
+ throw new Error(`Invalid file type: ${t.type}. Expected a video file.`);
1331
+ this.file = t;
1332
+ }
1333
+ /**
1334
+ * Check if the file is a valid video file
1335
+ */
1336
+ isVideoFile(t) {
1337
+ if (t.type.startsWith("video/"))
1338
+ return !0;
1339
+ const e = [".mp4", ".webm", ".ogg", ".mov", ".avi", ".mkv", ".flv", ".wmv", ".m4v", ".3gp", ".ts", ".m3u8"], i = t.name.toLowerCase();
1340
+ return e.some((n) => i.endsWith(n));
1341
+ }
1342
+ /**
1343
+ * Extract video metadata
1344
+ */
1345
+ async extract() {
1346
+ return new Promise((t, e) => {
1347
+ try {
1348
+ this.videoElement = document.createElement("video"), this.videoElement.preload = "metadata", this.videoElement.muted = !0;
1349
+ const i = URL.createObjectURL(this.file);
1350
+ this.videoElement.onloadedmetadata = () => {
1351
+ try {
1352
+ if (!this.videoElement) {
1353
+ e(new Error("Video element not initialized"));
1354
+ return;
1355
+ }
1356
+ const n = this.videoElement.videoWidth, s = this.videoElement.videoHeight, r = this.videoElement.duration, a = this.calculateAspectRatio(n, s), l = this.file.size, u = this.formatBytes(l), m = this.formatDuration(r), g = this.getFileExtension(this.file.name), v = r > 0 ? Math.round(l * 8 / r / 1e3) : void 0;
1357
+ this.info = {
1358
+ width: n,
1359
+ height: s,
1360
+ aspectRatio: a,
1361
+ size: l,
1362
+ sizeInBytes: l,
1363
+ // raw value alias
1364
+ sizeFormatted: u,
1365
+ duration: r,
1366
+ durationInSeconds: r,
1367
+ // raw value alias
1368
+ durationFormatted: m,
1369
+ mimeType: this.file.type || "video/unknown",
1370
+ fileName: this.file.name,
1371
+ fileExtension: g,
1372
+ bitrate: v
1373
+ }, URL.revokeObjectURL(i), this.videoElement.remove(), t(this.info);
1374
+ } catch (n) {
1375
+ URL.revokeObjectURL(i), e(n);
1376
+ }
1377
+ }, this.videoElement.onerror = () => {
1378
+ URL.revokeObjectURL(i), e(new Error(`Failed to load video file: ${this.file.name}`));
1379
+ }, this.videoElement.src = i;
1380
+ } catch (i) {
1381
+ e(i);
1382
+ }
1383
+ });
1384
+ }
1385
+ /**
1386
+ * Calculate aspect ratio (e.g., "16:9", "4:3")
1387
+ */
1388
+ calculateAspectRatio(t, e) {
1389
+ const i = this.getGCD(t, e), n = t / i, s = e / i, r = n / s;
1390
+ return Math.abs(r - 16 / 9) < 0.01 ? "16:9" : Math.abs(r - 4 / 3) < 0.01 ? "4:3" : Math.abs(r - 21 / 9) < 0.01 ? "21:9" : Math.abs(r - 1) < 0.01 ? "1:1" : `${n}:${s}`;
1391
+ }
1392
+ /**
1393
+ * Get Greatest Common Divisor
1394
+ */
1395
+ getGCD(t, e) {
1396
+ return e === 0 ? t : this.getGCD(e, t % e);
1397
+ }
1398
+ /**
1399
+ * Format bytes to human-readable size
1400
+ */
1401
+ formatBytes(t) {
1402
+ if (t === 0) return "0 Bytes";
1403
+ const e = 1024, i = ["Bytes", "KB", "MB", "GB", "TB"], n = Math.floor(Math.log(t) / Math.log(e));
1404
+ return `${parseFloat((t / Math.pow(e, n)).toFixed(2))} ${i[n]}`;
1405
+ }
1406
+ /**
1407
+ * Format duration to HH:MM:SS
1408
+ */
1409
+ formatDuration(t) {
1410
+ if (!isFinite(t) || t < 0) return "00:00";
1411
+ const e = Math.floor(t / 3600), i = Math.floor(t % 3600 / 60), n = Math.floor(t % 60);
1412
+ return e > 0 ? `${e.toString().padStart(2, "0")}:${i.toString().padStart(2, "0")}:${n.toString().padStart(2, "0")}` : `${i.toString().padStart(2, "0")}:${n.toString().padStart(2, "0")}`;
1413
+ }
1414
+ /**
1415
+ * Get file extension
1416
+ */
1417
+ getFileExtension(t) {
1418
+ const e = t.split(".");
1419
+ return e.length > 1 ? `.${e[e.length - 1].toLowerCase()}` : "";
1420
+ }
1421
+ // Getter properties for convenience
1422
+ get width() {
1423
+ var t;
1424
+ return ((t = this.info) == null ? void 0 : t.width) || 0;
1425
+ }
1426
+ get height() {
1427
+ var t;
1428
+ return ((t = this.info) == null ? void 0 : t.height) || 0;
1429
+ }
1430
+ get aspectRatio() {
1431
+ var t;
1432
+ return ((t = this.info) == null ? void 0 : t.aspectRatio) || "unknown";
1433
+ }
1434
+ get size() {
1435
+ var t;
1436
+ return ((t = this.info) == null ? void 0 : t.size) || 0;
1437
+ }
1438
+ get sizeInBytes() {
1439
+ var t;
1440
+ return ((t = this.info) == null ? void 0 : t.sizeInBytes) || 0;
1441
+ }
1442
+ get sizeFormatted() {
1443
+ var t;
1444
+ return ((t = this.info) == null ? void 0 : t.sizeFormatted) || "0 Bytes";
1445
+ }
1446
+ get duration() {
1447
+ var t;
1448
+ return ((t = this.info) == null ? void 0 : t.duration) || 0;
1449
+ }
1450
+ get durationInSeconds() {
1451
+ var t;
1452
+ return ((t = this.info) == null ? void 0 : t.durationInSeconds) || 0;
1453
+ }
1454
+ get durationFormatted() {
1455
+ var t;
1456
+ return ((t = this.info) == null ? void 0 : t.durationFormatted) || "00:00";
1457
+ }
1458
+ get mimeType() {
1459
+ var t;
1460
+ return ((t = this.info) == null ? void 0 : t.mimeType) || this.file.type || "video/unknown";
1461
+ }
1462
+ get fileName() {
1463
+ return this.file.name;
1464
+ }
1465
+ get fileExtension() {
1466
+ var t;
1467
+ return ((t = this.info) == null ? void 0 : t.fileExtension) || "";
1468
+ }
1469
+ get bitrate() {
1470
+ var t;
1471
+ return (t = this.info) == null ? void 0 : t.bitrate;
1472
+ }
1473
+ get quality() {
1474
+ if (!this.info) return "unknown";
1475
+ const t = this.info.height;
1476
+ return t >= 2160 ? "4K (2160p)" : t >= 1440 ? "2K (1440p)" : t >= 1080 ? "Full HD (1080p)" : t >= 720 ? "HD (720p)" : t >= 480 ? "SD (480p)" : t >= 360 ? "360p" : "Low Quality";
1477
+ }
1478
+ /**
1479
+ * Get all information as object
1480
+ */
1481
+ getInfo() {
1482
+ return this.info;
1483
+ }
1484
+ /**
1485
+ * Clean up resources
1486
+ */
1487
+ destroy() {
1488
+ this.videoElement && (this.videoElement.remove(), this.videoElement = null), this.info = null;
1489
+ }
1490
+ }
1324
1491
  const K = (c) => {
1325
1492
  const {
1326
1493
  src: t,
1327
1494
  autoplay: e,
1328
1495
  muted: i,
1329
- controls: s = !0,
1330
- poster: n,
1331
- preload: a,
1332
- theme: o,
1496
+ controls: n = !0,
1497
+ poster: s,
1498
+ preload: r,
1499
+ theme: a,
1333
1500
  s3Config: l,
1334
1501
  analytics: u,
1335
1502
  hlsConfig: m,
1336
- subtitles: $,
1337
- stickyControls: z,
1338
- onReady: k,
1339
- onPlay: E,
1340
- onPause: x,
1341
- onEnded: S,
1342
- onTimeUpdate: C,
1343
- onVolumeChange: L,
1344
- onError: T,
1503
+ subtitles: g,
1504
+ stickyControls: v,
1505
+ onReady: x,
1506
+ onPlay: S,
1507
+ onPause: C,
1508
+ onEnded: L,
1509
+ onTimeUpdate: T,
1510
+ onVolumeChange: I,
1511
+ onError: M,
1345
1512
  onLoadedMetadata: q,
1346
1513
  onQualityChange: P,
1347
1514
  style: U,
1348
- className: H,
1349
- width: g = "100%",
1350
- height: v = "500px"
1351
- } = c, w = y(null), I = y(null);
1352
- return f(() => {
1353
- if (!w.current) return;
1354
- const D = {
1515
+ className: D,
1516
+ width: f = "100%",
1517
+ height: w = "500px"
1518
+ } = c, y = b(null), B = b(null);
1519
+ return E(() => {
1520
+ if (!y.current) return;
1521
+ const H = {
1355
1522
  src: t,
1356
- container: w.current,
1523
+ container: y.current,
1357
1524
  autoplay: e,
1358
1525
  muted: i,
1359
- controls: s,
1360
- poster: n,
1361
- preload: a,
1362
- theme: o,
1526
+ controls: n,
1527
+ poster: s,
1528
+ preload: r,
1529
+ theme: a,
1363
1530
  s3Config: l,
1364
1531
  analytics: u,
1365
1532
  hlsConfig: m,
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;
1533
+ subtitles: g,
1534
+ stickyControls: v
1535
+ }, d = new A(H);
1536
+ return B.current = d, S && d.on("play", S), C && d.on("pause", C), L && d.on("ended", L), M && d.on("error", (h) => {
1537
+ var R;
1538
+ return M((R = h.data) == null ? void 0 : R.error);
1539
+ }), q && d.on("loadedmetadata", q), P && d.on("qualitychange", (h) => P(h.data.level)), T && d.on("timeupdate", (h) => T(h.data.currentTime)), I && d.on("volumechange", (h) => I(h.data.volume, h.data.muted)), x && x(d), () => {
1540
+ d.destroy(), B.current = null;
1374
1541
  };
1375
- }, [t]), /* @__PURE__ */ B(
1542
+ }, [t]), /* @__PURE__ */ F(
1376
1543
  "div",
1377
1544
  {
1378
- ref: w,
1379
- className: H,
1545
+ ref: y,
1546
+ className: D,
1380
1547
  style: {
1381
- width: typeof g == "number" ? `${g}px` : g,
1382
- height: typeof v == "number" ? `${v}px` : v,
1548
+ width: typeof f == "number" ? `${f}px` : f,
1549
+ height: typeof w == "number" ? `${w}px` : w,
1383
1550
  ...U
1384
1551
  }
1385
1552
  }
1386
1553
  );
1387
- }, G = (c) => {
1388
- const [t, e] = b(null), [i, s] = b(null), n = y(null);
1389
- return f(() => {
1390
- if (!n.current) return;
1391
- const a = new A({
1554
+ }, J = (c) => {
1555
+ const [t, e] = k(null), [i, n] = k(null), s = b(null);
1556
+ return E(() => {
1557
+ if (!s.current) return;
1558
+ const r = new A({
1392
1559
  ...c,
1393
- container: n.current
1560
+ container: s.current
1394
1561
  });
1395
- e(a);
1396
- const o = () => {
1397
- s(a.getState());
1562
+ e(r);
1563
+ const a = () => {
1564
+ n(r.getState());
1398
1565
  };
1399
- return a.on("play", o), a.on("pause", o), a.on("timeupdate", o), a.on("volumechange", o), a.on("loadedmetadata", o), () => {
1400
- a.destroy();
1566
+ return r.on("play", a), r.on("pause", a), r.on("timeupdate", a), r.on("volumechange", a), r.on("loadedmetadata", a), () => {
1567
+ r.destroy();
1401
1568
  };
1402
1569
  }, [c.src]), {
1403
- containerRef: n,
1570
+ containerRef: s,
1404
1571
  player: t,
1405
1572
  state: i
1406
1573
  };
1407
- }, F = R.createContext({
1574
+ }, z = $.createContext({
1408
1575
  player: null,
1409
1576
  state: null
1410
- }), J = (c) => {
1411
- const { player: t, children: e } = c, [i, s] = b(t.getState());
1412
- return f(() => {
1413
- const n = () => {
1414
- s(t.getState());
1577
+ }), Z = (c) => {
1578
+ const { player: t, children: e } = c, [i, n] = k(t.getState());
1579
+ return E(() => {
1580
+ const s = () => {
1581
+ n(t.getState());
1415
1582
  };
1416
- return t.on("play", n), t.on("pause", n), t.on("timeupdate", n), t.on("volumechange", n), t.on("loadedmetadata", n), () => {
1583
+ return t.on("play", s), t.on("pause", s), t.on("timeupdate", s), t.on("volumechange", s), t.on("loadedmetadata", s), () => {
1417
1584
  };
1418
- }, [t]), /* @__PURE__ */ B(F.Provider, { value: { player: t, state: i }, children: e });
1419
- }, Z = () => {
1420
- const c = R.useContext(F);
1585
+ }, [t]), /* @__PURE__ */ F(z.Provider, { value: { player: t, state: i }, children: e });
1586
+ }, tt = () => {
1587
+ const c = $.useContext(z);
1421
1588
  if (!c.player)
1422
1589
  throw new Error("useWontumPlayerContext must be used within WontumPlayerProvider");
1423
1590
  return c;
1424
1591
  };
1425
1592
  export {
1426
- _ as Analytics,
1427
- j as S3Handler,
1428
- Q as UIController,
1593
+ W as Analytics,
1594
+ Q as S3Handler,
1595
+ j as UIController,
1596
+ G as WontumFileInfo,
1429
1597
  A as WontumPlayer,
1430
- J as WontumPlayerProvider,
1598
+ Z as WontumPlayerProvider,
1431
1599
  K as WontumPlayerReact,
1432
- G as useWontumPlayer,
1433
- Z as useWontumPlayerContext
1600
+ J as useWontumPlayer,
1601
+ tt as useWontumPlayerContext
1434
1602
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obipascal/player",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "A modern HLS video player SDK for educational platforms with S3 integration",
5
5
  "main": "dist/wontum-player.cjs.js",
6
6
  "module": "dist/wontum-player.esm.js",