unified-video-framework 1.4.243 → 1.4.245
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.
|
@@ -1176,14 +1176,15 @@ export class WebPlayer extends BasePlayer {
|
|
|
1176
1176
|
height: '100%',
|
|
1177
1177
|
playerVars: {
|
|
1178
1178
|
controls: 0, // Hide YouTube controls
|
|
1179
|
-
disablekb:
|
|
1179
|
+
disablekb: 0, // Allow keyboard controls
|
|
1180
1180
|
fs: 0, // Hide fullscreen button
|
|
1181
1181
|
iv_load_policy: 3, // Hide annotations
|
|
1182
1182
|
modestbranding: 1, // Minimal YouTube branding
|
|
1183
1183
|
rel: 0, // Don't show related videos
|
|
1184
1184
|
showinfo: 0, // Hide video info
|
|
1185
1185
|
autoplay: this.config.autoPlay ? 1 : 0,
|
|
1186
|
-
mute: this.config.muted ? 1 : 0
|
|
1186
|
+
mute: this.config.muted ? 1 : 0,
|
|
1187
|
+
widget_referrer: window.location.href // Hide YouTube recommendations
|
|
1187
1188
|
},
|
|
1188
1189
|
events: {
|
|
1189
1190
|
onReady: () => this.onYouTubePlayerReady(),
|
|
@@ -1257,6 +1258,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
1257
1258
|
this.state.isPlaying = true;
|
|
1258
1259
|
this.state.isPaused = false;
|
|
1259
1260
|
this.state.isBuffering = false;
|
|
1261
|
+
this.updateYouTubeUI('playing');
|
|
1260
1262
|
this.emit('onPlay');
|
|
1261
1263
|
break;
|
|
1262
1264
|
|
|
@@ -1264,11 +1266,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
1264
1266
|
this.state.isPlaying = false;
|
|
1265
1267
|
this.state.isPaused = true;
|
|
1266
1268
|
this.state.isBuffering = false;
|
|
1269
|
+
this.updateYouTubeUI('paused');
|
|
1267
1270
|
this.emit('onPause');
|
|
1268
1271
|
break;
|
|
1269
1272
|
|
|
1270
1273
|
case window.YT.PlayerState.BUFFERING:
|
|
1271
1274
|
this.state.isBuffering = true;
|
|
1275
|
+
this.updateYouTubeUI('buffering');
|
|
1272
1276
|
this.emit('onBuffering', true);
|
|
1273
1277
|
break;
|
|
1274
1278
|
|
|
@@ -1276,15 +1280,33 @@ export class WebPlayer extends BasePlayer {
|
|
|
1276
1280
|
this.state.isPlaying = false;
|
|
1277
1281
|
this.state.isPaused = true;
|
|
1278
1282
|
this.state.isEnded = true;
|
|
1283
|
+
this.updateYouTubeUI('ended');
|
|
1279
1284
|
this.emit('onEnded');
|
|
1280
1285
|
break;
|
|
1281
1286
|
|
|
1282
1287
|
case window.YT.PlayerState.CUED:
|
|
1283
1288
|
this.state.duration = this.youtubePlayer.getDuration();
|
|
1289
|
+
this.updateYouTubeUI('cued');
|
|
1284
1290
|
break;
|
|
1285
1291
|
}
|
|
1286
1292
|
}
|
|
1287
1293
|
|
|
1294
|
+
private updateYouTubeUI(state: string): void {
|
|
1295
|
+
const playIcon = document.getElementById('uvf-play-icon');
|
|
1296
|
+
const pauseIcon = document.getElementById('uvf-pause-icon');
|
|
1297
|
+
const centerPlay = document.getElementById('uvf-center-play');
|
|
1298
|
+
|
|
1299
|
+
if (state === 'playing' || state === 'buffering') {
|
|
1300
|
+
if (playIcon) playIcon.style.display = 'none';
|
|
1301
|
+
if (pauseIcon) pauseIcon.style.display = 'block';
|
|
1302
|
+
if (centerPlay) centerPlay.classList.add('hidden');
|
|
1303
|
+
} else if (state === 'paused' || state === 'cued' || state === 'ended') {
|
|
1304
|
+
if (playIcon) playIcon.style.display = 'block';
|
|
1305
|
+
if (pauseIcon) pauseIcon.style.display = 'none';
|
|
1306
|
+
if (centerPlay) centerPlay.classList.remove('hidden');
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1288
1310
|
private onYouTubePlayerError(event: any): void {
|
|
1289
1311
|
const errorCode = event.data;
|
|
1290
1312
|
let errorMessage = 'YouTube player error';
|
|
@@ -1332,6 +1354,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
1332
1354
|
this.state.duration = duration || 0;
|
|
1333
1355
|
this.state.bufferedPercentage = buffered || 0;
|
|
1334
1356
|
|
|
1357
|
+
// Update UI progress bar
|
|
1358
|
+
this.updateYouTubeProgressBar(currentTime, duration, buffered);
|
|
1359
|
+
|
|
1335
1360
|
this.emit('onTimeUpdate', this.state.currentTime);
|
|
1336
1361
|
this.emit('onProgress', this.state.bufferedPercentage);
|
|
1337
1362
|
} catch (error) {
|
|
@@ -1341,6 +1366,33 @@ export class WebPlayer extends BasePlayer {
|
|
|
1341
1366
|
}, 250); // Update every 250ms
|
|
1342
1367
|
}
|
|
1343
1368
|
|
|
1369
|
+
private updateYouTubeProgressBar(currentTime: number, duration: number, buffered: number): void {
|
|
1370
|
+
if (!duration || duration === 0) return;
|
|
1371
|
+
|
|
1372
|
+
const percent = (currentTime / duration) * 100;
|
|
1373
|
+
|
|
1374
|
+
// Update progress filled
|
|
1375
|
+
const progressFilled = document.getElementById('uvf-progress-filled') as HTMLElement;
|
|
1376
|
+
if (progressFilled && !this.isDragging) {
|
|
1377
|
+
progressFilled.style.width = percent + '%';
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// Update progress handle
|
|
1381
|
+
const progressHandle = document.getElementById('uvf-progress-handle') as HTMLElement;
|
|
1382
|
+
if (progressHandle && !this.isDragging) {
|
|
1383
|
+
progressHandle.style.left = percent + '%';
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// Update buffered progress
|
|
1387
|
+
const progressBuffered = document.getElementById('uvf-progress-buffered') as HTMLElement;
|
|
1388
|
+
if (progressBuffered) {
|
|
1389
|
+
progressBuffered.style.width = buffered + '%';
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// Update time display
|
|
1393
|
+
this.updateTimeDisplay();
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1344
1396
|
|
|
1345
1397
|
protected loadScript(src: string): Promise<void> {
|
|
1346
1398
|
return new Promise((resolve, reject) => {
|
|
@@ -6171,6 +6223,28 @@ export class WebPlayer extends BasePlayer {
|
|
|
6171
6223
|
}
|
|
6172
6224
|
}
|
|
6173
6225
|
|
|
6226
|
+
/* Hide YouTube UI elements */
|
|
6227
|
+
iframe[src*="youtube.com"] {
|
|
6228
|
+
pointer-events: auto !important;
|
|
6229
|
+
}
|
|
6230
|
+
|
|
6231
|
+
/* Hide YouTube title, logo, and controls overlay */
|
|
6232
|
+
.ytp-chrome-top,
|
|
6233
|
+
.ytp-chrome-bottom,
|
|
6234
|
+
.ytp-watermark,
|
|
6235
|
+
.ytp-gradient-top,
|
|
6236
|
+
.ytp-gradient-bottom,
|
|
6237
|
+
.ytp-show-cards-title,
|
|
6238
|
+
.ytp-cards-teaser,
|
|
6239
|
+
.ytp-endscreen-element,
|
|
6240
|
+
.ytp-ce-element,
|
|
6241
|
+
.ytp-suggested-action,
|
|
6242
|
+
.ytp-pause-overlay {
|
|
6243
|
+
display: none !important;
|
|
6244
|
+
visibility: hidden !important;
|
|
6245
|
+
opacity: 0 !important;
|
|
6246
|
+
}
|
|
6247
|
+
|
|
6174
6248
|
/* Ultra-wide screens */
|
|
6175
6249
|
@media screen and (min-width: 1440px) {
|
|
6176
6250
|
.uvf-video-title {
|
|
@@ -6750,6 +6824,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
6750
6824
|
const fullscreenBtn = document.getElementById('uvf-fullscreen-btn');
|
|
6751
6825
|
const settingsBtn = document.getElementById('uvf-settings-btn');
|
|
6752
6826
|
|
|
6827
|
+
// Get the event target (video element or YouTube player)
|
|
6828
|
+
const getEventTarget = () => this.youtubePlayer && this.youtubePlayerReady ? this.youtubePlayer : this.video;
|
|
6829
|
+
|
|
6753
6830
|
// Disable right-click context menu
|
|
6754
6831
|
this.video.addEventListener('contextmenu', (e) => {
|
|
6755
6832
|
e.preventDefault();
|
|
@@ -7291,45 +7368,61 @@ export class WebPlayer extends BasePlayer {
|
|
|
7291
7368
|
case 'ArrowLeft':
|
|
7292
7369
|
e.preventDefault();
|
|
7293
7370
|
e.stopImmediatePropagation(); // Prevent duplicate handler triggers
|
|
7294
|
-
|
|
7295
|
-
|
|
7371
|
+
const currentTime = this.getCurrentTime();
|
|
7372
|
+
const duration = this.getDuration();
|
|
7373
|
+
if (!isNaN(duration) && duration > 0) {
|
|
7374
|
+
this.seek(Math.max(0, currentTime - 10));
|
|
7296
7375
|
shortcutText = '-10s';
|
|
7297
7376
|
}
|
|
7298
7377
|
break;
|
|
7299
7378
|
case 'ArrowRight':
|
|
7300
7379
|
e.preventDefault();
|
|
7301
7380
|
e.stopImmediatePropagation(); // Prevent duplicate handler triggers
|
|
7302
|
-
|
|
7303
|
-
|
|
7381
|
+
const currentTimeRight = this.getCurrentTime();
|
|
7382
|
+
const durationRight = this.getDuration();
|
|
7383
|
+
if (!isNaN(durationRight) && durationRight > 0) {
|
|
7384
|
+
this.seek(Math.min(durationRight, currentTimeRight + 10));
|
|
7304
7385
|
shortcutText = '+10s';
|
|
7305
7386
|
}
|
|
7306
7387
|
break;
|
|
7307
7388
|
case 'ArrowUp':
|
|
7308
7389
|
e.preventDefault();
|
|
7309
7390
|
this.changeVolume(0.1);
|
|
7310
|
-
|
|
7311
|
-
|
|
7391
|
+
let volumeUp = 0;
|
|
7392
|
+
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
7393
|
+
volumeUp = this.youtubePlayer.getVolume();
|
|
7394
|
+
} else if (this.isCasting && this.remotePlayer) {
|
|
7395
|
+
volumeUp = (this.remotePlayer.volumeLevel || 0) * 100;
|
|
7312
7396
|
} else {
|
|
7313
|
-
|
|
7397
|
+
volumeUp = (this.video?.volume || 0) * 100;
|
|
7314
7398
|
}
|
|
7399
|
+
shortcutText = `Volume ${Math.round(volumeUp)}%`;
|
|
7315
7400
|
break;
|
|
7316
7401
|
case 'ArrowDown':
|
|
7317
7402
|
e.preventDefault();
|
|
7318
7403
|
this.changeVolume(-0.1);
|
|
7319
|
-
|
|
7320
|
-
|
|
7404
|
+
let volumeDown = 0;
|
|
7405
|
+
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
7406
|
+
volumeDown = this.youtubePlayer.getVolume();
|
|
7407
|
+
} else if (this.isCasting && this.remotePlayer) {
|
|
7408
|
+
volumeDown = (this.remotePlayer.volumeLevel || 0) * 100;
|
|
7321
7409
|
} else {
|
|
7322
|
-
|
|
7410
|
+
volumeDown = (this.video?.volume || 0) * 100;
|
|
7323
7411
|
}
|
|
7412
|
+
shortcutText = `Volume ${Math.round(volumeDown)}%`;
|
|
7324
7413
|
break;
|
|
7325
7414
|
case 'm':
|
|
7326
7415
|
e.preventDefault();
|
|
7327
7416
|
this.toggleMuteAction();
|
|
7328
|
-
|
|
7329
|
-
|
|
7417
|
+
let isMuted = false;
|
|
7418
|
+
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
7419
|
+
isMuted = this.youtubePlayer.isMuted();
|
|
7420
|
+
} else if (this.isCasting && this.remotePlayer) {
|
|
7421
|
+
isMuted = this.remotePlayer.isMuted;
|
|
7330
7422
|
} else {
|
|
7331
|
-
|
|
7423
|
+
isMuted = this.video?.muted || false;
|
|
7332
7424
|
}
|
|
7425
|
+
shortcutText = isMuted ? 'Muted' : 'Unmuted';
|
|
7333
7426
|
break;
|
|
7334
7427
|
case 'f':
|
|
7335
7428
|
e.preventDefault();
|
|
@@ -7368,9 +7461,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
7368
7461
|
case '9':
|
|
7369
7462
|
e.preventDefault();
|
|
7370
7463
|
// Only jump to position if video is loaded and duration is valid
|
|
7371
|
-
|
|
7464
|
+
const durationForSeek = this.getDuration();
|
|
7465
|
+
if (!isNaN(durationForSeek) && durationForSeek > 0) {
|
|
7372
7466
|
const percent = parseInt(e.key) * 10;
|
|
7373
|
-
|
|
7467
|
+
const seekTime = (durationForSeek * percent) / 100;
|
|
7468
|
+
this.seek(seekTime);
|
|
7374
7469
|
shortcutText = `${percent}%`;
|
|
7375
7470
|
}
|
|
7376
7471
|
break;
|