@obipascal/player 1.0.8 → 1.0.9

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
@@ -34,6 +34,7 @@ A modern, feature-rich HLS video player SDK for educational platforms with Cloud
34
34
  - ⚛️ **React Support**: Component, Hook, and Context Provider patterns
35
35
  - 🔧 **TypeScript**: Full TypeScript support with comprehensive type definitions
36
36
  - 📊 **Analytics & QoE**: Built-in analytics tracking and Quality of Experience metrics
37
+ - 🔌 **Real-time Analytics**: Native WebSocket and Socket.IO support for live analytics streaming
37
38
  - 🎯 **25 Events**: Complete event system compatible with Mux Player
38
39
  - 📱 **Responsive**: Mobile-friendly with touch support
39
40
  - 🎬 **Quality Selector**: Automatic quality switching with manual override
@@ -50,6 +51,12 @@ Or with yarn:
50
51
  yarn add @obipascal/player hls.js
51
52
  ```
52
53
 
54
+ **Optional:** For Socket.IO real-time analytics support:
55
+
56
+ ```bash
57
+ npm install socket.io-client
58
+ ```
59
+
53
60
  ## 🚀 Quick Start
54
61
 
55
62
  ### Vanilla JavaScript
@@ -691,7 +698,19 @@ Modern vertical volume slider with popup interface - hover over volume button to
691
698
 
692
699
  ## 📊 Analytics
693
700
 
694
- Track video engagement and quality metrics:
701
+ Track video engagement and quality metrics with HTTP endpoints or real-time WebSocket/Socket.IO streaming:
702
+
703
+ **Features:**
704
+
705
+ - ✅ HTTP endpoint support for traditional analytics
706
+ - ✅ Native WebSocket support for real-time streaming
707
+ - ✅ Socket.IO support with full TypeScript types
708
+ - ✅ Dual streaming (HTTP + Socket simultaneously)
709
+ - ✅ Event transformation and filtering
710
+ - ✅ Auto-reconnection with configurable delays
711
+ - ✅ Quality of Experience (QoE) metrics included in every event
712
+
713
+ ### HTTP Analytics
695
714
 
696
715
  ```typescript
