unified-video-framework 1.4.129 → 1.4.131
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/package.json +1 -1
- package/packages/core/dist/interfaces.d.ts +20 -0
- package/packages/core/dist/interfaces.d.ts.map +1 -1
- package/packages/core/src/interfaces.ts +21 -0
- package/packages/web/dist/WebPlayer.d.ts +2 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +173 -53
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/src/WebPlayer.ts +219 -63
|
@@ -40,6 +40,14 @@ export class WebPlayer extends BasePlayer {
|
|
|
40
40
|
private currentSubtitle = 'off';
|
|
41
41
|
private currentPlaybackRate = 1;
|
|
42
42
|
private isDragging: boolean = false;
|
|
43
|
+
|
|
44
|
+
// Settings configuration
|
|
45
|
+
private settingsConfig = {
|
|
46
|
+
enabled: true, // Show settings button
|
|
47
|
+
speed: true, // Show playback speed options
|
|
48
|
+
quality: true, // Show quality options
|
|
49
|
+
subtitles: true // Show subtitle options
|
|
50
|
+
};
|
|
43
51
|
private watermarkCanvas: HTMLCanvasElement | null = null;
|
|
44
52
|
private playerWrapper: HTMLElement | null = null;
|
|
45
53
|
// Free preview gate state
|
|
@@ -96,6 +104,26 @@ export class WebPlayer extends BasePlayer {
|
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
|
|
107
|
+
async initialize(container: HTMLElement | string, config?: any): Promise<void> {
|
|
108
|
+
// Set useCustomControls based on config before calling parent initialize
|
|
109
|
+
if (config && config.customControls !== undefined) {
|
|
110
|
+
this.useCustomControls = config.customControls;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Configure settings menu options
|
|
114
|
+
if (config && config.settings) {
|
|
115
|
+
this.settingsConfig = {
|
|
116
|
+
enabled: config.settings.enabled !== undefined ? config.settings.enabled : true,
|
|
117
|
+
speed: config.settings.speed !== undefined ? config.settings.speed : true,
|
|
118
|
+
quality: config.settings.quality !== undefined ? config.settings.quality : true,
|
|
119
|
+
subtitles: config.settings.subtitles !== undefined ? config.settings.subtitles : true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Call parent initialize
|
|
124
|
+
await super.initialize(container, config);
|
|
125
|
+
}
|
|
126
|
+
|
|
99
127
|
protected async setupPlayer(): Promise<void> {
|
|
100
128
|
if (!this.container) {
|
|
101
129
|
throw new Error('Container element is required');
|
|
@@ -2278,6 +2306,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
2278
2306
|
overflow-y: auto;
|
|
2279
2307
|
-webkit-overflow-scrolling: touch;
|
|
2280
2308
|
overscroll-behavior: contain;
|
|
2309
|
+
z-index: 1000; /* Ensure settings menu appears above other elements */
|
|
2281
2310
|
/* Firefox */
|
|
2282
2311
|
scrollbar-width: thin;
|
|
2283
2312
|
scrollbar-color: var(--uvf-firefox-scrollbar-color) transparent;
|
|
@@ -4228,8 +4257,14 @@ export class WebPlayer extends BasePlayer {
|
|
|
4228
4257
|
|
|
4229
4258
|
/**
|
|
4230
4259
|
* Creates framework branding logo
|
|
4260
|
+
* Only creates branding if showFrameworkBranding is not explicitly set to false
|
|
4231
4261
|
*/
|
|
4232
4262
|
private createFrameworkBranding(container: HTMLElement): void {
|
|
4263
|
+
// Double-check configuration (defensive programming)
|
|
4264
|
+
if ((this.config as any).showFrameworkBranding === false) {
|
|
4265
|
+
this.debugLog('Framework branding disabled by configuration');
|
|
4266
|
+
return;
|
|
4267
|
+
}
|
|
4233
4268
|
const brandingContainer = document.createElement('div');
|
|
4234
4269
|
brandingContainer.className = 'uvf-framework-branding';
|
|
4235
4270
|
brandingContainer.setAttribute('title', 'Powered by flicknexs');
|
|
@@ -4372,8 +4407,10 @@ export class WebPlayer extends BasePlayer {
|
|
|
4372
4407
|
timeDisplay.textContent = '00:00 / 00:00';
|
|
4373
4408
|
aboveSeekbarSection.appendChild(timeDisplay);
|
|
4374
4409
|
|
|
4375
|
-
// Add framework branding next to time display
|
|
4376
|
-
this.
|
|
4410
|
+
// Add framework branding next to time display if enabled
|
|
4411
|
+
if ((this.config as any).showFrameworkBranding !== false) {
|
|
4412
|
+
this.createFrameworkBranding(aboveSeekbarSection);
|
|
4413
|
+
}
|
|
4377
4414
|
|
|
4378
4415
|
// Progress section
|
|
4379
4416
|
const progressSection = document.createElement('div');
|
|
@@ -4457,25 +4494,29 @@ export class WebPlayer extends BasePlayer {
|
|
|
4457
4494
|
qualityBadge.textContent = 'HD';
|
|
4458
4495
|
rightControls.appendChild(qualityBadge);
|
|
4459
4496
|
|
|
4460
|
-
// Settings button with menu
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4497
|
+
// Settings button with menu (show only if enabled)
|
|
4498
|
+
if (this.settingsConfig.enabled) {
|
|
4499
|
+
const settingsContainer = document.createElement('div');
|
|
4500
|
+
settingsContainer.style.position = 'relative';
|
|
4501
|
+
|
|
4502
|
+
const settingsBtn = document.createElement('button');
|
|
4503
|
+
settingsBtn.className = 'uvf-control-btn';
|
|
4504
|
+
settingsBtn.id = 'uvf-settings-btn';
|
|
4505
|
+
settingsBtn.setAttribute('title', 'Settings');
|
|
4506
|
+
settingsBtn.setAttribute('aria-label', 'Settings');
|
|
4507
|
+
settingsBtn.innerHTML = '<svg viewBox="0 0 24 24"><path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/></svg>';
|
|
4508
|
+
settingsContainer.appendChild(settingsBtn);
|
|
4509
|
+
|
|
4510
|
+
// Settings menu - will be populated dynamically based on video capabilities and configuration
|
|
4511
|
+
const settingsMenu = document.createElement('div');
|
|
4512
|
+
settingsMenu.className = 'uvf-settings-menu';
|
|
4513
|
+
settingsMenu.id = 'uvf-settings-menu';
|
|
4514
|
+
// Initially empty - will be populated by updateSettingsMenu method
|
|
4515
|
+
settingsMenu.innerHTML = '';
|
|
4516
|
+
settingsMenu.style.display = 'none';
|
|
4517
|
+
settingsContainer.appendChild(settingsMenu);
|
|
4518
|
+
rightControls.appendChild(settingsContainer);
|
|
4519
|
+
}
|
|
4479
4520
|
|
|
4480
4521
|
// EPG button (Electronic Program Guide)
|
|
4481
4522
|
const epgBtn = document.createElement('button');
|
|
@@ -5047,9 +5088,36 @@ export class WebPlayer extends BasePlayer {
|
|
|
5047
5088
|
protected setupWatermark(): void {
|
|
5048
5089
|
if (!this.watermarkCanvas) return;
|
|
5049
5090
|
|
|
5091
|
+
// Get watermark configuration
|
|
5092
|
+
const watermarkConfig = (this.config as any).watermark || {};
|
|
5093
|
+
|
|
5094
|
+
// Check if watermark is disabled
|
|
5095
|
+
if (watermarkConfig.enabled === false) {
|
|
5096
|
+
this.debugLog('Watermark disabled by configuration');
|
|
5097
|
+
return;
|
|
5098
|
+
}
|
|
5099
|
+
|
|
5050
5100
|
const ctx = this.watermarkCanvas.getContext('2d');
|
|
5051
5101
|
if (!ctx) return;
|
|
5052
5102
|
|
|
5103
|
+
// Default configuration values
|
|
5104
|
+
const config = {
|
|
5105
|
+
text: watermarkConfig.text || 'PREMIUM',
|
|
5106
|
+
showTime: watermarkConfig.showTime !== false, // default true
|
|
5107
|
+
updateInterval: watermarkConfig.updateInterval || 5000,
|
|
5108
|
+
randomPosition: watermarkConfig.randomPosition !== false, // default true
|
|
5109
|
+
position: watermarkConfig.position || {},
|
|
5110
|
+
style: {
|
|
5111
|
+
fontSize: watermarkConfig.style?.fontSize || 14,
|
|
5112
|
+
fontFamily: watermarkConfig.style?.fontFamily || 'Arial',
|
|
5113
|
+
opacity: watermarkConfig.style?.opacity ?? 0.3,
|
|
5114
|
+
color: watermarkConfig.style?.color,
|
|
5115
|
+
gradientColors: watermarkConfig.style?.gradientColors || ['#ff0000', '#ff4d4f']
|
|
5116
|
+
}
|
|
5117
|
+
};
|
|
5118
|
+
|
|
5119
|
+
this.debugLog('Watermark configuration:', config);
|
|
5120
|
+
|
|
5053
5121
|
const renderWatermark = () => {
|
|
5054
5122
|
const container = this.watermarkCanvas!.parentElement;
|
|
5055
5123
|
if (!container) return;
|
|
@@ -5059,38 +5127,119 @@ export class WebPlayer extends BasePlayer {
|
|
|
5059
5127
|
|
|
5060
5128
|
ctx.clearRect(0, 0, this.watermarkCanvas!.width, this.watermarkCanvas!.height);
|
|
5061
5129
|
|
|
5062
|
-
//
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
const v2 = styles.getPropertyValue('--uvf-accent-2').trim();
|
|
5071
|
-
if (v1) c1 = v1;
|
|
5072
|
-
if (v2) c2 = v2;
|
|
5073
|
-
}
|
|
5074
|
-
} catch (_) {}
|
|
5075
|
-
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
|
|
5076
|
-
gradient.addColorStop(0, c1);
|
|
5077
|
-
gradient.addColorStop(1, c2);
|
|
5130
|
+
// Build watermark text
|
|
5131
|
+
let text = config.text;
|
|
5132
|
+
if (config.showTime) {
|
|
5133
|
+
const timeStr = new Date().toLocaleTimeString();
|
|
5134
|
+
text += ` • ${timeStr}`;
|
|
5135
|
+
}
|
|
5136
|
+
|
|
5137
|
+
// Set up styling
|
|
5078
5138
|
ctx.save();
|
|
5079
|
-
ctx.globalAlpha =
|
|
5080
|
-
ctx.font =
|
|
5081
|
-
ctx.fillStyle = gradient;
|
|
5139
|
+
ctx.globalAlpha = config.style.opacity;
|
|
5140
|
+
ctx.font = `${config.style.fontSize}px ${config.style.fontFamily}`;
|
|
5082
5141
|
ctx.textAlign = 'left';
|
|
5083
5142
|
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5143
|
+
// Set fill style
|
|
5144
|
+
if (config.style.color) {
|
|
5145
|
+
// Use solid color
|
|
5146
|
+
ctx.fillStyle = config.style.color;
|
|
5147
|
+
} else {
|
|
5148
|
+
// Use gradient (default or custom)
|
|
5149
|
+
const wrapper = this.playerWrapper as HTMLElement | null;
|
|
5150
|
+
let c1 = config.style.gradientColors[0];
|
|
5151
|
+
let c2 = config.style.gradientColors[1];
|
|
5152
|
+
|
|
5153
|
+
// Try to get theme colors if using defaults
|
|
5154
|
+
if (!watermarkConfig.style?.gradientColors) {
|
|
5155
|
+
try {
|
|
5156
|
+
if (wrapper) {
|
|
5157
|
+
const styles = getComputedStyle(wrapper);
|
|
5158
|
+
const v1 = styles.getPropertyValue('--uvf-accent-1').trim();
|
|
5159
|
+
const v2 = styles.getPropertyValue('--uvf-accent-2').trim();
|
|
5160
|
+
if (v1) c1 = v1;
|
|
5161
|
+
if (v2) c2 = v2;
|
|
5162
|
+
}
|
|
5163
|
+
} catch (_) {}
|
|
5164
|
+
}
|
|
5165
|
+
|
|
5166
|
+
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
|
|
5167
|
+
gradient.addColorStop(0, c1);
|
|
5168
|
+
gradient.addColorStop(1, c2);
|
|
5169
|
+
ctx.fillStyle = gradient;
|
|
5170
|
+
}
|
|
5171
|
+
|
|
5172
|
+
// Calculate position
|
|
5173
|
+
let x: number, y: number;
|
|
5087
5174
|
|
|
5175
|
+
if (config.randomPosition) {
|
|
5176
|
+
// Random position (default behavior)
|
|
5177
|
+
x = 20 + Math.random() * Math.max(0, this.watermarkCanvas!.width - 200);
|
|
5178
|
+
y = 40 + Math.random() * Math.max(0, this.watermarkCanvas!.height - 80);
|
|
5179
|
+
} else {
|
|
5180
|
+
// Fixed or calculated position
|
|
5181
|
+
const posX = config.position.x;
|
|
5182
|
+
const posY = config.position.y;
|
|
5183
|
+
|
|
5184
|
+
// Calculate X position
|
|
5185
|
+
if (typeof posX === 'number') {
|
|
5186
|
+
x = posX;
|
|
5187
|
+
} else {
|
|
5188
|
+
switch (posX) {
|
|
5189
|
+
case 'left':
|
|
5190
|
+
x = 20;
|
|
5191
|
+
break;
|
|
5192
|
+
case 'center':
|
|
5193
|
+
x = this.watermarkCanvas!.width / 2;
|
|
5194
|
+
ctx.textAlign = 'center';
|
|
5195
|
+
break;
|
|
5196
|
+
case 'right':
|
|
5197
|
+
x = this.watermarkCanvas!.width - 20;
|
|
5198
|
+
ctx.textAlign = 'right';
|
|
5199
|
+
break;
|
|
5200
|
+
case 'random':
|
|
5201
|
+
x = 20 + Math.random() * Math.max(0, this.watermarkCanvas!.width - 200);
|
|
5202
|
+
break;
|
|
5203
|
+
default:
|
|
5204
|
+
x = 20; // default left
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
|
|
5208
|
+
// Calculate Y position
|
|
5209
|
+
if (typeof posY === 'number') {
|
|
5210
|
+
y = posY;
|
|
5211
|
+
} else {
|
|
5212
|
+
switch (posY) {
|
|
5213
|
+
case 'top':
|
|
5214
|
+
y = 40;
|
|
5215
|
+
break;
|
|
5216
|
+
case 'center':
|
|
5217
|
+
y = this.watermarkCanvas!.height / 2;
|
|
5218
|
+
break;
|
|
5219
|
+
case 'bottom':
|
|
5220
|
+
y = this.watermarkCanvas!.height - 20;
|
|
5221
|
+
break;
|
|
5222
|
+
case 'random':
|
|
5223
|
+
y = 40 + Math.random() * Math.max(0, this.watermarkCanvas!.height - 80);
|
|
5224
|
+
break;
|
|
5225
|
+
default:
|
|
5226
|
+
y = 40; // default top
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
|
|
5231
|
+
// Render the watermark
|
|
5088
5232
|
ctx.fillText(text, x, y);
|
|
5089
5233
|
ctx.restore();
|
|
5234
|
+
|
|
5235
|
+
this.debugLog('Watermark rendered:', { text, x, y });
|
|
5090
5236
|
};
|
|
5091
5237
|
|
|
5092
|
-
|
|
5093
|
-
renderWatermark
|
|
5238
|
+
// Set up interval with configured frequency
|
|
5239
|
+
setInterval(renderWatermark, config.updateInterval);
|
|
5240
|
+
renderWatermark(); // Render immediately
|
|
5241
|
+
|
|
5242
|
+
this.debugLog('Watermark setup complete with update interval:', config.updateInterval + 'ms');
|
|
5094
5243
|
}
|
|
5095
5244
|
|
|
5096
5245
|
public setPaywallConfig(config: any) {
|
|
@@ -5981,22 +6130,24 @@ export class WebPlayer extends BasePlayer {
|
|
|
5981
6130
|
|
|
5982
6131
|
let menuHTML = '';
|
|
5983
6132
|
|
|
5984
|
-
// Playback Speed Section
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
<div class="uvf-settings-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6133
|
+
// Playback Speed Section (only if enabled in config)
|
|
6134
|
+
if (this.settingsConfig.speed) {
|
|
6135
|
+
menuHTML += `
|
|
6136
|
+
<div class="uvf-settings-group">
|
|
6137
|
+
<div class="uvf-settings-label">Playback Speed</div>
|
|
6138
|
+
<div class="uvf-settings-option speed-option" data-speed="0.25">0.25x</div>
|
|
6139
|
+
<div class="uvf-settings-option speed-option" data-speed="0.5">0.5x</div>
|
|
6140
|
+
<div class="uvf-settings-option speed-option" data-speed="0.75">0.75x</div>
|
|
6141
|
+
<div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 1 ? 'active' : ''}" data-speed="1">Normal</div>
|
|
6142
|
+
<div class="uvf-settings-option speed-option" data-speed="1.25">1.25x</div>
|
|
6143
|
+
<div class="uvf-settings-option speed-option" data-speed="1.5">1.5x</div>
|
|
6144
|
+
<div class="uvf-settings-option speed-option" data-speed="1.75">1.75x</div>
|
|
6145
|
+
<div class="uvf-settings-option speed-option" data-speed="2">2x</div>
|
|
6146
|
+
</div>`;
|
|
6147
|
+
}
|
|
6148
|
+
|
|
6149
|
+
// Quality Section (only if enabled in config and qualities detected)
|
|
6150
|
+
if (this.settingsConfig.quality && this.availableQualities.length > 0) {
|
|
6000
6151
|
menuHTML += `<div class="uvf-settings-group">
|
|
6001
6152
|
<div class="uvf-settings-label">Quality</div>`;
|
|
6002
6153
|
|
|
@@ -6008,8 +6159,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
6008
6159
|
menuHTML += `</div>`;
|
|
6009
6160
|
}
|
|
6010
6161
|
|
|
6011
|
-
// Subtitles Section (only if subtitles available)
|
|
6012
|
-
if (this.availableSubtitles.length > 0) {
|
|
6162
|
+
// Subtitles Section (only if enabled in config and subtitles available)
|
|
6163
|
+
if (this.settingsConfig.subtitles && this.availableSubtitles.length > 0) {
|
|
6013
6164
|
menuHTML += `<div class="uvf-settings-group">
|
|
6014
6165
|
<div class="uvf-settings-label">Subtitles/Captions</div>`;
|
|
6015
6166
|
|
|
@@ -6020,6 +6171,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
6020
6171
|
|
|
6021
6172
|
menuHTML += `</div>`;
|
|
6022
6173
|
}
|
|
6174
|
+
|
|
6175
|
+
// If no sections are enabled or available, show a message
|
|
6176
|
+
if (!menuHTML.trim()) {
|
|
6177
|
+
menuHTML = '<div class="uvf-settings-group"><div class="uvf-settings-label">No settings available</div></div>';
|
|
6178
|
+
}
|
|
6023
6179
|
|
|
6024
6180
|
settingsMenu.innerHTML = menuHTML;
|
|
6025
6181
|
|