@obipascal/player 1.0.8 → 1.0.10

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
@@ -1169,11 +1421,20 @@ fileInput.addEventListener("change", async (event) => {
1169
1421
  console.log("- File Name:", videoInfo.fileName) // e.g., "my-video.mp4"
1170
1422
  console.log("- Extension:", videoInfo.fileExtension) // e.g., ".mp4"
1171
1423
  console.log("- Bitrate:", videoInfo.bitrate, "kbps") // e.g., 3500
1424
+ console.log("- Frame Rate:", videoInfo.frameRate, "fps") // e.g., 30 or 60
1425
+ console.log("- Has Audio:", videoInfo.hasAudio) // e.g., true
1426
+ console.log("- Audio Channels:", videoInfo.audioChannels) // e.g., 2 (stereo)
1172
1427
 
1173
1428
  // Get all info as object
1174
1429
  const allInfo = videoInfo.getInfo()
1175
1430
  console.log(allInfo)
1176
1431
 
1432
+ // Validate against platform requirements
1433
+ const isValid = validateVideo(videoInfo)
1434
+ if (!isValid.valid) {
1435
+ console.error("Validation errors:", isValid.errors)
1436
+ }
1437
+
1177
1438
  // Clean up when done
1178
1439
  videoInfo.destroy()
1179
1440
  } catch (error) {
@@ -1181,6 +1442,56 @@ fileInput.addEventListener("change", async (event) => {
1181
1442
  // Throws error if file is not a video
1182
1443
  }
1183
1444
  })
1445
+
1446
+ // Example validation function for educational platform
1447
+ function validateVideo(info: VideoFileInfo) {
1448
+ const errors: string[] = []
1449
+
1450
+ // Aspect Ratio: 16:9 required
1451
+ if (info.aspectRatio !== "16:9") {
1452
+ errors.push(`Aspect ratio must be 16:9, got ${info.aspectRatio}`)
1453
+ }
1454
+
1455
+ // Resolution: Minimum 1280×720
1456
+ if (info.height < 720 || info.width < 1280) {
1457
+ errors.push(`Minimum resolution is 1280×720, got ${info.width}×${info.height}`)
1458
+ }
1459
+
1460
+ // File Format: .MP4 or .MOV
1461
+ if (![".mp4", ".mov"].includes(info.fileExtension.toLowerCase())) {
1462
+ errors.push(`File format must be MP4 or MOV, got ${info.fileExtension}`)
1463
+ }
1464
+
1465
+ // Bitrate: 5-10 Mbps
1466
+ if (info.bitrate && (info.bitrate < 5000 || info.bitrate > 10000)) {
1467
+ errors.push(`Bitrate should be 5-10 Mbps, got ${info.bitrate} kbps`)
1468
+ }
1469
+
1470
+ // Audio: Must be stereo (2 channels)
1471
+ if (!info.hasAudio) {
1472
+ errors.push("Video must have audio track")
1473
+ } else if (info.audioChannels && info.audioChannels !== 2) {
1474
+ errors.push(`Audio must be stereo (2 channels), got ${info.audioChannels}`)
1475
+ }
1476
+
1477
+ // File Size: ≤4.0 GB
1478
+ const maxSize = 4 * 1024 * 1024 * 1024 // 4GB in bytes
1479
+ if (info.sizeInBytes > maxSize) {
1480
+ errors.push(`File size must be ≤4GB, got ${info.sizeFormatted}`)
1481
+ }
1482
+
1483
+ // Duration: 2 minutes to 2 hours
1484
+ if (info.durationInSeconds < 120 || info.durationInSeconds > 7200) {
1485
+ errors.push(`Duration must be 2min-2hrs, got ${info.durationFormatted}`)
1486
+ }
1487
+
1488
+ // Frame Rate: 30 or 60 fps
1489
+ if (info.frameRate && ![30, 60].includes(info.frameRate)) {
1490
+ errors.push(`Frame rate should be 30 or 60 fps, got ${info.frameRate}`)
1491
+ }
1492
+
1493
+ return { valid: errors.length === 0, errors }
1494
+ }
1184
1495
  ```
1185
1496
 
1186
1497
  #### WontumFileInfo API
@@ -1215,6 +1526,13 @@ Throws an error if the file is not a valid video file.
1215
1526
  - `fileName: string` - Original file name
1216
1527
  - `fileExtension: string` - File extension (e.g., ".mp4")
1217
1528
  - `bitrate: number | undefined` - Estimated bitrate in kbps
1529
+ - `frameRate: number | undefined` - Frame rate in fps (30, 60, etc.)
1530
+ - `hasAudio: boolean` - Whether video has an audio track
1531
+ - `audioChannels: number | undefined` - Number of audio channels (1=mono, 2=stereo)
1532
+
1533
+ **Validation Use Case:**
1534
+
1535
+ Perfect for validating videos against platform requirements (aspect ratio, resolution, format, bitrate, audio channels, file size, duration, frame rate).
1218
1536
 
1219
1537
  **Supported Video Formats:**
1220
1538
 
@@ -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;
@@ -2,6 +2,15 @@
2
2
  * Video file information extractor
3
3
  * Extracts metadata from video files (width, height, duration, size, etc.)
4
4
  */
5
+ declare global {
6
+ interface HTMLVideoElement {
7
+ mozHasAudio?: boolean;
8
+ webkitAudioDecodedByteCount?: number;
9
+ audioTracks?: {
10
+ length: number;
11
+ };
12
+ }
13
+ }
5
14
  export interface VideoFileInfo {
6
15
  width: number;
7
16
  height: number;
@@ -17,12 +26,15 @@ export interface VideoFileInfo {
17
26
  fileExtension: string;
18
27
  bitrate?: number;
19
28
  frameRate?: number;
29
+ audioChannels?: number;
20
30
  videoCodec?: string;
21
31
  audioCodec?: string;
32
+ hasAudio?: boolean;
22
33
  }
23
34
  export declare class WontumFileInfo {
24
35
  private file;
25
36
  private videoElement;
37
+ private audioContext;
26
38
  private info;
27
39
  constructor(file: File);
28
40
  /**
@@ -37,6 +49,14 @@ export declare class WontumFileInfo {
37
49
  * Calculate aspect ratio (e.g., "16:9", "4:3")
38
50
  */
39
51
  private calculateAspectRatio;
52
+ /**
53
+ * Detect frame rate by analyzing video playback
54
+ */
55
+ private detectFrameRate;
56
+ /**
57
+ * Detect audio channel information using Web Audio API
58
+ */
59
+ private detectAudioInfo;
40
60
  /**
41
61
  * Get Greatest Common Divisor
42
62
  */
@@ -66,6 +86,9 @@ export declare class WontumFileInfo {
66
86
  get fileName(): string;
67
87
  get fileExtension(): string;
68
88
  get bitrate(): number | undefined;
89
+ get frameRate(): number | undefined;
90
+ get audioChannels(): number | undefined;
91
+ get hasAudio(): boolean;
69
92
  get quality(): string;
70
93
  /**
71
94
  * Get all information as object
@@ -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
  */