697
716
  const player = new WontumPlayer({
@@ -720,6 +739,206 @@ console.log(metrics)
720
739
  // }
721
740
  ```
722
741
 
742
+ ### WebSocket Real-Time Analytics
743
+
744
+ Stream analytics events in real-time using native WebSocket for live dashboards and monitoring:
745
+
746
+ ```typescript
747
+ const player = new WontumPlayer({
748
+ src: "https://example.com/video.m3u8",
749
+ container: "#player",
750
+ analytics: {
751
+ enabled: true,
752
+ userId: "user_456",
753
+ videoId: "video_789",
754
+ // Native WebSocket configuration
755
+ webSocket: {
756
+ type: "websocket", // Specify native WebSocket
757
+ connection: "wss://analytics.example.com/stream",
758
+ // Optional: Transform events before sending
759
+ transform: (event) => ({
760
+ type: event.eventType,
761
+ video_id: event.videoId,
762
+ user_id: event.userId,
763
+ timestamp: event.timestamp,
764
+ metrics: event.data,
765
+ }),
766
+ // Optional: Handle errors
767
+ onError: (error) => {
768
+ console.error("Analytics WebSocket error:", error)
769
+ },
770
+ // Optional: Connection opened
771
+ onOpen: (event) => {
772
+ console.log("Analytics WebSocket connected")
773
+ },
774
+ // Optional: Connection closed
775
+ onClose: (event) => {
776
+ console.log("Analytics WebSocket disconnected")
777
+ },
778
+ // Auto-reconnect on disconnect (default: true)
779
+ autoReconnect: true,
780
+ // Reconnect delay in milliseconds (default: 3000)
781
+ reconnectDelay: 3000,
782
+ },
783
+ },
784
+ })
785
+ ```
786
+
787
+ ### Socket.IO Real-Time Analytics
788
+
789
+ For Socket.IO-based real-time analytics (requires `socket.io-client` to be loaded):
790
+
791
+ ```typescript
792
+ // Option 1: Let the SDK create the Socket.IO connection
793
+ const player = new WontumPlayer({
794
+ src: "https://example.com/video.m3u8",
795
+ container: "#player",
796
+ analytics: {
797
+ enabled: true,
798
+ userId: "user_456",
799
+ videoId: "video_789",
800
+ webSocket: {
801
+ type: "socket.io",
802
+ connection: "https://analytics.example.com", // Socket.IO server URL
803
+ options: {
804
+ path: "/socket.io/",
805
+ transports: ["websocket", "polling"],
806
+ auth: {
807
+ token: "your-auth-token",
808
+ },
809
+ reconnection: true,
810
+ reconnectionDelay: 1000,
811
+ },
812
+ eventName: "video_analytics", // Event name to emit (default: "analytics")
813
+ transform: (event) => ({
814
+ event: event.eventType,
815
+ video: event.videoId,
816
+ user: event.userId,
817
+ data: event.data,
818
+ }),
819
+ onConnect: () => {
820
+ console.log("Socket.IO connected")
821
+ },
822
+ onDisconnect: (reason) => {
823
+ console.log("Socket.IO disconnected:", reason)
824
+ },
825
+ onError: (error) => {
826
+ console.error("Socket.IO error:", error)
827
+ },
828
+ },
829
+ },
830
+ })
831
+ ```
832
+
833
+ ```typescript
834
+ // Option 2: Use existing Socket.IO connection
835
+ import { io } from "socket.io-client"
836
+
837
+ const socket = io("https://analytics.example.com", {
838
+ auth: {
839
+ token: "your-auth-token",
840
+ },
841
+ })
842
+
843
+ const player = new WontumPlayer({
844
+ src: "https://example.com/video.m3u8",
845
+ container: "#player",
846
+ analytics: {
847
+ enabled: true,
848
+ userId: "user_456",
849
+ videoId: "video_789",
850
+ webSocket: {
851
+ type: "socket.io",
852
+ connection: socket, // Use existing Socket.IO instance
853
+ eventName: "analytics",
854
+ },
855
+ },
856
+ })
857
+ ```
858
+
859
+ ### Using Existing WebSocket Connection
860
+
861
+ ```typescript
862
+ // Create your own WebSocket connection
863
+ const ws = new WebSocket("wss://analytics.example.com/stream")
864
+
865
+ // Configure authentication or custom headers before connecting
866
+ ws.addEventListener("open", () => {
867
+ // Send authentication message
868
+ ws.send(
869
+ JSON.stringify({
870
+ type: "auth",
871
+ token: "your-auth-token",
872
+ }),
873
+ )
874
+ })
875
+
876
+ const player = new WontumPlayer({
877
+ src: "https://example.com/video.m3u8",
878
+ container: "#player",
879
+ analytics: {
880
+ enabled: true,
881
+ userId: "user_456",
882
+ videoId: "video_789",
883
+ webSocket: {
884
+ type: "websocket",
885
+ connection: ws, // Use existing connection
886
+ transform: (event) => ({
887
+ // Custom format for your backend
888
+ action: "video_event",
889
+ payload: {
890
+ event: event.eventType,
891
+ data: event.data,
892
+ },
893
+ }),
894
+ },
895
+ },
896
+ })
897
+ ```
898
+
899
+ ### Dual Analytics (HTTP + WebSocket/Socket.IO)
900
+
901
+ Send analytics to both HTTP endpoint and real-time socket simultaneously:
902
+
903
+ ```typescript
904
+ const player = new WontumPlayer({
905
+ src: "https://example.com/video.m3u8",
906
+ container: "#player",
907
+ analytics: {
908
+ enabled: true,
909
+ endpoint: "https://api.example.com/analytics", // HTTP fallback/storage
910
+ webSocket: {
911
+ type: "socket.io",
912
+ connection: "https://realtime.example.com", // Real-time monitoring
913
+ eventName: "video_analytics",
914
+ },
915
+ userId: "user_456",
916
+ videoId: "video_789",
917
+ },
918
+ })
919
+ ```
920
+
921
+ ### Analytics Events Tracked
922
+
923
+ The SDK automatically tracks these events:
924
+
925
+ - **Session**: `session_start`, `session_end`
926
+ - **Playback**: `play`, `pause`, `ended`, `playing`
927
+ - **Buffering**: `buffering_start`, `buffering_end`, `waiting`, `stalled`
928
+ - **Seeking**: `seeking`, `seeked`
929
+ - **Quality**: `qualitychange`, `renditionchange`
930
+ - **Errors**: `error`
931
+ - **User Actions**: Volume changes, fullscreen, playback rate changes
932
+
933
+ Each event includes Quality of Experience (QoE) metrics:
934
+
935
+ - `sessionDuration` - Total session time
936
+ - `totalPlayTime` - Actual video play time
937
+ - `totalBufferTime` - Time spent buffering
938
+ - `bufferingRatio` - Buffer time / play time ratio
939
+ - `rebufferCount` - Number of rebuffer events
940
+ - `seekCount` - Number of seek operations
941
+
723
942
  ## API Reference
724
943
 
725
944
  ### WontumPlayer
@@ -750,6 +969,39 @@ interface S3Config {
750
969
  region?: string // S3 region
751
970
  endpoint?: string // Custom S3 endpoint
752
971
  }
972
+
973
+ interface AnalyticsConfig {
974
+ enabled?: boolean // Enable analytics tracking
975
+ endpoint?: string // HTTP endpoint for analytics events
976
+ webSocket?: WebSocketAnalyticsHandler | SocketIOAnalyticsHandler // Real-time streaming
977
+ sessionId?: string // Session identifier
978
+ userId?: string // User identifier
979
+ videoId?: string // Video identifier
980
+ }
981
+
982
+ // Native WebSocket Configuration
983
+ interface WebSocketAnalyticsHandler {
984
+ type: "websocket"
985
+ connection: WebSocket | string // WebSocket instance or URL
986
+ transform?: (event: AnalyticsEvent) => any // Transform before sending
987
+ onError?: (error: Event) => void
988
+ onOpen?: (event: Event) => void
989
+ onClose?: (event: CloseEvent) => void
990
+ autoReconnect?: boolean // Default: true
991
+ reconnectDelay?: number // Default: 3000ms
992
+ }
993
+
994
+ // Socket.IO Configuration
995
+ interface SocketIOAnalyticsHandler {
996
+ type: "socket.io"
997
+ connection: Socket | string // Socket.IO instance or URL
998
+ options?: Partial<ManagerOptions & SocketOptions> // Socket.IO options
999
+ eventName?: string // Event name to emit (default: "analytics")
1000
+ transform?: (event: AnalyticsEvent) => any
1001
+ onError?: (error: Error) => void
1002
+ onConnect?: () => void
1003
+ onDisconnect?: (reason: string) => void
1004
+ }
753
1005
  ```
754
1006
 
755
1007
  #### Methods
@@ -14,6 +14,10 @@ export declare class Analytics {
14
14
  private bufferStartTime;
15
15
  private rebufferCount;
16
16
  private seekCount;
17
+ private webSocket;
18
+ private socketIO;
19
+ private wsReconnectTimeout;
20
+ private isDestroyed;
17
21
  constructor(config?: AnalyticsConfig);
18
22
  trackEvent(eventType: string, data?: Record<string, any>): void;
19
23
  private updateMetrics;
@@ -22,6 +26,10 @@ export declare class Analytics {
22
26
  private getConnectionInfo;
23
27
  private sendEvent;
24
28
  private generateSessionId;
29
+ private initializeSocketIO;
30
+ private sendToSocketIO;
31
+ private initializeWebSocket;
32
+ private sendToWebSocket;
25
33
  getEvents(): AnalyticsEvent[];
26
34
  getMetrics(): Record<string, any>;
27
35
  destroy(): void;
@@ -4,6 +4,6 @@ export { S3Handler } from './s3-handler';
4
4
  export { UIController } from './ui-controller';
5
5
  export { WontumFileInfo } from './file-info';
6
6
  export { WontumPlayerReact, useWontumPlayer, WontumPlayerProvider, useWontumPlayerContext } from './react';
7
- export type { WontumPlayerConfig, PlayerTheme, S3Config, AnalyticsConfig, PlayerState, PlayerEvent, PlayerEventType, AnalyticsEvent, QualityLevel } from './types';
7
+ export type { WontumPlayerConfig, PlayerTheme, S3Config, AnalyticsConfig, WebSocketAnalyticsHandler, SocketIOAnalyticsHandler, PlayerState, PlayerEvent, PlayerEventType, AnalyticsEvent, QualityLevel, } from './types';
8
8
  export type { VideoFileInfo } from './file-info';
9
9
  export type { WontumPlayerReactProps } from './react';
@@ -1,3 +1,5 @@
1
+ import { Socket } from 'socket.io-client';
2
+
1
3
  /**
2
4
  * Player configuration options
3
5
  */
@@ -55,8 +57,10 @@ export interface S3Config {
55
57
  export interface AnalyticsConfig {
56
58
  /** Enable analytics */
57
59
  enabled?: boolean;
58
- /** Custom analytics endpoint */
60
+ /** Custom analytics endpoint (HTTP/HTTPS) */
59
61
  endpoint?: string;
62
+ /** WebSocket handler for real-time analytics streaming (supports both native WebSocket and Socket.IO) */
63
+ webSocket?: WebSocketAnalyticsHandler | SocketIOAnalyticsHandler;
60
64
  /** Session identifier */
61
65
  sessionId?: string;
62
66
  /** User identifier */
@@ -64,6 +68,48 @@ export interface AnalyticsConfig {
64
68
  /** Video identifier */
65
69
  videoId?: string;
66
70
  }
71
+ /**
72
+ * Native WebSocket handler for real-time analytics
73
+ */
74
+ export interface WebSocketAnalyticsHandler {
75
+ /** Type identifier for native WebSocket */
76
+ type: "websocket";
77
+ /** WebSocket connection instance or URL to connect to */
78
+ connection: WebSocket | string;
79
+ /** Optional: Transform event before sending (allows filtering, formatting, etc.) */
80
+ transform?: (event: AnalyticsEvent) => any;
81
+ /** Optional: Error handler for WebSocket errors */
82
+ onError?: (error: Event) => void;
83
+ /** Optional: Handler for when WebSocket connection opens */
84
+ onOpen?: (event: Event) => void;
85
+ /** Optional: Handler for when WebSocket connection closes */
86
+ onClose?: (event: CloseEvent) => void;
87
+ /** Optional: Reconnect automatically on disconnect (default: true) */
88
+ autoReconnect?: boolean;
89
+ /** Optional: Reconnect delay in milliseconds (default: 3000) */
90
+ reconnectDelay?: number;
91
+ }
92
+ /**
93
+ * Socket.IO handler for real-time analytics
94
+ */
95
+ export interface SocketIOAnalyticsHandler {
96
+ /** Type identifier for Socket.IO */
97
+ type: "socket.io";
98
+ /** Socket.IO client instance or URL to connect to */
99
+ connection: typeof Socket | string;
100
+ /** Optional: Socket.IO connection options (used when connection is a URL) */
101
+ options?: Record<string, any>;
102
+ /** Optional: Event name to emit (default: "analytics") */
103
+ eventName?: string;
104
+ /** Optional: Transform event before sending (allows filtering, formatting, etc.) */
105
+ transform?: (event: AnalyticsEvent) => any;
106
+ /** Optional: Error handler */
107
+ onError?: (error: Error) => void;
108
+ /** Optional: Handler for when connection is established */
109
+ onConnect?: () => void;
110
+ /** Optional: Handler for when connection is lost */
111
+ onDisconnect?: (reason: string) => void;
112
+ }
67
113
  /**
68
114
  * Player state
69
115
  */
@@ -1,14 +1,14 @@
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=`
1
+ "use strict";var V=Object.create;var k=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,Q=Object.prototype.hasOwnProperty;var Y=(l,t,e)=>t in l?k(l,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):l[t]=e;var X=(l,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of _(t))!Q.call(l,n)&&n!==e&&k(l,n,{get:()=>t[n],enumerable:!(i=N(t,n))||i.enumerable});return l};var G=(l,t,e)=>(e=l!=null?V(j(l)):{},X(t||!l||!l.__esModule?k(e,"default",{value:l,enumerable:!0}):e,l));var s=(l,t,e)=>Y(l,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 K(l){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(l){for(const e in l)if(e!=="default"){const i=Object.getOwnPropertyDescriptor(l,e);Object.defineProperty(t,e,i.get?i:{enumerable:!0,get:()=>l[e]})}}return t.default=l,Object.freeze(t)}const F=K(h);class O{constructor(t){s(this,"config");s(this,"sessionId");s(this,"events",[]);s(this,"sessionStartTime");s(this,"playbackStartTime",null);s(this,"totalPlayTime",0);s(this,"totalBufferTime",0);s(this,"bufferStartTime",null);s(this,"rebufferCount",0);s(this,"seekCount",0);s(this,"webSocket",null);s(this,"socketIO",null);s(this,"wsReconnectTimeout",null);s(this,"isDestroyed",!1);var e,i;if(this.config=t,this.sessionId=(t==null?void 0:t.sessionId)||this.generateSessionId(),this.sessionStartTime=Date.now(),(e=this.config)!=null&&e.webSocket){const n=this.config.webSocket;"type"in n?n.type==="socket.io"?this.initializeSocketIO():this.initializeWebSocket():this.initializeWebSocket()}(i=this.config)!=null&&i.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.webSocket&&this.webSocket.readyState===WebSocket.OPEN&&this.sendToWebSocket(i),this.socketIO&&this.socketIO.connected&&this.sendToSocketIO(i),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)}`}async initializeSocketIO(){var e;if(!((e=this.config)!=null&&e.webSocket)||!("type"in this.config.webSocket))return;const t=this.config.webSocket;if(t.type==="socket.io")try{if(typeof t.connection=="string"){const n=(await import("socket.io-client")).default;this.socketIO=n(t.connection,t.options||{})}else this.socketIO=t.connection;if(!this.socketIO)return;this.socketIO.on("connect",()=>{process.env.NODE_ENV==="development"&&console.log("[Analytics Socket.IO] Connected"),t.onConnect&&t.onConnect()}),this.socketIO.on("connect_error",i=>{console.error("[Analytics Socket.IO] Connection error:",i),t.onError&&t.onError(i)}),this.socketIO.on("disconnect",i=>{process.env.NODE_ENV==="development"&&console.log("[Analytics Socket.IO] Disconnected:",i),t.onDisconnect&&t.onDisconnect(i)}),this.socketIO.on("error",i=>{console.error("[Analytics Socket.IO] Error:",i),t.onError&&t.onError(i)})}catch(i){console.error("[Analytics Socket.IO] Failed to initialize:",i)}}sendToSocketIO(t){var e;if(!(!this.socketIO||!this.socketIO.connected))try{const i=(e=this.config)==null?void 0:e.webSocket,n=i!=null&&i.transform?i.transform(t):t,o=(i==null?void 0:i.eventName)||"analytics";this.socketIO.emit(o,n),process.env.NODE_ENV==="development"&&console.log(`[Analytics Socket.IO] Emitted (${o}):`,t.eventType)}catch(i){console.error("[Analytics Socket.IO] Failed to emit event:",i)}}initializeWebSocket(){var e;if(!((e=this.config)!=null&&e.webSocket))return;const t=this.config.webSocket;try{typeof t.connection=="string"?this.webSocket=new WebSocket(t.connection):this.webSocket=t.connection,this.webSocket.onopen=i=>{process.env.NODE_ENV==="development"&&console.log("[Analytics WebSocket] Connected"),t.onOpen&&t.onOpen(i)},this.webSocket.onerror=i=>{console.error("[Analytics WebSocket] Error:",i),t.onError&&t.onError(i)},this.webSocket.onclose=i=>{if(process.env.NODE_ENV==="development"&&console.log("[Analytics WebSocket] Disconnected"),t.onClose&&t.onClose(i),t.autoReconnect!==!1&&!this.isDestroyed){const o=t.reconnectDelay||3e3;process.env.NODE_ENV==="development"&&console.log(`[Analytics WebSocket] Reconnecting in ${o}ms...`),this.wsReconnectTimeout=window.setTimeout(()=>{this.initializeWebSocket()},o)}}}catch(i){console.error("[Analytics WebSocket] Failed to initialize:",i)}}sendToWebSocket(t){var e;if(!(!this.webSocket||this.webSocket.readyState!==WebSocket.OPEN))try{const i=(e=this.config)==null?void 0:e.webSocket,n=i!=null&&i.transform?i.transform(t):t;this.webSocket.send(JSON.stringify(n)),process.env.NODE_ENV==="development"&&console.log("[Analytics WebSocket] Sent:",t.eventType)}catch(i){console.error("[Analytics WebSocket] Failed to send event:",i)}}getEvents(){return[...this.events]}getMetrics(){return{sessionId:this.sessionId,...this.getQoEMetrics(),eventCount:this.events.length}}destroy(){var t;this.isDestroyed=!0,(t=this.config)!=null&&t.enabled&&this.trackEvent("session_end",this.getSessionData()),this.wsReconnectTimeout&&(clearTimeout(this.wsReconnectTimeout),this.wsReconnectTimeout=null),this.webSocket&&(this.webSocket.close(),this.webSocket=null),this.socketIO&&(this.socketIO.removeAllListeners(),this.socketIO.disconnect(),this.socketIO=null),this.events=[]}}class ${constructor(t,e){s(this,"container");s(this,"player");s(this,"controlsContainer");s(this,"progressContainer");s(this,"progressBar");s(this,"playButton");s(this,"skipBackwardButton");s(this,"skipForwardButton");s(this,"volumeButton");s(this,"volumeContainer");s(this,"fullscreenButton");s(this,"pipButton");s(this,"settingsButton");s(this,"volumeSlider");s(this,"progressInput");s(this,"hideControlsTimeout",null);s(this,"stickyControls",!1);s(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",o=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)",c=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: ${s};
5
+ font-family: ${o};
6
6
  overflow: hidden;
7
7
  --primary-color: ${i};
8
8
  --accent-color: ${n};
9
9
  --controls-bg: ${r};
10
10
  --button-hover: ${a};
11
- --progress-height: ${l};
11
+ --progress-height: ${c};
12
12
  --border-radius: ${u};
13
13
  }
14
14
 
@@ -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",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=`
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,o=parseFloat(n.value),r=this.player.getState(),a=o/100*r.duration;this.player.seek(a)}),this.volumeSlider.addEventListener("input",i=>{const n=i.target,o=parseFloat(n.value)/100;this.player.setVolume(o)}),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 o=n.currentTarget,r=o.getAttribute("data-tab");t.forEach(u=>u.classList.remove("active")),o.classList.add("active"),this.controlsContainer.querySelectorAll(".wontum-tab-panel").forEach(u=>u.classList.remove("active"));const c=this.controlsContainer.querySelector(`[data-panel="${r}"]`);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,i=this.player.getState();if(i.duration>0){const o=e/i.duration*100;this.progressBar.style.width=`${o}%`,this.progressInput.value=o.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
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}`}
567
+ ${e.map((n,o)=>`
568
+ <div class="wontum-subtitle-option ${o===i?"active":""}" data-track="${o}">
569
+ ${n.label||n.language||`Track ${o+1}`}
570
570
  </div>
571
571
  `).join("")}
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"}
572
+ `,t.querySelectorAll(".wontum-subtitle-option").forEach(n=>{n.addEventListener("click",o=>{const r=o.target,a=parseInt(r.dataset.track||"-1");a===-1?this.player.disableSubtitles():this.player.enableSubtitles(a),t.querySelectorAll(".wontum-subtitle-option").forEach(c=>c.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(o=>`
573
+ <div class="wontum-speed-option ${i===o?"active":""}" data-speed="${o}">
574
+ ${o===1?"Normal":o+"x"}
575
575
  </div>
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=`
576
+ `).join(""),t.querySelectorAll(".wontum-speed-option").forEach(o=>{o.addEventListener("click",r=>{const a=r.target,c=parseFloat(a.dataset.speed||"1");this.player.setPlaybackRate(c),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
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
- ${i.map((n,s)=>`
584
- <div class="wontum-quality-option" data-quality="${s}">${n.name}</div>
583
+ ${i.map((n,o)=>`
584
+ <div class="wontum-quality-option" data-quality="${o}">${n.name}</div>
585
585
  `).join("")}
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">
586
+ `,e.querySelectorAll(".wontum-quality-option").forEach(n=>{n.addEventListener("click",o=>{const r=o.target,a=parseInt(r.dataset.quality||"-1");this.player.setQuality(a),e.querySelectorAll(".wontum-quality-option").forEach(c=>c.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 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;
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){s(this,"config");s(this,"urlCache",new Map);s(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 o,r;if(this.signedUrls.has(t))return t;if((o=this.config)!=null&&o.signUrl)try{const a=await this.config.signUrl(t);return this.signedUrls.add(t),a}catch(a){const c=(a==null?void 0:a.name)==="AbortError"||((r=a==null?void 0:a.message)==null?void 0:r.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:",a),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: ${(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 o=await this.config.getPresignedUrl(e);return this.urlCache.set(e,{url:o,expiresAt:Date.now()+50*60*1e3}),o}catch(o){throw console.error("Failed to generate presigned URL:",o),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 E{constructor(t){s(this,"container");s(this,"videoElement");s(this,"hls",null);s(this,"config");s(this,"eventListeners",new Map);s(this,"analytics");s(this,"s3Handler");s(this,"uiController");s(this,"qualities",[]);s(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 O(t.analytics),this.s3Handler=new z(t.s3Config),this.videoElement=this.createVideoElement(),this.container.appendChild(this.videoElement),this.uiController=new $(this.container,this),this.setupVideoListeners(),this.loadSource(t.src),t.autoplay&&(this.videoElement.autoplay=!0),t.muted&&this.mute(),t.poster&&(this.videoElement.poster=t.poster),t.preload&&(this.videoElement.preload=t.preload),t.subtitles&&this.addSubtitleTracks(t.subtitles)}addSubtitleTracks(t){t.forEach(e=>{const 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,o={...this.config.hlsConfig,xhrSetup:(r,a)=>{var c;n&&(r.withCredentials=!0),(c=this.config.hlsConfig)!=null&&c.xhrSetup&&this.config.hlsConfig.xhrSetup(r,a)}};this.hls=new g(o),this.hls.loadSource(i),this.hls.attachMedia(this.videoElement),this.hls.on(g.Events.MANIFEST_PARSED,(r,a)=>{const c=this.extractQualities(a.levels);this.qualities=c}),this.hls.on(g.Events.LEVEL_SWITCHED,(r,a)=>{var u;const c=(u=this.hls)==null?void 0:u.levels[a.level];c&&(this.state.quality=`${c.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(o=>{o(i)})}destroy(){this.hls&&(this.hls.destroy(),this.hls=null),this.uiController.destroy(),this.videoElement.remove(),this.eventListeners.clear(),this.analytics.destroy()}}class J{constructor(t){s(this,"file");s(this,"videoElement",null);s(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,o=this.videoElement.videoHeight,r=this.videoElement.duration,a=this.calculateAspectRatio(n,o),c=this.file.size,u=this.formatBytes(c),p=this.formatDuration(r),v=this.getFileExtension(this.file.name),f=r>0?Math.round(c*8/r/1e3):void 0;this.info={width:n,height:o,aspectRatio:a,size:c,sizeInBytes:c,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,o=e/i,r=n/o;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}:${o}`}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 Z=l=>{const{src:t,autoplay:e,muted:i,controls:n=!0,poster:o,preload:r,theme:a,s3Config:c,analytics:u,hlsConfig:p,subtitles:v,stickyControls:f,onReady:S,onPlay:x,onPause:C,onEnded:L,onTimeUpdate:T,onVolumeChange:I,onError:P,onLoadedMetadata:R,onQualityChange:M,style:U,className:H,width:y="100%",height:w="500px"}=l,b=h.useRef(null),q=h.useRef(null);return h.useEffect(()=>{if(!b.current)return;const W={src:t,container:b.current,autoplay:e,muted:i,controls:n,poster:o,preload:r,theme:a,s3Config:c,analytics:u,hlsConfig:p,subtitles:v,stickyControls:f},d=new E(W);return q.current=d,x&&d.on("play",x),C&&d.on("pause",C),L&&d.on("ended",L),P&&d.on("error",m=>{var A;return P((A=m.data)==null?void 0:A.error)}),R&&d.on("loadedmetadata",R),M&&d.on("qualitychange",m=>M(m.data.level)),T&&d.on("timeupdate",m=>T(m.data.currentTime)),I&&d.on("volumechange",m=>I(m.data.volume,m.data.muted)),S&&S(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,...U}})},tt=l=>{const[t,e]=h.useState(null),[i,n]=h.useState(null),o=h.useRef(null);return h.useEffect(()=>{if(!o.current)return;const r=new E({...l,container:o.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()}},[l.src]),{containerRef:o,player:t,state:i}},D=F.createContext({player:null,state:null}),et=l=>{const{player:t,children:e}=l,[i,n]=h.useState(t.getState());return h.useEffect(()=>{const o=()=>{n(t.getState())};return t.on("play",o),t.on("pause",o),t.on("timeupdate",o),t.on("volumechange",o),t.on("loadedmetadata",o),()=>{}},[t]),B.jsx(D.Provider,{value:{player:t,state:i},children:e})},it=()=>{const l=F.useContext(D);if(!l.player)throw new Error("useWontumPlayerContext must be used within WontumPlayerProvider");return l};exports.Analytics=O;exports.S3Handler=z;exports.UIController=$;exports.WontumFileInfo=J;exports.WontumPlayer=E;exports.WontumPlayerProvider=et;exports.WontumPlayerReact=Z;exports.useWontumPlayer=tt;exports.useWontumPlayerContext=it;
@@ -1,24 +1,32 @@
1
- var V = Object.defineProperty;
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 o = (c, t, e) => N(c, typeof t != "symbol" ? t + "" : t, e);
1
+ var H = Object.defineProperty;
2
+ var V = (c, t, e) => t in c ? H(c, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[t] = e;
3
+ var s = (c, t, e) => V(c, typeof t != "symbol" ? t + "" : t, e);
4
4
  import p from "hls.js";
5
- import { jsx as F } from "react/jsx-runtime";
6
- import * as $ from "react";
5
+ import { jsx as A } from "react/jsx-runtime";
6
+ import * as F from "react";
7
7
  import { useRef as b, useEffect as E, useState as k } from "react";
8
- class W {
8
+ class N {
9
9
  constructor(t) {
10
- o(this, "config");
11
- o(this, "sessionId");
12
- o(this, "events", []);
13
- o(this, "sessionStartTime");
14
- o(this, "playbackStartTime", null);
15
- o(this, "totalPlayTime", 0);
16
- o(this, "totalBufferTime", 0);
17
- o(this, "bufferStartTime", null);
18
- o(this, "rebufferCount", 0);
19
- o(this, "seekCount", 0);
20
- var e;
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());
10
+ s(this, "config");
11
+ s(this, "sessionId");
12
+ s(this, "events", []);
13
+ s(this, "sessionStartTime");
14
+ s(this, "playbackStartTime", null);
15
+ s(this, "totalPlayTime", 0);
16
+ s(this, "totalBufferTime", 0);
17
+ s(this, "bufferStartTime", null);
18
+ s(this, "rebufferCount", 0);
19
+ s(this, "seekCount", 0);
20
+ s(this, "webSocket", null);
21
+ s(this, "socketIO", null);
22
+ s(this, "wsReconnectTimeout", null);
23
+ s(this, "isDestroyed", !1);
24
+ var e, i;
25
+ if (this.config = t, this.sessionId = (t == null ? void 0 : t.sessionId) || this.generateSessionId(), this.sessionStartTime = Date.now(), (e = this.config) != null && e.webSocket) {
26
+ const n = this.config.webSocket;
27
+ "type" in n ? n.type === "socket.io" ? this.initializeSocketIO() : this.initializeWebSocket() : this.initializeWebSocket();
28
+ }
29
+ (i = this.config) != null && i.enabled && this.trackEvent("session_start", this.getSessionData());
22
30
  }
23
31
  trackEvent(t, e = {}) {
24
32
  var n;
@@ -34,7 +42,7 @@ class W {
34
42
  ...this.getQoEMetrics()
35
43
  }
36
44
  };
37
- this.events.push(i), this.updateMetrics(t, e), this.config.endpoint && this.sendEvent(i), process.env.NODE_ENV === "development" && console.log("[Analytics]", t, i.data);
45
+ this.events.push(i), this.updateMetrics(t, e), this.webSocket && this.webSocket.readyState === WebSocket.OPEN && this.sendToWebSocket(i), this.socketIO && this.socketIO.connected && this.sendToSocketIO(i), this.config.endpoint && this.sendEvent(i), process.env.NODE_ENV === "development" && console.log("[Analytics]", t, i.data);
38
46
  }
39
47
  updateMetrics(t, e) {
40
48
  switch (t) {
@@ -104,6 +112,72 @@ class W {
104
112
  generateSessionId() {
105
113
  return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
106
114
  }
115
+ async initializeSocketIO() {
116
+ var e;
117
+ if (!((e = this.config) != null && e.webSocket) || !("type" in this.config.webSocket)) return;
118
+ const t = this.config.webSocket;
119
+ if (t.type === "socket.io")
120
+ try {
121
+ if (typeof t.connection == "string") {
122
+ const n = (await import("socket.io-client")).default;
123
+ this.socketIO = n(t.connection, t.options || {});
124
+ } else
125
+ this.socketIO = t.connection;
126
+ if (!this.socketIO) return;
127
+ this.socketIO.on("connect", () => {
128
+ process.env.NODE_ENV === "development" && console.log("[Analytics Socket.IO] Connected"), t.onConnect && t.onConnect();
129
+ }), this.socketIO.on("connect_error", (i) => {
130
+ console.error("[Analytics Socket.IO] Connection error:", i), t.onError && t.onError(i);
131
+ }), this.socketIO.on("disconnect", (i) => {
132
+ process.env.NODE_ENV === "development" && console.log("[Analytics Socket.IO] Disconnected:", i), t.onDisconnect && t.onDisconnect(i);
133
+ }), this.socketIO.on("error", (i) => {
134
+ console.error("[Analytics Socket.IO] Error:", i), t.onError && t.onError(i);
135
+ });
136
+ } catch (i) {
137
+ console.error("[Analytics Socket.IO] Failed to initialize:", i);
138
+ }
139
+ }
140
+ sendToSocketIO(t) {
141
+ var e;
142
+ if (!(!this.socketIO || !this.socketIO.connected))
143
+ try {
144
+ const i = (e = this.config) == null ? void 0 : e.webSocket, n = i != null && i.transform ? i.transform(t) : t, o = (i == null ? void 0 : i.eventName) || "analytics";
145
+ this.socketIO.emit(o, n), process.env.NODE_ENV === "development" && console.log(`[Analytics Socket.IO] Emitted (${o}):`, t.eventType);
146
+ } catch (i) {
147
+ console.error("[Analytics Socket.IO] Failed to emit event:", i);
148
+ }
149
+ }
150
+ initializeWebSocket() {
151
+ var e;
152
+ if (!((e = this.config) != null && e.webSocket)) return;
153
+ const t = this.config.webSocket;
154
+ try {
155
+ typeof t.connection == "string" ? this.webSocket = new WebSocket(t.connection) : this.webSocket = t.connection, this.webSocket.onopen = (i) => {
156
+ process.env.NODE_ENV === "development" && console.log("[Analytics WebSocket] Connected"), t.onOpen && t.onOpen(i);
157
+ }, this.webSocket.onerror = (i) => {
158
+ console.error("[Analytics WebSocket] Error:", i), t.onError && t.onError(i);
159
+ }, this.webSocket.onclose = (i) => {
160
+ if (process.env.NODE_ENV === "development" && console.log("[Analytics WebSocket] Disconnected"), t.onClose && t.onClose(i), t.autoReconnect !== !1 && !this.isDestroyed) {
161
+ const o = t.reconnectDelay || 3e3;
162
+ process.env.NODE_ENV === "development" && console.log(`[Analytics WebSocket] Reconnecting in ${o}ms...`), this.wsReconnectTimeout = window.setTimeout(() => {
163
+ this.initializeWebSocket();
164
+ }, o);
165
+ }
166
+ };
167
+ } catch (i) {
168
+ console.error("[Analytics WebSocket] Failed to initialize:", i);
169
+ }
170
+ }
171
+ sendToWebSocket(t) {
172
+ var e;
173
+ if (!(!this.webSocket || this.webSocket.readyState !== WebSocket.OPEN))
174
+ try {
175
+ const i = (e = this.config) == null ? void 0 : e.webSocket, n = i != null && i.transform ? i.transform(t) : t;
176
+ this.webSocket.send(JSON.stringify(n)), process.env.NODE_ENV === "development" && console.log("[Analytics WebSocket] Sent:", t.eventType);
177
+ } catch (i) {
178
+ console.error("[Analytics WebSocket] Failed to send event:", i);
179
+ }
180
+ }
107
181
  getEvents() {
108
182
  return [...this.events];
109
183
  }
@@ -116,42 +190,42 @@ class W {
116
190
  }
117
191
  destroy() {
118
192
  var t;
119
- (t = this.config) != null && t.enabled && this.trackEvent("session_end", this.getSessionData()), this.events = [];
193
+ this.isDestroyed = !0, (t = this.config) != null && t.enabled && this.trackEvent("session_end", this.getSessionData()), this.wsReconnectTimeout && (clearTimeout(this.wsReconnectTimeout), this.wsReconnectTimeout = null), this.webSocket && (this.webSocket.close(), this.webSocket = null), this.socketIO && (this.socketIO.removeAllListeners(), this.socketIO.disconnect(), this.socketIO = null), this.events = [];
120
194
  }
121
195
  }
122
- class j {
196
+ class W {
123
197
  constructor(t, e) {
124
- o(this, "container");
125
- o(this, "player");
126
- o(this, "controlsContainer");
127
- o(this, "progressContainer");
128
- o(this, "progressBar");
129
- o(this, "playButton");
130
- o(this, "skipBackwardButton");
131
- o(this, "skipForwardButton");
132
- o(this, "volumeButton");
133
- o(this, "volumeContainer");
134
- o(this, "fullscreenButton");
135
- o(this, "pipButton");
136
- o(this, "settingsButton");
198
+ s(this, "container");
199
+ s(this, "player");
200
+ s(this, "controlsContainer");
201
+ s(this, "progressContainer");
202
+ s(this, "progressBar");
203
+ s(this, "playButton");
204
+ s(this, "skipBackwardButton");
205
+ s(this, "skipForwardButton");
206
+ s(this, "volumeButton");
207
+ s(this, "volumeContainer");
208
+ s(this, "fullscreenButton");
209
+ s(this, "pipButton");
210
+ s(this, "settingsButton");
137
211
  // private timeDisplay: HTMLElement
138
- o(this, "volumeSlider");
139
- o(this, "progressInput");
212
+ s(this, "volumeSlider");
213
+ s(this, "progressInput");
140
214
  // private controlsVisible = true
141
- o(this, "hideControlsTimeout", null);
142
- o(this, "stickyControls", !1);
143
- o(this, "isVolumeSliderActive", !1);
215
+ s(this, "hideControlsTimeout", null);
216
+ s(this, "stickyControls", !1);
217
+ s(this, "isVolumeSliderActive", !1);
144
218
  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
219
  }
146
220
  injectStyles() {
147
221
  const t = "wontum-player-styles";
148
222
  if (document.getElementById(t)) return;
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");
223
+ const e = this.player.config.theme || {}, i = e.primaryColor || "#3b82f6", n = e.accentColor || "#2563eb", o = 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
224
  m.id = t, m.textContent = `
151
225
  .wontum-player-container {
152
226
  position: relative;
153
227
  background: #000;
154
- font-family: ${s};
228
+ font-family: ${o};
155
229
  overflow: hidden;
156
230
  --primary-color: ${i};
157
231
  --accent-color: ${n};
@@ -729,11 +803,11 @@ class j {
729
803
  }), this.skipForwardButton.addEventListener("click", () => {
730
804
  this.player.skipForward(10);
731
805
  }), this.progressInput.addEventListener("input", (i) => {
732
- const n = i.target, s = parseFloat(n.value), r = this.player.getState(), a = s / 100 * r.duration;
806
+ const n = i.target, o = parseFloat(n.value), r = this.player.getState(), a = o / 100 * r.duration;
733
807
  this.player.seek(a);
734
808
  }), this.volumeSlider.addEventListener("input", (i) => {
735
- const n = i.target, s = parseFloat(n.value) / 100;
736
- this.player.setVolume(s);
809
+ const n = i.target, o = parseFloat(n.value) / 100;
810
+ this.player.setVolume(o);
737
811
  }), this.volumeButton.addEventListener("click", () => {
738
812
  this.player.getState().muted ? this.player.unmute() : this.player.mute();
739
813
  }), this.volumeContainer.addEventListener("mouseenter", () => {
@@ -761,8 +835,8 @@ class j {
761
835
  const t = this.controlsContainer.querySelectorAll(".wontum-tab");
762
836
  t.forEach((i) => {
763
837
  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"));
838
+ const o = n.currentTarget, r = o.getAttribute("data-tab");
839
+ t.forEach((u) => u.classList.remove("active")), o.classList.add("active"), this.controlsContainer.querySelectorAll(".wontum-tab-panel").forEach((u) => u.classList.remove("active"));
766
840
  const l = this.controlsContainer.querySelector(`[data-panel="${r}"]`);
767
841
  l == null || l.classList.add("active");
768
842
  });
@@ -782,8 +856,8 @@ class j {
782
856
  }), this.player.on("timeupdate", (t) => {
783
857
  const { currentTime: e } = t.data, i = this.player.getState();
784
858
  if (i.duration > 0) {
785
- const s = e / i.duration * 100;
786
- this.progressBar.style.width = `${s}%`, this.progressInput.value = s.toString();
859
+ const o = e / i.duration * 100;
860
+ this.progressBar.style.width = `${o}%`, this.progressInput.value = o.toString();
787
861
  }
788
862
  const n = this.controlsContainer.querySelector(".wontum-current-time");
789
863
  n.textContent = this.formatTime(e);
@@ -811,15 +885,15 @@ class j {
811
885
  t.innerHTML = `
812
886
  <div class="wontum-subtitle-option ${i === -1 ? "active" : ""}" data-track="-1">Off</div>
813
887
  ${e.map(
814
- (n, s) => `
815
- <div class="wontum-subtitle-option ${s === i ? "active" : ""}" data-track="${s}">
816
- ${n.label || n.language || `Track ${s + 1}`}
888
+ (n, o) => `
889
+ <div class="wontum-subtitle-option ${o === i ? "active" : ""}" data-track="${o}">
890
+ ${n.label || n.language || `Track ${o + 1}`}
817
891
  </div>
818
892
  `
819
893
  ).join("")}
820
894
  `, t.querySelectorAll(".wontum-subtitle-option").forEach((n) => {
821
- n.addEventListener("click", (s) => {
822
- const r = s.target, a = parseInt(r.dataset.track || "-1");
895
+ n.addEventListener("click", (o) => {
896
+ const r = o.target, a = parseInt(r.dataset.track || "-1");
823
897
  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
898
  });
825
899
  });
@@ -827,13 +901,13 @@ class j {
827
901
  updateSpeedMenu() {
828
902
  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
903
  t.innerHTML = n.map(
830
- (s) => `
831
- <div class="wontum-speed-option ${i === s ? "active" : ""}" data-speed="${s}">
832
- ${s === 1 ? "Normal" : s + "x"}
904
+ (o) => `
905
+ <div class="wontum-speed-option ${i === o ? "active" : ""}" data-speed="${o}">
906
+ ${o === 1 ? "Normal" : o + "x"}
833
907
  </div>
834
908
  `
835
- ).join(""), t.querySelectorAll(".wontum-speed-option").forEach((s) => {
836
- s.addEventListener("click", (r) => {
909
+ ).join(""), t.querySelectorAll(".wontum-speed-option").forEach((o) => {
910
+ o.addEventListener("click", (r) => {
837
911
  const a = r.target, l = parseFloat(a.dataset.speed || "1");
838
912
  this.player.setPlaybackRate(l), t.querySelectorAll(".wontum-speed-option").forEach((u) => u.classList.remove("active")), a.classList.add("active");
839
913
  });
@@ -861,13 +935,13 @@ class j {
861
935
  e.innerHTML = `
862
936
  <div class="wontum-quality-option active" data-quality="-1">Auto</div>
863
937
  ${i.map(
864
- (n, s) => `
865
- <div class="wontum-quality-option" data-quality="${s}">${n.name}</div>
938
+ (n, o) => `
939
+ <div class="wontum-quality-option" data-quality="${o}">${n.name}</div>
866
940
  `
867
941
  ).join("")}
868
942
  `, e.querySelectorAll(".wontum-quality-option").forEach((n) => {
869
- n.addEventListener("click", (s) => {
870
- const r = s.target, a = parseInt(r.dataset.quality || "-1");
943
+ n.addEventListener("click", (o) => {
944
+ const r = o.target, a = parseInt(r.dataset.quality || "-1");
871
945
  this.player.setQuality(a), e.querySelectorAll(".wontum-quality-option").forEach((l) => l.classList.remove("active")), r.classList.add("active");
872
946
  });
873
947
  });
@@ -933,11 +1007,11 @@ class j {
933
1007
  this.hideControlsTimeout && clearTimeout(this.hideControlsTimeout), this.controlsContainer.remove();
934
1008
  }
935
1009
  }
936
- class Q {
1010
+ class _ {
937
1011
  constructor(t) {
938
- o(this, "config");
939
- o(this, "urlCache", /* @__PURE__ */ new Map());
940
- o(this, "signedUrls", /* @__PURE__ */ new Set());
1012
+ s(this, "config");
1013
+ s(this, "urlCache", /* @__PURE__ */ new Map());
1014
+ s(this, "signedUrls", /* @__PURE__ */ new Set());
941
1015
  this.config = t;
942
1016
  }
943
1017
  /**
@@ -971,10 +1045,10 @@ class Q {
971
1045
  * The endpoint should set signed cookies and return the URL
972
1046
  */
973
1047
  async signCloudFrontUrl(t, e = 0) {
974
- var s, r;
1048
+ var o, r;
975
1049
  if (this.signedUrls.has(t))
976
1050
  return t;
977
- if ((s = this.config) != null && s.signUrl)
1051
+ if ((o = this.config) != null && o.signUrl)
978
1052
  try {
979
1053
  const a = await this.config.signUrl(t);
980
1054
  return this.signedUrls.add(t), a;
@@ -1010,13 +1084,13 @@ class Q {
1010
1084
  return i.url;
1011
1085
  if ((n = this.config) != null && n.getPresignedUrl)
1012
1086
  try {
1013
- const s = await this.config.getPresignedUrl(e);
1087
+ const o = await this.config.getPresignedUrl(e);
1014
1088
  return this.urlCache.set(e, {
1015
- url: s,
1089
+ url: o,
1016
1090
  expiresAt: Date.now() + 50 * 60 * 1e3
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");
1091
+ }), o;
1092
+ } catch (o) {
1093
+ throw console.error("Failed to generate presigned URL:", o), new Error("Failed to generate presigned URL for S3 object");
1020
1094
  }
1021
1095
  return console.warn("No getPresignedUrl function provided. Using direct S3 URL (requires public bucket)"), t;
1022
1096
  }
@@ -1042,18 +1116,18 @@ class Q {
1042
1116
  this.urlCache.clear(), this.signedUrls.clear();
1043
1117
  }
1044
1118
  }
1045
- class A {
1119
+ class $ {
1046
1120
  constructor(t) {
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", {
1121
+ s(this, "container");
1122
+ s(this, "videoElement");
1123
+ s(this, "hls", null);
1124
+ s(this, "config");
1125
+ s(this, "eventListeners", /* @__PURE__ */ new Map());
1126
+ s(this, "analytics");
1127
+ s(this, "s3Handler");
1128
+ s(this, "uiController");
1129
+ s(this, "qualities", []);
1130
+ s(this, "state", {
1057
1131
  playing: !1,
1058
1132
  paused: !0,
1059
1133
  ended: !1,
@@ -1069,7 +1143,7 @@ class A {
1069
1143
  });
1070
1144
  if (this.config = t, this.container = typeof t.container == "string" ? document.querySelector(t.container) : t.container, !this.container)
1071
1145
  throw new Error("Container element not found");
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);
1146
+ this.analytics = new N(t.analytics), this.s3Handler = new _(t.s3Config), this.videoElement = this.createVideoElement(), this.container.appendChild(this.videoElement), this.uiController = new W(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
1147
  }
1074
1148
  addSubtitleTracks(t) {
1075
1149
  t.forEach((e) => {
@@ -1139,14 +1213,14 @@ class A {
1139
1213
  try {
1140
1214
  const i = await this.s3Handler.processUrl(t);
1141
1215
  if (p.isSupported()) {
1142
- const n = ((e = this.config.s3Config) == null ? void 0 : e.withCredentials) ?? !1, s = {
1216
+ const n = ((e = this.config.s3Config) == null ? void 0 : e.withCredentials) ?? !1, o = {
1143
1217
  ...this.config.hlsConfig,
1144
1218
  xhrSetup: (r, a) => {
1145
1219
  var l;
1146
1220
  n && (r.withCredentials = !0), (l = this.config.hlsConfig) != null && l.xhrSetup && this.config.hlsConfig.xhrSetup(r, a);
1147
1221
  }
1148
1222
  };
1149
- this.hls = new p(s), this.hls.loadSource(i), this.hls.attachMedia(this.videoElement), this.hls.on(p.Events.MANIFEST_PARSED, (r, a) => {
1223
+ this.hls = new p(o), this.hls.loadSource(i), this.hls.attachMedia(this.videoElement), this.hls.on(p.Events.MANIFEST_PARSED, (r, a) => {
1150
1224
  const l = this.extractQualities(a.levels);
1151
1225
  this.qualities = l;
1152
1226
  }), this.hls.on(p.Events.LEVEL_SWITCHED, (r, a) => {
@@ -1313,8 +1387,8 @@ class A {
1313
1387
  data: e,
1314
1388
  timestamp: Date.now()
1315
1389
  };
1316
- (n = this.eventListeners.get(t)) == null || n.forEach((s) => {
1317
- s(i);
1390
+ (n = this.eventListeners.get(t)) == null || n.forEach((o) => {
1391
+ o(i);
1318
1392
  });
1319
1393
  }
1320
1394
  destroy() {
@@ -1323,9 +1397,9 @@ class A {
1323
1397
  }
1324
1398
  class G {
1325
1399
  constructor(t) {
1326
- o(this, "file");
1327
- o(this, "videoElement", null);
1328
- o(this, "info", null);
1400
+ s(this, "file");
1401
+ s(this, "videoElement", null);
1402
+ s(this, "info", null);
1329
1403
  if (!this.isVideoFile(t))
1330
1404
  throw new Error(`Invalid file type: ${t.type}. Expected a video file.`);
1331
1405
  this.file = t;
@@ -1353,10 +1427,10 @@ class G {
1353
1427
  e(new Error("Video element not initialized"));
1354
1428
  return;
1355
1429
  }
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;
1430
+ const n = this.videoElement.videoWidth, o = this.videoElement.videoHeight, r = this.videoElement.duration, a = this.calculateAspectRatio(n, o), 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
1431
  this.info = {
1358
1432
  width: n,
1359
- height: s,
1433
+ height: o,
1360
1434
  aspectRatio: a,
1361
1435
  size: l,
1362
1436
  sizeInBytes: l,
@@ -1386,8 +1460,8 @@ class G {
1386
1460
  * Calculate aspect ratio (e.g., "16:9", "4:3")
1387
1461
  */
1388
1462
  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}`;
1463
+ const i = this.getGCD(t, e), n = t / i, o = e / i, r = n / o;
1464
+ 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}:${o}`;
1391
1465
  }
1392
1466
  /**
1393
1467
  * Get Greatest Common Divisor
@@ -1494,7 +1568,7 @@ const K = (c) => {
1494
1568
  autoplay: e,
1495
1569
  muted: i,
1496
1570
  controls: n = !0,
1497
- poster: s,
1571
+ poster: o,
1498
1572
  preload: r,
1499
1573
  theme: a,
1500
1574
  s3Config: l,
@@ -1502,29 +1576,29 @@ const K = (c) => {
1502
1576
  hlsConfig: m,
1503
1577
  subtitles: g,
1504
1578
  stickyControls: v,
1505
- onReady: x,
1506
- onPlay: S,
1579
+ onReady: S,
1580
+ onPlay: x,
1507
1581
  onPause: C,
1508
1582
  onEnded: L,
1509
1583
  onTimeUpdate: T,
1510
1584
  onVolumeChange: I,
1511
1585
  onError: M,
1512
- onLoadedMetadata: q,
1586
+ onLoadedMetadata: R,
1513
1587
  onQualityChange: P,
1514
- style: U,
1515
- className: D,
1588
+ style: D,
1589
+ className: O,
1516
1590
  width: f = "100%",
1517
- height: w = "500px"
1518
- } = c, y = b(null), B = b(null);
1591
+ height: y = "500px"
1592
+ } = c, w = b(null), q = b(null);
1519
1593
  return E(() => {
1520
- if (!y.current) return;
1521
- const H = {
1594
+ if (!w.current) return;
1595
+ const U = {
1522
1596
  src: t,
1523
- container: y.current,
1597
+ container: w.current,
1524
1598
  autoplay: e,
1525
1599
  muted: i,
1526
1600
  controls: n,
1527
- poster: s,
1601
+ poster: o,
1528
1602
  preload: r,
1529
1603
  theme: a,
1530
1604
  s3Config: l,
@@ -1532,32 +1606,32 @@ const K = (c) => {
1532
1606
  hlsConfig: m,
1533
1607
  subtitles: g,
1534
1608
  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;
1609
+ }, d = new $(U);
1610
+ return q.current = d, x && d.on("play", x), C && d.on("pause", C), L && d.on("ended", L), M && d.on("error", (h) => {
1611
+ var B;
1612
+ return M((B = h.data) == null ? void 0 : B.error);
1613
+ }), R && d.on("loadedmetadata", R), 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)), S && S(d), () => {
1614
+ d.destroy(), q.current = null;
1541
1615
  };
1542
- }, [t]), /* @__PURE__ */ F(
1616
+ }, [t]), /* @__PURE__ */ A(
1543
1617
  "div",
1544
1618
  {
1545
- ref: y,
1546
- className: D,
1619
+ ref: w,
1620
+ className: O,
1547
1621
  style: {
1548
1622
  width: typeof f == "number" ? `${f}px` : f,
1549
- height: typeof w == "number" ? `${w}px` : w,
1550
- ...U
1623
+ height: typeof y == "number" ? `${y}px` : y,
1624
+ ...D
1551
1625
  }
1552
1626
  }
1553
1627
  );
1554
1628
  }, J = (c) => {
1555
- const [t, e] = k(null), [i, n] = k(null), s = b(null);
1629
+ const [t, e] = k(null), [i, n] = k(null), o = b(null);
1556
1630
  return E(() => {
1557
- if (!s.current) return;
1558
- const r = new A({
1631
+ if (!o.current) return;
1632
+ const r = new $({
1559
1633
  ...c,
1560
- container: s.current
1634
+ container: o.current
1561
1635
  });
1562
1636
  e(r);
1563
1637
  const a = () => {
@@ -1567,34 +1641,34 @@ const K = (c) => {
1567
1641
  r.destroy();
1568
1642
  };
1569
1643
  }, [c.src]), {
1570
- containerRef: s,
1644
+ containerRef: o,
1571
1645
  player: t,
1572
1646
  state: i
1573
1647
  };
1574
- }, z = $.createContext({
1648
+ }, z = F.createContext({
1575
1649
  player: null,
1576
1650
  state: null
1577
1651
  }), Z = (c) => {
1578
1652
  const { player: t, children: e } = c, [i, n] = k(t.getState());
1579
1653
  return E(() => {
1580
- const s = () => {
1654
+ const o = () => {
1581
1655
  n(t.getState());
1582
1656
  };
1583
- return t.on("play", s), t.on("pause", s), t.on("timeupdate", s), t.on("volumechange", s), t.on("loadedmetadata", s), () => {
1657
+ return t.on("play", o), t.on("pause", o), t.on("timeupdate", o), t.on("volumechange", o), t.on("loadedmetadata", o), () => {
1584
1658
  };
1585
- }, [t]), /* @__PURE__ */ F(z.Provider, { value: { player: t, state: i }, children: e });
1659
+ }, [t]), /* @__PURE__ */ A(z.Provider, { value: { player: t, state: i }, children: e });
1586
1660
  }, tt = () => {
1587
- const c = $.useContext(z);
1661
+ const c = F.useContext(z);
1588
1662
  if (!c.player)
1589
1663
  throw new Error("useWontumPlayerContext must be used within WontumPlayerProvider");
1590
1664
  return c;
1591
1665
  };
1592
1666
  export {
1593
- W as Analytics,
1594
- Q as S3Handler,
1595
- j as UIController,
1667
+ N as Analytics,
1668
+ _ as S3Handler,
1669
+ W as UIController,
1596
1670
  G as WontumFileInfo,
1597
- A as WontumPlayer,
1671
+ $ as WontumPlayer,
1598
1672
  Z as WontumPlayerProvider,
1599
1673
  K as WontumPlayerReact,
1600
1674
  J as useWontumPlayer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obipascal/player",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
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",
@@ -33,8 +33,10 @@
33
33
  "@types/node": "^20.10.0",
34
34
  "@types/react": "^19.2.8",
35
35
  "@types/react-dom": "^19.2.3",
36
+ "@types/socket.io-client": "^1.4.36",
36
37
  "react": "^19.2.3",
37
38
  "react-dom": "^19.2.3",
39
+ "socket.io-client": "^4.8.3",
38
40
  "typescript": "^5.3.3",
39
41
  "vite": "^5.0.8",
40
42
  "vite-plugin-dts": "^3.7.0"
@@ -53,5 +55,8 @@
53
55
  "react-dom": {
54
56
  "optional": true
55
57
  }
58
+ },
59
+ "optionalDependencies": {
60
+ "socket.io-client": "^4.8.3"
56
61
  }
57
62
  }