unified-video-framework 1.4.251 → 1.4.252
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.
|
@@ -1255,6 +1255,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
1255
1255
|
// Extract available YouTube qualities
|
|
1256
1256
|
this.extractYouTubeAvailableQualities();
|
|
1257
1257
|
|
|
1258
|
+
// Try to get video title from YouTube API
|
|
1259
|
+
this.getYouTubeVideoTitle();
|
|
1260
|
+
|
|
1258
1261
|
// Update metadata UI and settings after player is ready
|
|
1259
1262
|
setTimeout(() => {
|
|
1260
1263
|
this.updateMetadataUI();
|
|
@@ -1305,6 +1308,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
1305
1308
|
case window.YT.PlayerState.CUED:
|
|
1306
1309
|
this.state.duration = this.youtubePlayer.getDuration();
|
|
1307
1310
|
this.updateYouTubeUI('cued');
|
|
1311
|
+
|
|
1312
|
+
// Try to get video title when video is cued
|
|
1313
|
+
setTimeout(() => {
|
|
1314
|
+
this.getYouTubeVideoTitle();
|
|
1315
|
+
}, 100);
|
|
1308
1316
|
break;
|
|
1309
1317
|
}
|
|
1310
1318
|
}
|
|
@@ -1354,6 +1362,64 @@ export class WebPlayer extends BasePlayer {
|
|
|
1354
1362
|
});
|
|
1355
1363
|
}
|
|
1356
1364
|
|
|
1365
|
+
/**
|
|
1366
|
+
* Get YouTube video title from the player API
|
|
1367
|
+
*/
|
|
1368
|
+
private getYouTubeVideoTitle(): void {
|
|
1369
|
+
if (!this.youtubePlayer || !this.youtubePlayerReady) return;
|
|
1370
|
+
|
|
1371
|
+
try {
|
|
1372
|
+
// Try to get video data from YouTube player
|
|
1373
|
+
const videoData = this.youtubePlayer.getVideoData();
|
|
1374
|
+
if (videoData && videoData.title) {
|
|
1375
|
+
this.debugLog('Got YouTube title from player API:', videoData.title);
|
|
1376
|
+
|
|
1377
|
+
// Update source metadata with the correct title
|
|
1378
|
+
if (this.source && this.source.metadata) {
|
|
1379
|
+
this.source.metadata.title = videoData.title;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// Update UI immediately
|
|
1383
|
+
this.updateMetadataUI();
|
|
1384
|
+
}
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
this.debugWarn('Could not get YouTube video title from API:', error);
|
|
1387
|
+
|
|
1388
|
+
// Fallback: Try to get from oembed API
|
|
1389
|
+
this.getYouTubeVideoTitleFromOEmbed();
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* Fallback method to get title from YouTube oembed API
|
|
1395
|
+
*/
|
|
1396
|
+
private async getYouTubeVideoTitleFromOEmbed(): Promise<void> {
|
|
1397
|
+
if (!this.source?.metadata?.videoId) return;
|
|
1398
|
+
|
|
1399
|
+
try {
|
|
1400
|
+
const videoId = this.source.metadata.videoId;
|
|
1401
|
+
const oembedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`;
|
|
1402
|
+
|
|
1403
|
+
const response = await fetch(oembedUrl);
|
|
1404
|
+
if (response.ok) {
|
|
1405
|
+
const data = await response.json();
|
|
1406
|
+
if (data.title) {
|
|
1407
|
+
this.debugLog('Got YouTube title from oembed API:', data.title);
|
|
1408
|
+
|
|
1409
|
+
// Update source metadata
|
|
1410
|
+
if (this.source && this.source.metadata) {
|
|
1411
|
+
this.source.metadata.title = data.title;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// Update UI
|
|
1415
|
+
this.updateMetadataUI();
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
this.debugWarn('Could not get YouTube title from oembed API:', error);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1357
1423
|
private youtubeTimeTrackingInterval: NodeJS.Timeout | null = null;
|
|
1358
1424
|
private youtubeAvailableQualities: any[] = [];
|
|
1359
1425
|
private youtubeCurrentQuality: any = null;
|
|
@@ -1403,6 +1469,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
1403
1469
|
this.youtubeCurrentQuality = qualityMap['auto'];
|
|
1404
1470
|
}
|
|
1405
1471
|
|
|
1472
|
+
// Update quality badge with current YouTube quality
|
|
1473
|
+
this.updateQualityBadge();
|
|
1474
|
+
|
|
1406
1475
|
// Update settings menu to show YouTube qualities
|
|
1407
1476
|
this.updateSettingsMenu();
|
|
1408
1477
|
|
|
@@ -1451,6 +1520,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
1451
1520
|
this.debugLog('⚠️ Requested quality:', qualityLevel, '| Actual quality:', currentQuality, '(YouTube may have optimized)');
|
|
1452
1521
|
}
|
|
1453
1522
|
|
|
1523
|
+
// Update quality badge
|
|
1524
|
+
this.updateQualityBadge();
|
|
1525
|
+
|
|
1454
1526
|
// Update settings menu UI
|
|
1455
1527
|
this.updateSettingsMenu();
|
|
1456
1528
|
} catch (verifyError) {
|
|
@@ -1463,6 +1535,28 @@ export class WebPlayer extends BasePlayer {
|
|
|
1463
1535
|
this.showNotification('Quality control limited on YouTube');
|
|
1464
1536
|
}
|
|
1465
1537
|
}
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* Update quality badge with current quality
|
|
1541
|
+
*/
|
|
1542
|
+
private updateQualityBadge(): void {
|
|
1543
|
+
const qualityBadge = document.getElementById('uvf-quality-badge');
|
|
1544
|
+
if (!qualityBadge) return;
|
|
1545
|
+
|
|
1546
|
+
// For YouTube, use YouTube current quality
|
|
1547
|
+
if (this.youtubePlayer && this.youtubePlayerReady && this.youtubeCurrentQuality) {
|
|
1548
|
+
qualityBadge.textContent = this.youtubeCurrentQuality.label || 'AUTO';
|
|
1549
|
+
this.debugLog('Updated quality badge for YouTube:', this.youtubeCurrentQuality.label);
|
|
1550
|
+
} else if (this.currentQualityIndex >= 0 && this.qualities[this.currentQualityIndex]) {
|
|
1551
|
+
// For other sources, use current quality
|
|
1552
|
+
const quality = this.qualities[this.currentQualityIndex];
|
|
1553
|
+
qualityBadge.textContent = quality.label || 'HD';
|
|
1554
|
+
this.debugLog('Updated quality badge for standard video:', quality.label);
|
|
1555
|
+
} else {
|
|
1556
|
+
// Default
|
|
1557
|
+
qualityBadge.textContent = 'AUTO';
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1466
1560
|
|
|
1467
1561
|
private startYouTubeTimeTracking(): void {
|
|
1468
1562
|
if (this.youtubeTimeTrackingInterval) {
|
|
@@ -1968,12 +2062,27 @@ export class WebPlayer extends BasePlayer {
|
|
|
1968
2062
|
private updateTimeTooltip(e: MouseEvent): void {
|
|
1969
2063
|
const progressBar = document.getElementById('uvf-progress-bar');
|
|
1970
2064
|
const tooltip = document.getElementById('uvf-time-tooltip');
|
|
1971
|
-
if (!progressBar || !tooltip
|
|
2065
|
+
if (!progressBar || !tooltip) return;
|
|
2066
|
+
|
|
2067
|
+
// Get duration from appropriate source
|
|
2068
|
+
let duration = 0;
|
|
2069
|
+
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
2070
|
+
try {
|
|
2071
|
+
duration = this.youtubePlayer.getDuration() || 0;
|
|
2072
|
+
} catch (error) {
|
|
2073
|
+
this.debugWarn('Error getting YouTube duration for tooltip:', error);
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
} else if (this.video) {
|
|
2077
|
+
duration = this.video.duration || 0;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
if (!duration || !isFinite(duration)) return;
|
|
1972
2081
|
|
|
1973
2082
|
const rect = progressBar.getBoundingClientRect();
|
|
1974
2083
|
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
|
|
1975
2084
|
const percent = (x / rect.width);
|
|
1976
|
-
const time = percent *
|
|
2085
|
+
const time = percent * duration;
|
|
1977
2086
|
|
|
1978
2087
|
// Update tooltip content and position
|
|
1979
2088
|
tooltip.textContent = this.formatTime(time);
|
|
@@ -6789,7 +6898,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
6789
6898
|
const qualityBadge = document.createElement('div');
|
|
6790
6899
|
qualityBadge.className = 'uvf-quality-badge';
|
|
6791
6900
|
qualityBadge.id = 'uvf-quality-badge';
|
|
6792
|
-
qualityBadge.textContent = '
|
|
6901
|
+
qualityBadge.textContent = 'AUTO'; // Default to AUTO for better UX
|
|
6793
6902
|
rightControls.appendChild(qualityBadge);
|
|
6794
6903
|
|
|
6795
6904
|
// Settings button with menu (show only if enabled)
|