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: 1, // Disable keyboard controls
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
- if (this.video && !isNaN(this.video.duration)) {
7295
- this.seek(Math.max(0, this.video.currentTime - 10));
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
- if (this.video && !isNaN(this.video.duration)) {
7303
- this.seek(Math.min(this.video.duration, this.video.currentTime + 10));
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
- if (this.isCasting && this.remotePlayer) {
7311
- shortcutText = `Volume ${Math.round(((this.remotePlayer.volumeLevel || 0) * 100))}%`;
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
- shortcutText = `Volume ${Math.round((this.video?.volume || 0) * 100)}%`;
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
- if (this.isCasting && this.remotePlayer) {
7320
- shortcutText = `Volume ${Math.round(((this.remotePlayer.volumeLevel || 0) * 100))}%`;
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
- shortcutText = `Volume ${Math.round((this.video?.volume || 0) * 100)}%`;
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
- if (this.isCasting && this.remotePlayer) {
7329
- shortcutText = this.remotePlayer.isMuted ? 'Muted' : 'Unmuted';
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
- shortcutText = this.video?.muted ? 'Muted' : 'Unmuted';
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
- if (this.video && !isNaN(this.video.duration) && this.video.duration > 0) {
7464
+ const durationForSeek = this.getDuration();
7465
+ if (!isNaN(durationForSeek) && durationForSeek > 0) {
7372
7466
  const percent = parseInt(e.key) * 10;
7373
- this.video.currentTime = (this.video.duration * percent) / 100;
7467
+ const seekTime = (durationForSeek * percent) / 100;
7468
+ this.seek(seekTime);
7374
7469
  shortcutText = `${percent}%`;
7375
7470
  }
7376
7471
  break;