unified-video-framework 1.4.256 → 1.4.258
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 +7 -7
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +181 -277
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/src/WebPlayer.ts +259 -388
|
@@ -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 === true ? 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,202 @@ 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 === true;
|
|
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 === true;
|
|
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'];
|
|
1096
|
+
this.debugWarn('Could not check YouTube duration for Live detection:', e);
|
|
1147
1097
|
}
|
|
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
1098
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1099
|
+
}
|
|
1100
|
+
updateControlsVisibility() {
|
|
1101
|
+
const controlsContainer = document.getElementById('uvf-controls-container');
|
|
1102
|
+
if (!controlsContainer)
|
|
1103
|
+
return;
|
|
1104
|
+
if (this.youtubePlayer && this.useYouTubeNativeControls) {
|
|
1105
|
+
controlsContainer.style.display = 'none';
|
|
1106
|
+
if (this.config.youtubeNativeControls === false) {
|
|
1107
|
+
this.recreateYouTubePlayerWithNativeControls();
|
|
1108
|
+
}
|
|
1109
|
+
this.debugLog('✅ YouTube native controls enabled', {
|
|
1110
|
+
isLive: this.isYouTubeLive,
|
|
1111
|
+
reason: this.config.youtubeNativeControls === true ? 'Explicitly enabled in config' : 'Live stream detected'
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
controlsContainer.style.display = 'flex';
|
|
1116
|
+
if (this.config.youtubeNativeControls === true) {
|
|
1117
|
+
this.recreateYouTubePlayerWithoutNativeControls();
|
|
1162
1118
|
}
|
|
1119
|
+
this.debugLog('✅ Custom controls enabled for YouTube video');
|
|
1163
1120
|
}
|
|
1164
1121
|
}
|
|
1165
|
-
|
|
1166
|
-
if (!this.
|
|
1122
|
+
recreateYouTubePlayerWithNativeControls() {
|
|
1123
|
+
if (!this.source?.metadata?.videoId)
|
|
1167
1124
|
return;
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
'medium': { label: '360p', value: 'medium', height: 360 },
|
|
1176
|
-
'small': { label: '240p', value: 'small', height: 240 },
|
|
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);
|
|
1125
|
+
const videoId = this.source.metadata.videoId;
|
|
1126
|
+
const currentTime = this.youtubePlayer?.getCurrentTime() || 0;
|
|
1127
|
+
const container = this.playerWrapper || this.video?.parentElement;
|
|
1128
|
+
if (!container)
|
|
1129
|
+
return;
|
|
1130
|
+
if (this.youtubePlayer) {
|
|
1131
|
+
this.youtubePlayer.destroy();
|
|
1221
1132
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1133
|
+
const existingContainer = container.querySelector(`#youtube-player-${videoId}`);
|
|
1134
|
+
if (existingContainer) {
|
|
1135
|
+
existingContainer.remove();
|
|
1225
1136
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
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
|
-
}
|
|
1137
|
+
const iframeContainer = document.createElement('div');
|
|
1138
|
+
iframeContainer.id = `youtube-player-${videoId}`;
|
|
1139
|
+
iframeContainer.style.cssText = `
|
|
1140
|
+
position: absolute;
|
|
1141
|
+
top: 0;
|
|
1142
|
+
left: 0;
|
|
1143
|
+
width: 100%;
|
|
1144
|
+
height: 100%;
|
|
1145
|
+
z-index: 1;
|
|
1146
|
+
`;
|
|
1147
|
+
container.appendChild(iframeContainer);
|
|
1148
|
+
this.youtubePlayer = new window.YT.Player(iframeContainer.id, {
|
|
1149
|
+
videoId: videoId,
|
|
1150
|
+
width: '100%',
|
|
1151
|
+
height: '100%',
|
|
1152
|
+
playerVars: {
|
|
1153
|
+
autoplay: this.config.autoPlay ? 1 : 0,
|
|
1154
|
+
controls: 1,
|
|
1155
|
+
modestbranding: 1,
|
|
1156
|
+
rel: 0,
|
|
1157
|
+
showinfo: 0,
|
|
1158
|
+
iv_load_policy: 3,
|
|
1159
|
+
playsinline: 1,
|
|
1160
|
+
start: Math.floor(currentTime)
|
|
1161
|
+
},
|
|
1162
|
+
events: {
|
|
1163
|
+
onReady: () => {
|
|
1164
|
+
this.youtubePlayerReady = true;
|
|
1165
|
+
this.debugLog('YouTube player with native controls ready');
|
|
1166
|
+
this.emit('onReady');
|
|
1167
|
+
},
|
|
1168
|
+
onStateChange: (event) => this.onYouTubePlayerStateChange(event),
|
|
1169
|
+
onError: (event) => this.onYouTubePlayerError(event)
|
|
1276
1170
|
}
|
|
1277
|
-
}
|
|
1171
|
+
});
|
|
1278
1172
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
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
|
-
}
|
|
1173
|
+
recreateYouTubePlayerWithoutNativeControls() {
|
|
1174
|
+
if (!this.source?.metadata?.videoId)
|
|
1175
|
+
return;
|
|
1176
|
+
const videoId = this.source.metadata.videoId;
|
|
1177
|
+
const currentTime = this.youtubePlayer?.getCurrentTime() || 0;
|
|
1178
|
+
const container = this.playerWrapper || this.video?.parentElement;
|
|
1179
|
+
if (!container)
|
|
1180
|
+
return;
|
|
1181
|
+
if (this.youtubePlayer) {
|
|
1182
|
+
this.youtubePlayer.destroy();
|
|
1300
1183
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1184
|
+
const existingContainer = container.querySelector(`#youtube-player-${videoId}`);
|
|
1185
|
+
if (existingContainer) {
|
|
1186
|
+
existingContainer.remove();
|
|
1303
1187
|
}
|
|
1188
|
+
const iframeContainer = document.createElement('div');
|
|
1189
|
+
iframeContainer.id = `youtube-player-${videoId}`;
|
|
1190
|
+
iframeContainer.style.cssText = `
|
|
1191
|
+
position: absolute;
|
|
1192
|
+
top: 0;
|
|
1193
|
+
left: 0;
|
|
1194
|
+
width: 100%;
|
|
1195
|
+
height: 100%;
|
|
1196
|
+
z-index: 1;
|
|
1197
|
+
`;
|
|
1198
|
+
container.appendChild(iframeContainer);
|
|
1199
|
+
this.youtubePlayer = new window.YT.Player(iframeContainer.id, {
|
|
1200
|
+
videoId: videoId,
|
|
1201
|
+
width: '100%',
|
|
1202
|
+
height: '100%',
|
|
1203
|
+
playerVars: {
|
|
1204
|
+
autoplay: this.config.autoPlay ? 1 : 0,
|
|
1205
|
+
controls: 0,
|
|
1206
|
+
disablekb: 0,
|
|
1207
|
+
fs: 0,
|
|
1208
|
+
iv_load_policy: 3,
|
|
1209
|
+
modestbranding: 1,
|
|
1210
|
+
rel: 0,
|
|
1211
|
+
showinfo: 0,
|
|
1212
|
+
playsinline: 1,
|
|
1213
|
+
start: Math.floor(currentTime)
|
|
1214
|
+
},
|
|
1215
|
+
events: {
|
|
1216
|
+
onReady: () => {
|
|
1217
|
+
this.youtubePlayerReady = true;
|
|
1218
|
+
this.debugLog('YouTube player without native controls ready');
|
|
1219
|
+
this.emit('onReady');
|
|
1220
|
+
},
|
|
1221
|
+
onStateChange: (event) => this.onYouTubePlayerStateChange(event),
|
|
1222
|
+
onError: (event) => this.onYouTubePlayerError(event)
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1304
1225
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1226
|
+
toggleYouTubeControls(useNative = !this.useYouTubeNativeControls) {
|
|
1227
|
+
if (!this.youtubePlayer) {
|
|
1228
|
+
this.debugWarn('Cannot toggle YouTube controls - no YouTube player active');
|
|
1308
1229
|
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
1230
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1231
|
+
this.useYouTubeNativeControls = useNative;
|
|
1232
|
+
this.config.youtubeNativeControls = useNative;
|
|
1233
|
+
this.updateControlsVisibility();
|
|
1234
|
+
this.debugLog('YouTube controls toggled:', {
|
|
1235
|
+
useNative: this.useYouTubeNativeControls,
|
|
1236
|
+
isLive: this.isYouTubeLive
|
|
1237
|
+
});
|
|
1238
|
+
this.showNotification(`YouTube Controls: ${this.useYouTubeNativeControls ? 'Native' : 'Custom'}`);
|
|
1239
|
+
}
|
|
1240
|
+
async getYouTubeVideoTitleFromOEmbed() {
|
|
1241
|
+
if (!this.source?.metadata?.videoId)
|
|
1242
|
+
return;
|
|
1243
|
+
try {
|
|
1244
|
+
const videoId = this.source.metadata.videoId;
|
|
1245
|
+
const oembedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`;
|
|
1246
|
+
const response = await fetch(oembedUrl);
|
|
1247
|
+
if (response.ok) {
|
|
1248
|
+
const data = await response.json();
|
|
1249
|
+
if (data.title) {
|
|
1250
|
+
this.debugLog('Got YouTube title from oembed API:', data.title);
|
|
1251
|
+
if (this.source && this.source.metadata) {
|
|
1252
|
+
this.source.metadata.title = data.title;
|
|
1253
|
+
}
|
|
1254
|
+
this.updateMetadataUI();
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1317
1257
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1258
|
+
catch (error) {
|
|
1259
|
+
this.debugWarn('Could not get YouTube title from oembed API:', error);
|
|
1320
1260
|
}
|
|
1321
1261
|
}
|
|
1322
1262
|
startYouTubeTimeTracking() {
|
|
1323
1263
|
if (this.youtubeTimeTrackingInterval) {
|
|
1324
1264
|
clearInterval(this.youtubeTimeTrackingInterval);
|
|
1325
1265
|
}
|
|
1326
|
-
let qualityCheckCounter = 0;
|
|
1327
1266
|
this.youtubeTimeTrackingInterval = setInterval(() => {
|
|
1328
1267
|
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
1329
1268
|
try {
|
|
@@ -1333,12 +1272,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
1333
1272
|
this.state.currentTime = currentTime || 0;
|
|
1334
1273
|
this.state.duration = duration || 0;
|
|
1335
1274
|
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
1275
|
this.updateYouTubeProgressBar(currentTime, duration, buffered);
|
|
1343
1276
|
this.emit('onTimeUpdate', this.state.currentTime);
|
|
1344
1277
|
this.emit('onProgress', this.state.bufferedPercentage);
|
|
@@ -8200,18 +8133,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
8200
8133
|
}
|
|
8201
8134
|
setQualityByLabel(quality) {
|
|
8202
8135
|
if (this.youtubePlayer && this.youtubePlayerReady) {
|
|
8203
|
-
this.
|
|
8136
|
+
this.showShortcutIndicator('Quality control not available for YouTube');
|
|
8204
8137
|
document.querySelectorAll('.quality-option').forEach(option => {
|
|
8205
8138
|
option.classList.remove('active');
|
|
8206
|
-
if (option.dataset.quality ===
|
|
8139
|
+
if (option.dataset.quality === 'auto') {
|
|
8207
8140
|
option.classList.add('active');
|
|
8208
8141
|
}
|
|
8209
8142
|
});
|
|
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
8143
|
return;
|
|
8216
8144
|
}
|
|
8217
8145
|
const qualityBadge = document.getElementById('uvf-quality-badge');
|
|
@@ -8512,13 +8440,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
8512
8440
|
</div>`;
|
|
8513
8441
|
}
|
|
8514
8442
|
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);
|
|
8443
|
+
const currentQuality = this.availableQualities.find(q => q.value === this.currentQuality);
|
|
8520
8444
|
const currentQualityLabel = currentQuality ? currentQuality.label : 'Auto';
|
|
8521
|
-
this.debugLog('🎛️ Current quality for display:', qualityForDisplay, '(', currentQualityLabel, ')');
|
|
8522
8445
|
menuHTML += `
|
|
8523
8446
|
<div class="uvf-accordion-item">
|
|
8524
8447
|
<div class="uvf-accordion-header" data-section="quality">
|
|
@@ -8535,10 +8458,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
8535
8458
|
</div>
|
|
8536
8459
|
<div class="uvf-accordion-content" data-section="quality">`;
|
|
8537
8460
|
this.availableQualities.forEach(quality => {
|
|
8538
|
-
const
|
|
8539
|
-
? this.youtubeCurrentQuality.value
|
|
8540
|
-
: this.currentQuality;
|
|
8541
|
-
const isActive = quality.value === qualityValue ? 'active' : '';
|
|
8461
|
+
const isActive = quality.value === this.currentQuality ? 'active' : '';
|
|
8542
8462
|
const isPremium = this.isQualityPremium(quality);
|
|
8543
8463
|
const isLocked = isPremium && !this.isPremiumUser();
|
|
8544
8464
|
const qualityHeight = quality.height || 0;
|
|
@@ -8588,20 +8508,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
8588
8508
|
this.debugLog('Settings event listeners setup complete');
|
|
8589
8509
|
}
|
|
8590
8510
|
detectAvailableQualities() {
|
|
8591
|
-
|
|
8592
|
-
this.availableQualities = [];
|
|
8511
|
+
this.availableQualities = [{ value: 'auto', label: 'Auto' }];
|
|
8593
8512
|
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) {
|
|
8513
|
+
if (this.hls && this.hls.levels) {
|
|
8605
8514
|
this.hls.levels.forEach((level, index) => {
|
|
8606
8515
|
if (level.height) {
|
|
8607
8516
|
detectedQualities.push({
|
|
@@ -8846,11 +8755,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
8846
8755
|
}
|
|
8847
8756
|
setQualityFromSettings(quality) {
|
|
8848
8757
|
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
8758
|
if (quality === 'auto') {
|
|
8855
8759
|
if (this.hls) {
|
|
8856
8760
|
if (this.qualityFilter) {
|