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