unified-video-framework 1.4.256 → 1.4.257
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/IVideoPlayer.d.ts +1 -0
- package/packages/core/dist/interfaces/IVideoPlayer.d.ts.map +1 -1
- package/packages/core/dist/interfaces.d.ts +1 -0
- package/packages/core/dist/interfaces.d.ts.map +1 -1
- package/packages/core/src/interfaces/IVideoPlayer.ts +3 -0
- package/packages/core/src/interfaces.ts +1 -0
- package/packages/web/dist/WebPlayer.d.ts +6 -7
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +128 -280
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/src/WebPlayer.ts +190 -392
|
@@ -94,8 +94,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
94
94
|
this.youtubePlayerReady = false;
|
|
95
95
|
this.youtubeIframe = null;
|
|
96
96
|
this.youtubeTimeTrackingInterval = null;
|
|
97
|
-
this.
|
|
98
|
-
this.
|
|
97
|
+
this.isYouTubeLive = false;
|
|
98
|
+
this.useYouTubeNativeControls = false;
|
|
99
99
|
this.clickToUnmuteHandler = null;
|
|
100
100
|
}
|
|
101
101
|
debugLog(message, ...args) {
|
|
@@ -896,7 +896,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
896
896
|
width: '100%',
|
|
897
897
|
height: '100%',
|
|
898
898
|
playerVars: {
|
|
899
|
-
controls: 0,
|
|
899
|
+
controls: this.config.youtubeNativeControls !== false ? 1 : 0,
|
|
900
900
|
disablekb: 0,
|
|
901
901
|
fs: 0,
|
|
902
902
|
iv_load_policy: 3,
|
|
@@ -953,11 +953,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
953
953
|
if (this.config.muted) {
|
|
954
954
|
this.youtubePlayer.mute();
|
|
955
955
|
}
|
|
956
|
-
this.
|
|
956
|
+
this.detectYouTubeLiveStatus();
|
|
957
957
|
this.getYouTubeVideoTitle();
|
|
958
958
|
setTimeout(() => {
|
|
959
959
|
this.updateMetadataUI();
|
|
960
|
-
this.
|
|
960
|
+
this.updateControlsVisibility();
|
|
961
961
|
}, 500);
|
|
962
962
|
}
|
|
963
963
|
this.startYouTubeTimeTracking();
|
|
@@ -971,9 +971,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
971
971
|
this.state.isPaused = false;
|
|
972
972
|
this.state.isBuffering = false;
|
|
973
973
|
this.updateYouTubeUI('playing');
|
|
974
|
-
setTimeout(() => {
|
|
975
|
-
this.extractYouTubeAvailableQualities();
|
|
976
|
-
}, 1000);
|
|
977
974
|
this.emit('onPlay');
|
|
978
975
|
break;
|
|
979
976
|
case window.YT.PlayerState.PAUSED:
|
|
@@ -986,9 +983,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
986
983
|
case window.YT.PlayerState.BUFFERING:
|
|
987
984
|
this.state.isBuffering = true;
|
|
988
985
|
this.updateYouTubeUI('buffering');
|
|
989
|
-
setTimeout(() => {
|
|
990
|
-
this.extractYouTubeAvailableQualities();
|
|
991
|
-
}, 500);
|
|
992
986
|
this.emit('onBuffering', true);
|
|
993
987
|
break;
|
|
994
988
|
case window.YT.PlayerState.ENDED:
|
|
@@ -1002,8 +996,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
1002
996
|
this.state.duration = this.youtubePlayer.getDuration();
|
|
1003
997
|
this.updateYouTubeUI('cued');
|
|
1004
998
|
setTimeout(() => {
|
|
1005
|
-
this.
|
|
1006
|
-
|
|
999
|
+
this.detectYouTubeLiveStatus();
|
|
1000
|
+
this.updateControlsVisibility();
|
|
1001
|
+
}, 500);
|
|
1007
1002
|
break;
|
|
1008
1003
|
}
|
|
1009
1004
|
}
|
|
@@ -1072,258 +1067,146 @@ export class WebPlayer extends BasePlayer {
|
|
|
1072
1067
|
this.getYouTubeVideoTitleFromOEmbed();
|
|
1073
1068
|
}
|
|
1074
1069
|
}
|
|
1075
|
-
|
|
1076
|
-
if (!this.
|
|
1070
|
+
detectYouTubeLiveStatus() {
|
|
1071
|
+
if (!this.youtubePlayer || !this.youtubePlayerReady)
|
|
1077
1072
|
return;
|
|
1078
1073
|
try {
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
this.source.metadata.title = data.title;
|
|
1088
|
-
}
|
|
1089
|
-
this.updateMetadataUI();
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1074
|
+
const videoData = this.youtubePlayer.getVideoData();
|
|
1075
|
+
this.isYouTubeLive = videoData?.isLive || false;
|
|
1076
|
+
this.useYouTubeNativeControls = this.config.youtubeNativeControls !== false;
|
|
1077
|
+
this.debugLog('YouTube Live status:', {
|
|
1078
|
+
isLive: this.isYouTubeLive,
|
|
1079
|
+
useNativeControls: this.useYouTubeNativeControls,
|
|
1080
|
+
videoId: videoData?.video_id
|
|
1081
|
+
});
|
|
1092
1082
|
}
|
|
1093
1083
|
catch (error) {
|
|
1094
|
-
this.debugWarn('Could not
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
extractYouTubeAvailableQualities(retryCount = 0) {
|
|
1098
|
-
if (!this.youtubePlayer || !this.youtubePlayerReady) {
|
|
1099
|
-
this.debugLog('YouTube player not ready for quality extraction');
|
|
1100
|
-
return;
|
|
1101
|
-
}
|
|
1102
|
-
try {
|
|
1103
|
-
const availableQualityLevels = this.youtubePlayer.getAvailableQualityLevels();
|
|
1104
|
-
this.debugLog('🔍 YouTube quality detection attempt', retryCount + 1, '- Found levels:', availableQualityLevels);
|
|
1105
|
-
this.debugLog('🔍 YouTube player state:', this.youtubePlayer.getPlayerState());
|
|
1106
|
-
this.debugLog('🔍 Current YouTube quality:', this.youtubePlayer.getPlaybackQuality());
|
|
1107
|
-
if ((!availableQualityLevels || availableQualityLevels.length === 0 || (availableQualityLevels.length === 1 && availableQualityLevels[0] === 'auto')) && retryCount < 5) {
|
|
1108
|
-
this.debugLog('No meaningful qualities detected, retrying in', (retryCount + 1) * 1000, 'ms...');
|
|
1109
|
-
setTimeout(() => {
|
|
1110
|
-
this.extractYouTubeAvailableQualities(retryCount + 1);
|
|
1111
|
-
}, (retryCount + 1) * 1000);
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
const qualityMap = {
|
|
1115
|
-
'hd2160': { label: '4K', value: 'hd2160', height: 2160 },
|
|
1116
|
-
'hd1440': { label: '1440p', value: 'hd1440', height: 1440 },
|
|
1117
|
-
'hd1080': { label: '1080p', value: 'hd1080', height: 1080 },
|
|
1118
|
-
'hd720': { label: '720p', value: 'hd720', height: 720 },
|
|
1119
|
-
'large': { label: '480p', value: 'large', height: 480 },
|
|
1120
|
-
'medium': { label: '360p', value: 'medium', height: 360 },
|
|
1121
|
-
'small': { label: '240p', value: 'small', height: 240 },
|
|
1122
|
-
'tiny': { label: '144p', value: 'tiny', height: 144 },
|
|
1123
|
-
'auto': { label: 'Auto', value: 'auto', height: 0 }
|
|
1124
|
-
};
|
|
1125
|
-
this.youtubeAvailableQualities = [];
|
|
1126
|
-
this.youtubeAvailableQualities.push({ label: 'Auto', value: 'auto', height: 0 });
|
|
1127
|
-
if (availableQualityLevels && availableQualityLevels.length > 0) {
|
|
1128
|
-
availableQualityLevels.forEach((qualityLevel) => {
|
|
1129
|
-
if (qualityLevel !== 'auto' && qualityMap[qualityLevel]) {
|
|
1130
|
-
this.youtubeAvailableQualities.push(qualityMap[qualityLevel]);
|
|
1131
|
-
}
|
|
1132
|
-
});
|
|
1133
|
-
const autoQuality = this.youtubeAvailableQualities.shift();
|
|
1134
|
-
this.youtubeAvailableQualities.sort((a, b) => (b.height || 0) - (a.height || 0));
|
|
1135
|
-
if (autoQuality)
|
|
1136
|
-
this.youtubeAvailableQualities.unshift(autoQuality);
|
|
1137
|
-
}
|
|
1138
|
-
this.debugLog('✅ Successfully mapped YouTube qualities:', this.youtubeAvailableQualities);
|
|
1084
|
+
this.debugWarn('Could not detect YouTube Live status:', error);
|
|
1139
1085
|
try {
|
|
1140
|
-
const
|
|
1141
|
-
this.
|
|
1142
|
-
this.
|
|
1086
|
+
const duration = this.youtubePlayer.getDuration();
|
|
1087
|
+
this.isYouTubeLive = !duration || duration === 0;
|
|
1088
|
+
this.useYouTubeNativeControls = this.config.youtubeNativeControls !== false;
|
|
1089
|
+
this.debugLog('YouTube Live detected via duration check:', {
|
|
1090
|
+
duration,
|
|
1091
|
+
isLive: this.isYouTubeLive,
|
|
1092
|
+
useNativeControls: this.useYouTubeNativeControls
|
|
1093
|
+
});
|
|
1143
1094
|
}
|
|
1144
1095
|
catch (e) {
|
|
1145
|
-
this.debugWarn('Could not
|
|
1146
|
-
this.youtubeCurrentQuality = qualityMap['auto'];
|
|
1147
|
-
}
|
|
1148
|
-
this.detectAvailableQualities();
|
|
1149
|
-
this.debugLog('🚀 YouTube qualities successfully detected! Available qualities now:', this.availableQualities.length);
|
|
1150
|
-
this.updateQualityBadge();
|
|
1151
|
-
setTimeout(() => {
|
|
1152
|
-
this.debugLog('🔄 Force refreshing settings menu with YouTube qualities');
|
|
1153
|
-
this.updateSettingsMenu();
|
|
1154
|
-
}, 100);
|
|
1155
|
-
}
|
|
1156
|
-
catch (error) {
|
|
1157
|
-
this.debugWarn('Could not extract YouTube qualities:', error);
|
|
1158
|
-
if (retryCount < 3) {
|
|
1159
|
-
setTimeout(() => {
|
|
1160
|
-
this.extractYouTubeAvailableQualities(retryCount + 1);
|
|
1161
|
-
}, 2000);
|
|
1096
|
+
this.debugWarn('Could not check YouTube duration for Live detection:', e);
|
|
1162
1097
|
}
|
|
1163
1098
|
}
|
|
1164
1099
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1100
|
+
updateControlsVisibility() {
|
|
1101
|
+
const controlsContainer = document.getElementById('uvf-controls-container');
|
|
1102
|
+
if (!controlsContainer)
|
|
1167
1103
|
return;
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
'tiny': { label: '144p', value: 'tiny', height: 144 },
|
|
1178
|
-
'auto': { label: 'Auto', value: 'auto', height: 0 }
|
|
1179
|
-
};
|
|
1180
|
-
this.debugLog('📊 Attempting to set YouTube quality to:', qualityLevel);
|
|
1181
|
-
if (qualityLevel === 'auto') {
|
|
1182
|
-
this.youtubePlayer.setPlaybackQuality(qualityLevel);
|
|
1183
|
-
}
|
|
1184
|
-
else {
|
|
1185
|
-
try {
|
|
1186
|
-
if (typeof this.youtubePlayer.setPlaybackQualityRange === 'function') {
|
|
1187
|
-
this.youtubePlayer.setPlaybackQualityRange(qualityLevel, qualityLevel);
|
|
1188
|
-
this.debugLog('✅ setPlaybackQualityRange called for:', qualityLevel);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
catch (e) {
|
|
1192
|
-
this.debugWarn('setPlaybackQualityRange failed:', e);
|
|
1193
|
-
}
|
|
1194
|
-
try {
|
|
1195
|
-
this.youtubePlayer.setPlaybackQuality(qualityLevel);
|
|
1196
|
-
this.debugLog('✅ setPlaybackQuality called for:', qualityLevel);
|
|
1197
|
-
}
|
|
1198
|
-
catch (e) {
|
|
1199
|
-
this.debugWarn('setPlaybackQuality failed:', e);
|
|
1200
|
-
}
|
|
1201
|
-
try {
|
|
1202
|
-
const currentTime = this.youtubePlayer.getCurrentTime();
|
|
1203
|
-
const wasPlaying = this.youtubePlayer.getPlayerState() === window.YT.PlayerState.PLAYING;
|
|
1204
|
-
this.youtubePlayer.seekTo(currentTime + 0.1, true);
|
|
1205
|
-
setTimeout(() => {
|
|
1206
|
-
this.youtubePlayer.setPlaybackQuality(qualityLevel);
|
|
1207
|
-
setTimeout(() => {
|
|
1208
|
-
this.youtubePlayer.seekTo(currentTime, true);
|
|
1209
|
-
if (!wasPlaying) {
|
|
1210
|
-
this.youtubePlayer.pauseVideo();
|
|
1211
|
-
}
|
|
1212
|
-
}, 100);
|
|
1213
|
-
}, 50);
|
|
1214
|
-
this.debugLog('🔄 Applied seekTo trick for quality change');
|
|
1215
|
-
}
|
|
1216
|
-
catch (e) {
|
|
1217
|
-
this.debugWarn('Force quality change with seekTo trick failed:', e);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
this.verifyYouTubeQualityChange(qualityLevel, qualityMap, 0);
|
|
1104
|
+
if (this.youtubePlayer && this.useYouTubeNativeControls) {
|
|
1105
|
+
controlsContainer.style.display = 'none';
|
|
1106
|
+
if (!this.config.youtubeNativeControls) {
|
|
1107
|
+
this.recreateYouTubePlayerWithNativeControls();
|
|
1108
|
+
}
|
|
1109
|
+
this.debugLog('✅ YouTube native controls enabled', {
|
|
1110
|
+
isLive: this.isYouTubeLive,
|
|
1111
|
+
reason: this.config.youtubeNativeControls !== false ? 'Default behavior (all YouTube content)' : 'Live stream detected'
|
|
1112
|
+
});
|
|
1221
1113
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
this.
|
|
1114
|
+
else {
|
|
1115
|
+
controlsContainer.style.display = 'flex';
|
|
1116
|
+
this.debugLog('✅ Custom controls enabled for YouTube video');
|
|
1225
1117
|
}
|
|
1226
1118
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
this.showNotification(`Quality: ${qualityMap[currentQuality]?.label || 'requested quality'}`);
|
|
1238
|
-
this.debugLog('✅ Quality successfully changed to:', requestedQuality);
|
|
1239
|
-
}
|
|
1240
|
-
else {
|
|
1241
|
-
if (attempt === maxAttempts - 1) {
|
|
1242
|
-
this.debugLog('🔥 Final attempt: Trying loadVideoById method');
|
|
1243
|
-
try {
|
|
1244
|
-
const videoData = this.youtubePlayer.getVideoData();
|
|
1245
|
-
const currentTime = this.youtubePlayer.getCurrentTime();
|
|
1246
|
-
this.youtubePlayer.loadVideoById({
|
|
1247
|
-
videoId: videoData.video_id,
|
|
1248
|
-
startSeconds: currentTime,
|
|
1249
|
-
suggestedQuality: requestedQuality
|
|
1250
|
-
});
|
|
1251
|
-
this.debugLog('🔄 Video reloaded with suggested quality:', requestedQuality);
|
|
1252
|
-
}
|
|
1253
|
-
catch (e) {
|
|
1254
|
-
this.debugWarn('loadVideoById method failed:', e);
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
this.showNotification(`Quality: ${qualityMap[currentQuality]?.label || currentQuality}`);
|
|
1258
|
-
this.debugLog('⚠️ Quality change may not have worked. Requested:', requestedQuality, '| Current:', currentQuality);
|
|
1259
|
-
}
|
|
1260
|
-
this.updateQualityBadge();
|
|
1261
|
-
this.updateSettingsMenu();
|
|
1262
|
-
}
|
|
1263
|
-
else {
|
|
1264
|
-
this.debugLog('🔁 Quality not changed, retrying with method', attempt + 2);
|
|
1265
|
-
this.retryYouTubeQualityChange(requestedQuality, qualityMap, attempt + 1);
|
|
1266
|
-
this.verifyYouTubeQualityChange(requestedQuality, qualityMap, attempt + 1);
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
catch (verifyError) {
|
|
1270
|
-
this.debugWarn('Could not verify YouTube quality:', verifyError);
|
|
1271
|
-
if (attempt === maxAttempts - 1) {
|
|
1272
|
-
this.showNotification('Quality change attempted');
|
|
1273
|
-
this.updateQualityBadge();
|
|
1274
|
-
this.updateSettingsMenu();
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
}, delays[attempt] || 1000);
|
|
1278
|
-
}
|
|
1279
|
-
retryYouTubeQualityChange(qualityLevel, qualityMap, attempt) {
|
|
1280
|
-
try {
|
|
1281
|
-
if (attempt === 1) {
|
|
1282
|
-
this.debugLog('🔄 Retry attempt 1: Using cueVideoById');
|
|
1283
|
-
const videoData = this.youtubePlayer.getVideoData();
|
|
1284
|
-
const currentTime = this.youtubePlayer.getCurrentTime();
|
|
1285
|
-
this.youtubePlayer.cueVideoById({
|
|
1286
|
-
videoId: videoData.video_id,
|
|
1287
|
-
startSeconds: currentTime,
|
|
1288
|
-
suggestedQuality: qualityLevel
|
|
1289
|
-
});
|
|
1290
|
-
setTimeout(() => this.youtubePlayer.playVideo(), 100);
|
|
1291
|
-
}
|
|
1292
|
-
else if (attempt === 2) {
|
|
1293
|
-
this.debugLog('🔄 Retry attempt 2: Multiple sequential calls');
|
|
1294
|
-
for (let i = 0; i < 3; i++) {
|
|
1295
|
-
setTimeout(() => {
|
|
1296
|
-
this.youtubePlayer.setPlaybackQuality(qualityLevel);
|
|
1297
|
-
}, i * 100);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1119
|
+
recreateYouTubePlayerWithNativeControls() {
|
|
1120
|
+
if (!this.source?.metadata?.videoId)
|
|
1121
|
+
return;
|
|
1122
|
+
const videoId = this.source.metadata.videoId;
|
|
1123
|
+
const currentTime = this.youtubePlayer?.getCurrentTime() || 0;
|
|
1124
|
+
const container = this.playerWrapper || this.video?.parentElement;
|
|
1125
|
+
if (!container)
|
|
1126
|
+
return;
|
|
1127
|
+
if (this.youtubePlayer) {
|
|
1128
|
+
this.youtubePlayer.destroy();
|
|
1300
1129
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1130
|
+
const existingContainer = container.querySelector(`#youtube-player-${videoId}`);
|
|
1131
|
+
if (existingContainer) {
|
|
1132
|
+
existingContainer.remove();
|
|
1303
1133
|
}
|
|
1134
|
+
const iframeContainer = document.createElement('div');
|
|
1135
|
+
iframeContainer.id = `youtube-player-${videoId}`;
|
|
1136
|
+
iframeContainer.style.cssText = `
|
|
1137
|
+
position: absolute;
|
|
1138
|
+
top: 0;
|
|
1139
|
+
left: 0;
|
|
1140
|
+
width: 100%;
|
|
1141
|
+
height: 100%;
|
|
1142
|
+
z-index: 1;
|
|
1143
|
+
`;
|
|
1144
|
+
container.appendChild(iframeContainer);
|
|
1145
|
+
this.youtubePlayer = new window.YT.Player(iframeContainer.id, {
|
|
1146
|
+
videoId: videoId,
|
|
1147
|
+
width: '100%',
|
|
1148
|
+
height: '100%',
|
|
1149
|
+
playerVars: {
|
|
1150
|
+
autoplay: this.config.autoPlay ? 1 : 0,
|
|
1151
|
+
controls: 1,
|
|
1152
|
+
modestbranding: 1,
|
|
1153
|
+
rel: 0,
|
|
1154
|
+
showinfo: 0,
|
|
1155
|
+
iv_load_policy: 3,
|
|
1156
|
+
playsinline: 1,
|
|
1157
|
+
start: Math.floor(currentTime)
|
|
1158
|
+
},
|
|
1159
|
+
events: {
|
|
1160
|
+
onReady: () => {
|
|
1161
|
+
this.youtubePlayerReady = true;
|
|
1162
|
+
this.debugLog('YouTube player with native controls ready');
|
|
1163
|
+
this.emit('onReady');
|
|
1164
|
+
},
|
|
1165
|
+
onStateChange: (event) => this.onYouTubePlayerStateChange(event),
|
|
1166
|
+
onError: (event) => this.onYouTubePlayerError(event)
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1304
1169
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1170
|
+
toggleYouTubeControls(useNative = !this.useYouTubeNativeControls) {
|
|
1171
|
+
if (!this.youtubePlayer) {
|
|
1172
|
+
this.debugWarn('Cannot toggle YouTube controls - no YouTube player active');
|
|
1308
1173
|
return;
|
|
1309
|
-
if (this.youtubePlayer && this.youtubePlayerReady && this.youtubeCurrentQuality) {
|
|
1310
|
-
qualityBadge.textContent = this.youtubeCurrentQuality.label || 'AUTO';
|
|
1311
|
-
this.debugLog('Updated quality badge for YouTube:', this.youtubeCurrentQuality.label);
|
|
1312
1174
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1175
|
+
this.useYouTubeNativeControls = useNative;
|
|
1176
|
+
this.config.youtubeNativeControls = useNative;
|
|
1177
|
+
this.updateControlsVisibility();
|
|
1178
|
+
this.debugLog('YouTube controls toggled:', {
|
|
1179
|
+
useNative: this.useYouTubeNativeControls,
|
|
1180
|
+
isLive: this.isYouTubeLive
|
|
1181
|
+
});
|
|
1182
|
+
this.showNotification(`YouTube Controls: ${this.useYouTubeNativeControls ? 'Native' : 'Custom'}`);
|
|
1183
|
+
}
|
|
1184
|
+
async getYouTubeVideoTitleFromOEmbed() {
|
|
1185
|
+
if (!this.source?.metadata?.videoId)
|
|
1186
|
+
return;
|
|
1187
|
+
try {
|
|
1188
|
+
const videoId = this.source.metadata.videoId;
|
|
1189
|
+
const oembedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`;
|
|
1190
|
+
const response = await fetch(oembedUrl);
|
|
1191
|
+
if (response.ok) {
|
|
1192
|
+
const data = await response.json();
|
|
1193
|
+
if (data.title) {
|
|
1194
|
+
this.debugLog('Got YouTube title from oembed API:', data.title);
|
|
1195
|
+
if (this.source && this.source.metadata) {
|
|
1196
|
+
this.source.metadata.title = data.title;
|
|
1197
|
+
}
|
|
1198
|
+
this.updateMetadataUI();
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1317
1201
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1202
|
+
catch (error) {
|
|
1203
|
+
this.debugWarn('Could not get YouTube title from oembed API:', error);
|
|
1320
1204
|
}
|
|
1321
1205
|
}
|
|
1322
1206
|
startYouTubeTimeTracking() {
|
|
1323
1207
|
if (this.youtubeTimeTrackingInterval) {
|
|
1324
1208
|
clearInterval(this.youtubeTimeTrackingInterval);
|
|
1325
1209
|
}
|
|
1326
|
-
let qualityCheckCounter = 0;
|
|
1327
1210
|
this.youtubeTimeTrackingInterval = setInterval(() => {
|
|
1328
1211
|
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
1329
1212
|
try {
|
|
@@ -1333,12 +1216,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
1333
1216
|
this.state.currentTime = currentTime || 0;
|
|
1334
1217
|
this.state.duration = duration || 0;
|
|
1335
1218
|
this.state.bufferedPercentage = buffered || 0;
|
|
1336
|
-
qualityCheckCounter++;
|
|
1337
|
-
if (qualityCheckCounter >= 40 && this.youtubeAvailableQualities.length <= 1) {
|
|
1338
|
-
this.debugLog('Periodic YouTube quality check triggered');
|
|
1339
|
-
this.extractYouTubeAvailableQualities();
|
|
1340
|
-
qualityCheckCounter = 0;
|
|
1341
|
-
}
|
|
1342
1219
|
this.updateYouTubeProgressBar(currentTime, duration, buffered);
|
|
1343
1220
|
this.emit('onTimeUpdate', this.state.currentTime);
|
|
1344
1221
|
this.emit('onProgress', this.state.bufferedPercentage);
|
|
@@ -8200,18 +8077,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
8200
8077
|
}
|
|
8201
8078
|
setQualityByLabel(quality) {
|
|
8202
8079
|
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
8203
|
-
this.
|
|
8080
|
+
this.showShortcutIndicator('Quality control not available for YouTube');
|
|
8204
8081
|
document.querySelectorAll('.quality-option').forEach(option => {
|
|
8205
8082
|
option.classList.remove('active');
|
|
8206
|
-
if (option.dataset.quality ===
|
|
8083
|
+
if (option.dataset.quality === 'auto') {
|
|
8207
8084
|
option.classList.add('active');
|
|
8208
8085
|
}
|
|
8209
8086
|
});
|
|
8210
|
-
const qualityBadge = document.getElementById('uvf-quality-badge');
|
|
8211
|
-
if (qualityBadge) {
|
|
8212
|
-
const qualityOption = this.youtubeAvailableQualities.find(q => q.value === quality);
|
|
8213
|
-
qualityBadge.textContent = qualityOption ? qualityOption.label : 'AUTO';
|
|
8214
|
-
}
|
|
8215
8087
|
return;
|
|
8216
8088
|
}
|
|
8217
8089
|
const qualityBadge = document.getElementById('uvf-quality-badge');
|
|
@@ -8512,13 +8384,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
8512
8384
|
</div>`;
|
|
8513
8385
|
}
|
|
8514
8386
|
if (this.settingsConfig.quality && this.availableQualities.length > 0) {
|
|
8515
|
-
|
|
8516
|
-
const qualityForDisplay = this.youtubePlayer && this.youtubePlayerReady && this.youtubeCurrentQuality
|
|
8517
|
-
? this.youtubeCurrentQuality.value
|
|
8518
|
-
: this.currentQuality;
|
|
8519
|
-
const currentQuality = this.availableQualities.find(q => q.value === qualityForDisplay);
|
|
8387
|
+
const currentQuality = this.availableQualities.find(q => q.value === this.currentQuality);
|
|
8520
8388
|
const currentQualityLabel = currentQuality ? currentQuality.label : 'Auto';
|
|
8521
|
-
this.debugLog('🎛️ Current quality for display:', qualityForDisplay, '(', currentQualityLabel, ')');
|
|
8522
8389
|
menuHTML += `
|
|
8523
8390
|
<div class="uvf-accordion-item">
|
|
8524
8391
|
<div class="uvf-accordion-header" data-section="quality">
|
|
@@ -8535,10 +8402,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
8535
8402
|
</div>
|
|
8536
8403
|
<div class="uvf-accordion-content" data-section="quality">`;
|
|
8537
8404
|
this.availableQualities.forEach(quality => {
|
|
8538
|
-
const
|
|
8539
|
-
? this.youtubeCurrentQuality.value
|
|
8540
|
-
: this.currentQuality;
|
|
8541
|
-
const isActive = quality.value === qualityValue ? 'active' : '';
|
|
8405
|
+
const isActive = quality.value === this.currentQuality ? 'active' : '';
|
|
8542
8406
|
const isPremium = this.isQualityPremium(quality);
|
|
8543
8407
|
const isLocked = isPremium && !this.isPremiumUser();
|
|
8544
8408
|
const qualityHeight = quality.height || 0;
|
|
@@ -8588,20 +8452,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
8588
8452
|
this.debugLog('Settings event listeners setup complete');
|
|
8589
8453
|
}
|
|
8590
8454
|
detectAvailableQualities() {
|
|
8591
|
-
|
|
8592
|
-
this.availableQualities = [];
|
|
8455
|
+
this.availableQualities = [{ value: 'auto', label: 'Auto' }];
|
|
8593
8456
|
let detectedQualities = [];
|
|
8594
|
-
if (
|
|
8595
|
-
detectedQualities = this.youtubeAvailableQualities.map(q => ({
|
|
8596
|
-
value: q.value,
|
|
8597
|
-
label: q.label,
|
|
8598
|
-
height: q.height
|
|
8599
|
-
}));
|
|
8600
|
-
this.debugLog('Using YouTube qualities:', detectedQualities);
|
|
8601
|
-
this.availableQualities = detectedQualities;
|
|
8602
|
-
return;
|
|
8603
|
-
}
|
|
8604
|
-
else if (this.hls && this.hls.levels) {
|
|
8457
|
+
if (this.hls && this.hls.levels) {
|
|
8605
8458
|
this.hls.levels.forEach((level, index) => {
|
|
8606
8459
|
if (level.height) {
|
|
8607
8460
|
detectedQualities.push({
|
|
@@ -8846,11 +8699,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
8846
8699
|
}
|
|
8847
8700
|
setQualityFromSettings(quality) {
|
|
8848
8701
|
this.currentQuality = quality;
|
|
8849
|
-
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
8850
|
-
this.setYouTubeQuality(quality);
|
|
8851
|
-
this.debugLog(`YouTube quality set to ${quality}`);
|
|
8852
|
-
return;
|
|
8853
|
-
}
|
|
8854
8702
|
if (quality === 'auto') {
|
|
8855
8703
|
if (this.hls) {
|
|
8856
8704
|
if (this.qualityFilter) {
|