myetv-player 1.0.0 → 1.0.6
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/.github/workflows/codeql.yml +100 -0
- package/README.md +36 -58
- package/SECURITY.md +50 -0
- package/css/myetv-player.css +301 -218
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +1713 -1503
- package/dist/myetv-player.min.js +1670 -1471
- package/package.json +6 -1
- package/plugins/README.md +1016 -0
- package/plugins/cloudflare/README.md +1068 -0
- package/plugins/cloudflare/myetv-player-cloudflare-stream-plugin.js +556 -0
- package/plugins/facebook/README.md +1024 -0
- package/plugins/facebook/myetv-player-facebook-plugin.js +437 -0
- package/plugins/gamepad-remote-controller/README.md +816 -0
- package/plugins/gamepad-remote-controller/myetv-player-gamepad-remote-plugin.js +678 -0
- package/plugins/google-adsense-ads/README.md +1 -0
- package/plugins/google-adsense-ads/g-adsense-ads-plugin.js +158 -0
- package/plugins/google-ima-ads/README.md +1 -0
- package/plugins/google-ima-ads/g-ima-ads-plugin.js +355 -0
- package/plugins/twitch/README.md +1185 -0
- package/plugins/twitch/myetv-player-twitch-plugin.js +569 -0
- package/plugins/vast-vpaid-ads/README.md +1 -0
- package/plugins/vast-vpaid-ads/vast-vpaid-ads-plugin.js +346 -0
- package/plugins/vimeo/README.md +1416 -0
- package/plugins/vimeo/myetv-player-vimeo.js +640 -0
- package/plugins/youtube/README.md +851 -0
- package/plugins/youtube/myetv-player-youtube-plugin.js +1714 -210
- package/scss/README.md +160 -0
- package/scss/_menus.scss +840 -672
- package/scss/_responsive.scss +67 -105
- package/scss/_volume.scss +67 -105
- package/src/README.md +559 -0
- package/src/controls.js +16 -4
- package/src/core.js +1192 -1062
- package/src/i18n.js +27 -1
- package/src/quality.js +478 -436
- package/src/subtitles.js +2 -2
package/src/quality.js
CHANGED
|
@@ -2,141 +2,161 @@
|
|
|
2
2
|
// Conservative modularization - original code preserved exactly
|
|
3
3
|
// Created by https://www.myetv.tv https://oskarcosimo.com
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
initializeQualityMonitoring() {
|
|
6
|
+
this.qualityMonitorInterval = setInterval(() => {
|
|
7
|
+
if (!this.isChangingQuality) {
|
|
8
|
+
this.updateCurrentPlayingQuality();
|
|
9
|
+
}
|
|
10
|
+
}, 3000);
|
|
11
|
+
|
|
12
|
+
if (this.video) {
|
|
13
|
+
this.video.addEventListener('loadedmetadata', () => {
|
|
14
|
+
setTimeout(() => {
|
|
15
|
+
if (!this.isChangingQuality) {
|
|
16
|
+
this.updateCurrentPlayingQuality();
|
|
17
|
+
}
|
|
18
|
+
}, 100);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
this.video.addEventListener('resize', () => {
|
|
7
22
|
if (!this.isChangingQuality) {
|
|
8
23
|
this.updateCurrentPlayingQuality();
|
|
9
24
|
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (this.video) {
|
|
13
|
-
this.video.addEventListener('loadedmetadata', () => {
|
|
14
|
-
setTimeout(() => {
|
|
15
|
-
if (!this.isChangingQuality) {
|
|
16
|
-
this.updateCurrentPlayingQuality();
|
|
17
|
-
}
|
|
18
|
-
}, 100);
|
|
19
|
-
});
|
|
25
|
+
});
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
this.video.addEventListener('loadeddata', () => {
|
|
28
|
+
setTimeout(() => {
|
|
22
29
|
if (!this.isChangingQuality) {
|
|
23
30
|
this.updateCurrentPlayingQuality();
|
|
24
31
|
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
this.video.addEventListener('loadeddata', () => {
|
|
28
|
-
setTimeout(() => {
|
|
29
|
-
if (!this.isChangingQuality) {
|
|
30
|
-
this.updateCurrentPlayingQuality();
|
|
31
|
-
}
|
|
32
|
-
}, 1000);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
32
|
+
}, 1000);
|
|
33
|
+
});
|
|
35
34
|
}
|
|
35
|
+
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (this.video.currentSrc && this.qualities && this.qualities.length > 0) {
|
|
41
|
-
const currentSource = this.qualities.find(q => {
|
|
42
|
-
const currentUrl = this.video.currentSrc.toLowerCase();
|
|
43
|
-
const qualityUrl = q.src.toLowerCase();
|
|
44
|
-
|
|
45
|
-
if (this.debugQuality) {
|
|
46
|
-
if (this.options.debug) console.log('Quality comparison:', {
|
|
47
|
-
current: currentUrl,
|
|
48
|
-
quality: qualityUrl,
|
|
49
|
-
qualityName: q.quality,
|
|
50
|
-
match: currentUrl === qualityUrl || currentUrl.includes(qualityUrl) || qualityUrl.includes(currentUrl)
|
|
51
|
-
});
|
|
52
|
-
}
|
|
37
|
+
getCurrentPlayingQuality() {
|
|
38
|
+
if (!this.video) return null;
|
|
53
39
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
40
|
+
if (this.video.currentSrc && this.qualities && this.qualities.length > 0) {
|
|
41
|
+
const currentSource = this.qualities.find(q => {
|
|
42
|
+
const currentUrl = this.video.currentSrc.toLowerCase();
|
|
43
|
+
const qualityUrl = q.src.toLowerCase();
|
|
58
44
|
|
|
59
|
-
if (
|
|
60
|
-
if (this.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
if (this.debugQuality) {
|
|
46
|
+
if (this.options.debug) console.log('Quality comparison:', {
|
|
47
|
+
current: currentUrl,
|
|
48
|
+
quality: qualityUrl,
|
|
49
|
+
qualityName: q.quality,
|
|
50
|
+
match: currentUrl === qualityUrl || currentUrl.includes(qualityUrl) || qualityUrl.includes(currentUrl)
|
|
51
|
+
});
|
|
64
52
|
}
|
|
65
|
-
}
|
|
66
53
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
54
|
+
return currentUrl === qualityUrl ||
|
|
55
|
+
currentUrl.includes(qualityUrl) ||
|
|
56
|
+
qualityUrl.includes(currentUrl);
|
|
57
|
+
});
|
|
70
58
|
|
|
59
|
+
if (currentSource) {
|
|
71
60
|
if (this.debugQuality) {
|
|
72
|
-
if (this.options.debug) console.log('
|
|
61
|
+
if (this.options.debug) console.log('Quality found from source:', currentSource.quality);
|
|
73
62
|
}
|
|
74
|
-
|
|
75
|
-
if (height >= 2160) return '4K';
|
|
76
|
-
if (height >= 1440) return '1440p';
|
|
77
|
-
if (height >= 1080) return '1080p';
|
|
78
|
-
if (height >= 720) return '720p';
|
|
79
|
-
if (height >= 480) return '480p';
|
|
80
|
-
if (height >= 360) return '360p';
|
|
81
|
-
if (height >= 240) return '240p';
|
|
82
|
-
|
|
83
|
-
return `${height}p`;
|
|
63
|
+
return currentSource.quality;
|
|
84
64
|
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (this.video.videoHeight && this.video.videoWidth) {
|
|
68
|
+
const height = this.video.videoHeight;
|
|
69
|
+
const width = this.video.videoWidth;
|
|
85
70
|
|
|
86
71
|
if (this.debugQuality) {
|
|
87
|
-
if (this.options.debug) console.log('
|
|
88
|
-
currentSrc: this.video.currentSrc,
|
|
89
|
-
videoHeight: this.video.videoHeight,
|
|
90
|
-
videoWidth: this.video.videoWidth,
|
|
91
|
-
qualities: this.qualities
|
|
92
|
-
});
|
|
72
|
+
if (this.options.debug) console.log('Risoluzione video:', { height, width });
|
|
93
73
|
}
|
|
94
74
|
|
|
95
|
-
return
|
|
96
|
-
|
|
75
|
+
if (height >= 2160) return '4K';
|
|
76
|
+
if (height >= 1440) return '1440p';
|
|
77
|
+
if (height >= 1080) return '1080p';
|
|
78
|
+
if (height >= 720) return '720p';
|
|
79
|
+
if (height >= 480) return '480p';
|
|
80
|
+
if (height >= 360) return '360p';
|
|
81
|
+
if (height >= 240) return '240p';
|
|
97
82
|
|
|
98
|
-
|
|
99
|
-
|
|
83
|
+
return `${height}p`;
|
|
84
|
+
}
|
|
100
85
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
105
|
-
|
|
86
|
+
if (this.debugQuality) {
|
|
87
|
+
if (this.options.debug) console.log('No quality detected:', {
|
|
88
|
+
currentSrc: this.video.currentSrc,
|
|
89
|
+
videoHeight: this.video.videoHeight,
|
|
90
|
+
videoWidth: this.video.videoWidth,
|
|
91
|
+
qualities: this.qualities
|
|
92
|
+
});
|
|
106
93
|
}
|
|
107
94
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
updateCurrentPlayingQuality() {
|
|
99
|
+
const newPlayingQuality = this.getCurrentPlayingQuality();
|
|
100
|
+
|
|
101
|
+
if (newPlayingQuality && newPlayingQuality !== this.currentPlayingQuality) {
|
|
102
|
+
if (this.options.debug) console.log(`Quality changed: ${this.currentPlayingQuality} → ${newPlayingQuality}`);
|
|
103
|
+
this.currentPlayingQuality = newPlayingQuality;
|
|
104
|
+
this.updateQualityDisplay();
|
|
111
105
|
}
|
|
106
|
+
}
|
|
112
107
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
updateQualityDisplay() {
|
|
109
|
+
this.updateQualityButton();
|
|
110
|
+
this.updateQualityMenu();
|
|
111
|
+
}
|
|
116
112
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<span class="icon">⚙</span>
|
|
121
|
-
<div class="quality-btn-text">
|
|
122
|
-
<div class="selected-quality">${this.selectedQuality === 'auto' ? this.t('auto') : this.selectedQuality}</div>
|
|
123
|
-
<div class="current-quality">${this.currentPlayingQuality || ''}</div>
|
|
124
|
-
</div>
|
|
125
|
-
`;
|
|
126
|
-
} else {
|
|
127
|
-
const selectedEl = btnText.querySelector('.selected-quality');
|
|
128
|
-
const currentEl = btnText.querySelector('.current-quality');
|
|
113
|
+
updateQualityButton() {
|
|
114
|
+
const qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
115
|
+
if (!qualityBtn) return;
|
|
129
116
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
117
|
+
let btnText = qualityBtn.querySelector('.quality-btn-text');
|
|
118
|
+
if (!btnText) {
|
|
119
|
+
// SECURITY: Use DOM methods instead of innerHTML to prevent XSS
|
|
120
|
+
qualityBtn.textContent = ''; // Clear existing content
|
|
121
|
+
|
|
122
|
+
// Create icon element
|
|
123
|
+
const iconSpan = document.createElement('span');
|
|
124
|
+
iconSpan.className = 'icon';
|
|
125
|
+
iconSpan.textContent = '⚙';
|
|
126
|
+
qualityBtn.appendChild(iconSpan);
|
|
127
|
+
|
|
128
|
+
// Create text container
|
|
129
|
+
btnText = document.createElement('div');
|
|
130
|
+
btnText.className = 'quality-btn-text';
|
|
131
|
+
|
|
132
|
+
// Create selected quality element
|
|
133
|
+
const selectedQualityDiv = document.createElement('div');
|
|
134
|
+
selectedQualityDiv.className = 'selected-quality';
|
|
135
|
+
selectedQualityDiv.textContent = this.selectedQuality === 'auto' ? this.t('auto') : this.selectedQuality;
|
|
136
|
+
btnText.appendChild(selectedQualityDiv);
|
|
137
|
+
|
|
138
|
+
// Create current quality element
|
|
139
|
+
const currentQualityDiv = document.createElement('div');
|
|
140
|
+
currentQualityDiv.className = 'current-quality';
|
|
141
|
+
currentQualityDiv.textContent = this.currentPlayingQuality || '';
|
|
142
|
+
btnText.appendChild(currentQualityDiv);
|
|
143
|
+
|
|
144
|
+
// Append to button
|
|
145
|
+
qualityBtn.appendChild(btnText);
|
|
146
|
+
} else {
|
|
147
|
+
// SECURITY: Update existing elements using textContent (not innerHTML)
|
|
148
|
+
const selectedEl = btnText.querySelector('.selected-quality');
|
|
149
|
+
const currentEl = btnText.querySelector('.current-quality');
|
|
133
150
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
if (selectedEl) {
|
|
152
|
+
selectedEl.textContent = this.selectedQuality === 'auto' ? this.t('auto') : this.selectedQuality;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (currentEl) {
|
|
156
|
+
currentEl.textContent = this.currentPlayingQuality || '';
|
|
138
157
|
}
|
|
139
158
|
}
|
|
159
|
+
}
|
|
140
160
|
|
|
141
161
|
updateQualityMenu() {
|
|
142
162
|
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
@@ -190,213 +210,217 @@ updateQualityMenu() {
|
|
|
190
210
|
qualityMenu.innerHTML = menuHTML;
|
|
191
211
|
}
|
|
192
212
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
213
|
+
getQualityStatus() {
|
|
214
|
+
return {
|
|
215
|
+
selected: this.selectedQuality,
|
|
216
|
+
playing: this.currentPlayingQuality,
|
|
217
|
+
isAuto: this.selectedQuality === 'auto',
|
|
218
|
+
isChanging: this.isChangingQuality
|
|
219
|
+
};
|
|
220
|
+
}
|
|
201
221
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
222
|
+
getSelectedQuality() {
|
|
223
|
+
return this.selectedQuality;
|
|
224
|
+
}
|
|
205
225
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
226
|
+
isAutoQualityActive() {
|
|
227
|
+
return this.selectedQuality === 'auto';
|
|
228
|
+
}
|
|
209
229
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
230
|
+
enableQualityDebug() {
|
|
231
|
+
this.debugQuality = true;
|
|
232
|
+
this.enableAutoHideDebug(); // Abilita anche debug auto-hide
|
|
233
|
+
if (this.options.debug) console.log('Quality AND auto-hide debug enabled');
|
|
234
|
+
this.updateCurrentPlayingQuality();
|
|
235
|
+
}
|
|
216
236
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
237
|
+
disableQualityDebug() {
|
|
238
|
+
this.debugQuality = false;
|
|
239
|
+
this.disableAutoHideDebug();
|
|
240
|
+
if (this.options.debug) console.log('Quality AND auto-hide debug disabled');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
changeQuality(e) {
|
|
244
|
+
if (!e.target.classList.contains('quality-option')) return;
|
|
245
|
+
if (this.isChangingQuality) return;
|
|
246
|
+
|
|
247
|
+
// Handle adaptive streaming quality change
|
|
248
|
+
const adaptiveQuality = e.target.getAttribute('data-adaptive-quality');
|
|
249
|
+
if (adaptiveQuality !== null && this.isAdaptiveStream) {
|
|
250
|
+
const qualityIndex = adaptiveQuality === 'auto' ? -1 : parseInt(adaptiveQuality);
|
|
251
|
+
this.setAdaptiveQuality(qualityIndex);
|
|
252
|
+
this.updateAdaptiveQualityMenu();
|
|
253
|
+
return;
|
|
221
254
|
}
|
|
222
255
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (this.isChangingQuality) return;
|
|
256
|
+
const quality = e.target.getAttribute('data-quality');
|
|
257
|
+
if (!quality || quality === this.selectedQuality) return;
|
|
226
258
|
|
|
227
|
-
|
|
228
|
-
const adaptiveQuality = e.target.getAttribute('data-adaptive-quality');
|
|
229
|
-
if (adaptiveQuality !== null && this.isAdaptiveStream) {
|
|
230
|
-
const qualityIndex = adaptiveQuality === 'auto' ? -1 : parseInt(adaptiveQuality);
|
|
231
|
-
this.setAdaptiveQuality(qualityIndex);
|
|
232
|
-
this.updateAdaptiveQualityMenu();
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
259
|
+
if (this.options.debug) console.log(`Quality change requested: ${this.selectedQuality} → ${quality}`);
|
|
235
260
|
|
|
236
|
-
|
|
237
|
-
if (!quality || quality === this.selectedQuality) return;
|
|
261
|
+
this.selectedQuality = quality;
|
|
238
262
|
|
|
239
|
-
|
|
263
|
+
if (quality === 'auto') {
|
|
264
|
+
this.enableAutoQuality();
|
|
265
|
+
} else {
|
|
266
|
+
this.setQuality(quality);
|
|
267
|
+
}
|
|
240
268
|
|
|
241
|
-
|
|
269
|
+
this.updateQualityDisplay();
|
|
270
|
+
}
|
|
242
271
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
} else {
|
|
246
|
-
this.setQuality(quality);
|
|
247
|
-
}
|
|
272
|
+
setQuality(targetQuality) {
|
|
273
|
+
if (this.options.debug) console.log(`setQuality("${targetQuality}") called`);
|
|
248
274
|
|
|
249
|
-
|
|
275
|
+
if (!targetQuality) {
|
|
276
|
+
if (this.options.debug) console.error('targetQuality is empty!');
|
|
277
|
+
return;
|
|
250
278
|
}
|
|
251
279
|
|
|
252
|
-
|
|
253
|
-
|
|
280
|
+
if (!this.video || !this.qualities || this.qualities.length === 0) return;
|
|
281
|
+
if (this.isChangingQuality) return;
|
|
254
282
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
283
|
+
const newSource = this.qualities.find(q => q.quality === targetQuality);
|
|
284
|
+
if (!newSource || !newSource.src) {
|
|
285
|
+
if (this.options.debug) console.error(`Quality "${targetQuality}" not found`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
259
288
|
|
|
260
|
-
|
|
261
|
-
|
|
289
|
+
const currentTime = this.video.currentTime || 0;
|
|
290
|
+
const wasPlaying = !this.video.paused;
|
|
262
291
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
292
|
+
this.isChangingQuality = true;
|
|
293
|
+
this.selectedQuality = targetQuality;
|
|
294
|
+
this.video.pause();
|
|
268
295
|
|
|
269
|
-
|
|
270
|
-
|
|
296
|
+
// Show loading state during quality change
|
|
297
|
+
this.showLoading();
|
|
298
|
+
if (this.video.classList) {
|
|
299
|
+
this.video.classList.add('quality-changing');
|
|
300
|
+
}
|
|
271
301
|
|
|
272
|
-
|
|
273
|
-
this.
|
|
274
|
-
this.video.
|
|
302
|
+
const onLoadedData = () => {
|
|
303
|
+
if (this.options.debug) console.log(`Quality ${targetQuality} applied!`);
|
|
304
|
+
this.video.currentTime = currentTime;
|
|
275
305
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
306
|
+
if (wasPlaying) {
|
|
307
|
+
this.video.play().catch(e => {
|
|
308
|
+
if (this.options.debug) console.log('Play error:', e);
|
|
309
|
+
});
|
|
280
310
|
}
|
|
281
311
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (wasPlaying) {
|
|
287
|
-
this.video.play().catch(e => {
|
|
288
|
-
if (this.options.debug) console.log('Play error:', e);
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
this.currentPlayingQuality = targetQuality;
|
|
293
|
-
this.updateQualityDisplay();
|
|
294
|
-
this.isChangingQuality = false;
|
|
312
|
+
this.currentPlayingQuality = targetQuality;
|
|
313
|
+
this.updateQualityDisplay();
|
|
314
|
+
this.isChangingQuality = false;
|
|
295
315
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
316
|
+
// Restore resolution settings after quality change
|
|
317
|
+
this.restoreResolutionAfterQualityChange();
|
|
318
|
+
cleanup();
|
|
319
|
+
};
|
|
300
320
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
cleanup();
|
|
305
|
-
};
|
|
321
|
+
const onError = (error) => {
|
|
322
|
+
if (this.options.debug) console.error(`Loading error ${targetQuality}:`, error);
|
|
323
|
+
this.isChangingQuality = false;
|
|
306
324
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
this.video.removeEventListener('error', onError);
|
|
310
|
-
};
|
|
325
|
+
// Trigger ended event for error handling
|
|
326
|
+
this.onVideoError(error);
|
|
311
327
|
|
|
312
|
-
|
|
313
|
-
|
|
328
|
+
cleanup();
|
|
329
|
+
};
|
|
314
330
|
|
|
315
|
-
|
|
316
|
-
this.video.
|
|
317
|
-
|
|
331
|
+
const cleanup = () => {
|
|
332
|
+
this.video.removeEventListener('loadeddata', onLoadedData);
|
|
333
|
+
this.video.removeEventListener('error', onError);
|
|
334
|
+
};
|
|
318
335
|
|
|
319
|
-
|
|
320
|
-
|
|
336
|
+
this.video.addEventListener('loadeddata', onLoadedData, { once: true });
|
|
337
|
+
this.video.addEventListener('error', onError, { once: true });
|
|
321
338
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
339
|
+
this.video.src = newSource.src;
|
|
340
|
+
this.video.load();
|
|
341
|
+
}
|
|
326
342
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (success && currentTime > 0 && this.video.duration) {
|
|
330
|
-
this.video.currentTime = Math.min(currentTime, this.video.duration);
|
|
331
|
-
}
|
|
343
|
+
finishQualityChange(success, wasPlaying, currentTime, currentVolume, wasMuted, targetQuality) {
|
|
344
|
+
if (this.options.debug) console.log(`Quality change completion: success=${success}, target=${targetQuality}`);
|
|
332
345
|
|
|
333
|
-
|
|
334
|
-
|
|
346
|
+
if (this.qualityChangeTimeout) {
|
|
347
|
+
clearTimeout(this.qualityChangeTimeout);
|
|
348
|
+
this.qualityChangeTimeout = null;
|
|
349
|
+
}
|
|
335
350
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
} catch (error) {
|
|
342
|
-
if (this.options.debug) console.error('Errore ripristino stato:', error);
|
|
351
|
+
if (this.video) {
|
|
352
|
+
try {
|
|
353
|
+
if (success && currentTime > 0 && this.video.duration) {
|
|
354
|
+
this.video.currentTime = Math.min(currentTime, this.video.duration);
|
|
343
355
|
}
|
|
344
356
|
|
|
345
|
-
|
|
346
|
-
|
|
357
|
+
this.video.volume = currentVolume;
|
|
358
|
+
this.video.muted = wasMuted;
|
|
359
|
+
|
|
360
|
+
if (success && wasPlaying) {
|
|
361
|
+
this.video.play().catch(err => {
|
|
362
|
+
if (this.options.debug) console.warn('Play after quality change failed:', err);
|
|
363
|
+
});
|
|
347
364
|
}
|
|
365
|
+
} catch (error) {
|
|
366
|
+
if (this.options.debug) console.error('Errore ripristino stato:', error);
|
|
348
367
|
}
|
|
349
368
|
|
|
350
|
-
this.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (success) {
|
|
354
|
-
if (this.options.debug) console.log('Quality change completed successfully');
|
|
355
|
-
setTimeout(() => {
|
|
356
|
-
this.currentPlayingQuality = targetQuality;
|
|
357
|
-
this.updateQualityDisplay();
|
|
358
|
-
if (this.options.debug) console.log(`🎯 Quality confirmed active: ${targetQuality}`);
|
|
359
|
-
}, 100);
|
|
360
|
-
} else {
|
|
361
|
-
if (this.options.debug) console.warn('Quality change failed or timeout');
|
|
369
|
+
if (this.video.classList) {
|
|
370
|
+
this.video.classList.remove('quality-changing');
|
|
362
371
|
}
|
|
372
|
+
}
|
|
363
373
|
|
|
374
|
+
this.hideLoading();
|
|
375
|
+
this.isChangingQuality = false;
|
|
376
|
+
|
|
377
|
+
if (success) {
|
|
378
|
+
if (this.options.debug) console.log('Quality change completed successfully');
|
|
364
379
|
setTimeout(() => {
|
|
365
|
-
this.
|
|
366
|
-
|
|
380
|
+
this.currentPlayingQuality = targetQuality;
|
|
381
|
+
this.updateQualityDisplay();
|
|
382
|
+
if (this.options.debug) console.log(`🎯 Quality confirmed active: ${targetQuality}`);
|
|
383
|
+
}, 100);
|
|
384
|
+
} else {
|
|
385
|
+
if (this.options.debug) console.warn('Quality change failed or timeout');
|
|
367
386
|
}
|
|
368
387
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
}
|
|
388
|
+
setTimeout(() => {
|
|
389
|
+
this.updateCurrentPlayingQuality();
|
|
390
|
+
}, 2000);
|
|
391
|
+
}
|
|
375
392
|
|
|
376
|
-
|
|
377
|
-
|
|
393
|
+
cleanupQualityChange() {
|
|
394
|
+
if (this.qualityChangeTimeout) {
|
|
395
|
+
clearTimeout(this.qualityChangeTimeout);
|
|
396
|
+
this.qualityChangeTimeout = null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
378
399
|
|
|
379
|
-
|
|
380
|
-
|
|
400
|
+
enableAutoQuality() {
|
|
401
|
+
if (this.options.debug) console.log('🔄 enableAutoQuality - keeping selectedQuality as "auto"');
|
|
381
402
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
this.updateQualityDisplay();
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
403
|
+
// IMPORTANT: Keep selectedQuality as 'auto' for proper UI display
|
|
404
|
+
this.selectedQuality = 'auto';
|
|
387
405
|
|
|
388
|
-
|
|
389
|
-
|
|
406
|
+
if (!this.qualities || this.qualities.length === 0) {
|
|
407
|
+
if (this.options.debug) console.warn('⚠️ No qualities available for auto selection');
|
|
408
|
+
this.updateQualityDisplay();
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
390
411
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
console.log('📊 selectedQuality remains: "auto" (for UI)');
|
|
394
|
-
}
|
|
412
|
+
// Smart connection-based quality selection
|
|
413
|
+
let autoSelectedQuality = this.getAutoQualityBasedOnConnection();
|
|
395
414
|
|
|
396
|
-
|
|
397
|
-
|
|
415
|
+
if (this.options.debug) {
|
|
416
|
+
console.log('🎯 Auto quality selected:', autoSelectedQuality);
|
|
417
|
+
console.log('📊 selectedQuality remains: "auto" (for UI)');
|
|
398
418
|
}
|
|
399
419
|
|
|
420
|
+
// Apply the auto-selected quality but keep UI showing "auto"
|
|
421
|
+
this.applyAutoQuality(autoSelectedQuality);
|
|
422
|
+
}
|
|
423
|
+
|
|
400
424
|
// ENHANCED CONNECTION DETECTION - Uses RTT + downlink heuristics
|
|
401
425
|
// Handles both Ethernet and real mobile 4G intelligently
|
|
402
426
|
|
|
@@ -663,102 +687,120 @@ getAutoQualityBasedOnConnection() {
|
|
|
663
687
|
return maxQuality.quality;
|
|
664
688
|
}
|
|
665
689
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
690
|
+
applyAutoQuality(targetQuality) {
|
|
691
|
+
if (!targetQuality || !this.video || !this.qualities || this.qualities.length === 0) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
670
694
|
|
|
671
|
-
|
|
695
|
+
if (this.isChangingQuality) return;
|
|
672
696
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
697
|
+
const newSource = this.qualities.find(q => q.quality === targetQuality);
|
|
698
|
+
if (!newSource || !newSource.src) {
|
|
699
|
+
if (this.options.debug) console.error('Auto quality', targetQuality, 'not found');
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
678
702
|
|
|
679
|
-
|
|
680
|
-
|
|
703
|
+
// Store current resolution to restore after quality change
|
|
704
|
+
const currentResolution = this.getCurrentResolution();
|
|
681
705
|
|
|
682
|
-
|
|
683
|
-
|
|
706
|
+
const currentTime = this.video.currentTime || 0;
|
|
707
|
+
const wasPlaying = !this.video.paused;
|
|
684
708
|
|
|
685
|
-
|
|
686
|
-
|
|
709
|
+
this.isChangingQuality = true;
|
|
710
|
+
this.video.pause();
|
|
687
711
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if (this.options.debug) console.log('Autoplay prevented:', e);
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
this.currentPlayingQuality = targetQuality;
|
|
697
|
-
// Keep selectedQuality as 'auto' for UI display
|
|
698
|
-
this.updateQualityDisplay();
|
|
699
|
-
this.isChangingQuality = false;
|
|
700
|
-
cleanup();
|
|
701
|
-
};
|
|
712
|
+
// Show loading overlay
|
|
713
|
+
this.showLoading();
|
|
714
|
+
if (this.video.classList) {
|
|
715
|
+
this.video.classList.add('quality-changing');
|
|
716
|
+
}
|
|
702
717
|
|
|
703
|
-
const onError = (error) => {
|
|
704
|
-
if (this.options.debug) console.error('Auto quality loading error:', error);
|
|
705
|
-
this.isChangingQuality = false;
|
|
706
|
-
cleanup();
|
|
707
|
-
};
|
|
708
718
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
719
|
+
const onLoadedData = () => {
|
|
720
|
+
if (this.options.debug) console.log('Auto quality', targetQuality, 'applied');
|
|
721
|
+
this.video.currentTime = currentTime;
|
|
722
|
+
if (wasPlaying) {
|
|
723
|
+
this.video.play().catch(e => {
|
|
724
|
+
if (this.options.debug) console.log('Autoplay prevented:', e);
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
this.currentPlayingQuality = targetQuality;
|
|
728
|
+
// Keep selectedQuality as 'auto' for UI display
|
|
729
|
+
this.updateQualityDisplay();
|
|
713
730
|
|
|
714
|
-
|
|
715
|
-
this.
|
|
716
|
-
this.video.
|
|
717
|
-
|
|
718
|
-
|
|
731
|
+
// Hide loading overlay
|
|
732
|
+
this.hideLoading();
|
|
733
|
+
if (this.video.classList) {
|
|
734
|
+
this.video.classList.remove('quality-changing');
|
|
735
|
+
}
|
|
719
736
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
this.selectedQuality = quality;
|
|
737
|
+
this.isChangingQuality = false;
|
|
738
|
+
cleanup();
|
|
739
|
+
};
|
|
724
740
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
this.setQuality(quality);
|
|
729
|
-
}
|
|
741
|
+
const onError = (error) => {
|
|
742
|
+
if (this.options.debug) console.error('Auto quality loading error:', error);
|
|
743
|
+
this.isChangingQuality = false;
|
|
730
744
|
|
|
731
|
-
|
|
732
|
-
|
|
745
|
+
// Trigger ended event for error handling
|
|
746
|
+
this.onVideoError(error);
|
|
733
747
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
748
|
+
cleanup();
|
|
749
|
+
};
|
|
737
750
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
751
|
+
const cleanup = () => {
|
|
752
|
+
this.video.removeEventListener('loadeddata', onLoadedData);
|
|
753
|
+
this.video.removeEventListener('error', onError);
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
this.video.addEventListener('loadeddata', onLoadedData, { once: true });
|
|
757
|
+
this.video.addEventListener('error', onError, { once: true });
|
|
758
|
+
this.video.src = newSource.src;
|
|
759
|
+
this.video.load();
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
setDefaultQuality(quality) {
|
|
763
|
+
if (this.options.debug) console.log(`🔧 Setting defaultQuality: "${quality}"`);
|
|
764
|
+
this.options.defaultQuality = quality;
|
|
765
|
+
this.selectedQuality = quality;
|
|
766
|
+
|
|
767
|
+
if (quality === 'auto') {
|
|
768
|
+
this.enableAutoQuality();
|
|
769
|
+
} else {
|
|
770
|
+
this.setQuality(quality);
|
|
747
771
|
}
|
|
748
772
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
773
|
+
return this;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
getDefaultQuality() {
|
|
777
|
+
return this.options.defaultQuality;
|
|
778
|
+
}
|
|
752
779
|
|
|
753
|
-
|
|
780
|
+
getQualityLabel(height, width) {
|
|
781
|
+
if (height >= 2160) return '4K';
|
|
782
|
+
if (height >= 1440) return '1440p';
|
|
783
|
+
if (height >= 1080) return '1080p';
|
|
784
|
+
if (height >= 720) return '720p';
|
|
785
|
+
if (height >= 480) return '480p';
|
|
786
|
+
if (height >= 360) return '360p';
|
|
787
|
+
if (height >= 240) return '240p';
|
|
788
|
+
return `${height}p`;
|
|
789
|
+
}
|
|
754
790
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
});
|
|
791
|
+
updateAdaptiveQualityMenu() {
|
|
792
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
793
|
+
if (!qualityMenu || !this.isAdaptiveStream) return;
|
|
759
794
|
|
|
760
|
-
|
|
761
|
-
|
|
795
|
+
let menuHTML = `<div class="quality-option ${this.isAutoQuality() ? 'active' : ''}" data-adaptive-quality="auto">Auto</div>`;
|
|
796
|
+
|
|
797
|
+
this.adaptiveQualities.forEach(quality => {
|
|
798
|
+
const isActive = this.getCurrentAdaptiveQuality() === quality.index;
|
|
799
|
+
menuHTML += `<div class="quality-option ${isActive ? 'active' : ''}" data-adaptive-quality="${quality.index}">${quality.label}</div>`;
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
qualityMenu.innerHTML = menuHTML;
|
|
803
|
+
}
|
|
762
804
|
|
|
763
805
|
updateAdaptiveQualityDisplay() {
|
|
764
806
|
if (!this.isAdaptiveStream) return;
|
|
@@ -784,61 +826,61 @@ updateAdaptiveQualityDisplay() {
|
|
|
784
826
|
}
|
|
785
827
|
}
|
|
786
828
|
|
|
787
|
-
|
|
788
|
-
|
|
829
|
+
setAdaptiveQuality(qualityIndex) {
|
|
830
|
+
if (!this.isAdaptiveStream) return;
|
|
789
831
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
}
|
|
815
|
-
this.selectedQuality = this.adaptiveQualities[qualityIndex]?.label || 'Unknown';
|
|
832
|
+
try {
|
|
833
|
+
if (qualityIndex === 'auto' || qualityIndex === -1) {
|
|
834
|
+
// Enable auto quality
|
|
835
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
836
|
+
this.dashPlayer.updateSettings({
|
|
837
|
+
streaming: {
|
|
838
|
+
abr: { autoSwitchBitrate: { video: true } }
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
842
|
+
this.hlsPlayer.currentLevel = -1; // Auto level selection
|
|
843
|
+
}
|
|
844
|
+
this.selectedQuality = 'auto';
|
|
845
|
+
} else {
|
|
846
|
+
// Set specific quality
|
|
847
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
848
|
+
this.dashPlayer.updateSettings({
|
|
849
|
+
streaming: {
|
|
850
|
+
abr: { autoSwitchBitrate: { video: false } }
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
this.dashPlayer.setQualityFor('video', qualityIndex);
|
|
854
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
855
|
+
this.hlsPlayer.currentLevel = qualityIndex;
|
|
816
856
|
}
|
|
857
|
+
this.selectedQuality = this.adaptiveQualities[qualityIndex]?.label || 'Unknown';
|
|
858
|
+
}
|
|
817
859
|
|
|
818
|
-
|
|
819
|
-
|
|
860
|
+
this.updateAdaptiveQualityDisplay();
|
|
861
|
+
if (this.options.debug) console.log('📡 Adaptive quality set to:', qualityIndex);
|
|
820
862
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
863
|
+
} catch (error) {
|
|
864
|
+
if (this.options.debug) console.error('📡 Error setting adaptive quality:', error);
|
|
824
865
|
}
|
|
866
|
+
}
|
|
825
867
|
|
|
826
|
-
|
|
827
|
-
|
|
868
|
+
getCurrentAdaptiveQuality() {
|
|
869
|
+
if (!this.isAdaptiveStream) return null;
|
|
828
870
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}
|
|
835
|
-
} catch (error) {
|
|
836
|
-
if (this.options.debug) console.error('📡 Error getting current quality:', error);
|
|
871
|
+
try {
|
|
872
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
873
|
+
return this.dashPlayer.getQualityFor('video');
|
|
874
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
875
|
+
return this.hlsPlayer.currentLevel;
|
|
837
876
|
}
|
|
838
|
-
|
|
839
|
-
|
|
877
|
+
} catch (error) {
|
|
878
|
+
if (this.options.debug) console.error('📡 Error getting current quality:', error);
|
|
840
879
|
}
|
|
841
880
|
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
|
|
842
884
|
getCurrentAdaptiveQualityLabel() {
|
|
843
885
|
const currentIndex = this.getCurrentAdaptiveQuality();
|
|
844
886
|
if (currentIndex === null || currentIndex === -1) {
|
|
@@ -847,75 +889,75 @@ getCurrentAdaptiveQualityLabel() {
|
|
|
847
889
|
return this.adaptiveQualities[currentIndex]?.label || this.tauto;
|
|
848
890
|
}
|
|
849
891
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
}
|
|
855
|
-
return this.selectedQuality === 'auto';
|
|
892
|
+
isAutoQuality() {
|
|
893
|
+
if (this.isAdaptiveStream) {
|
|
894
|
+
const currentQuality = this.getCurrentAdaptiveQuality();
|
|
895
|
+
return currentQuality === null || currentQuality === -1 || this.selectedQuality === 'auto';
|
|
856
896
|
}
|
|
897
|
+
return this.selectedQuality === 'auto';
|
|
898
|
+
}
|
|
857
899
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
900
|
+
setResolution(resolution) {
|
|
901
|
+
if (!this.video || !this.container) {
|
|
902
|
+
if (this.options.debug) console.warn("Video or container not available for setResolution");
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
863
905
|
|
|
864
|
-
|
|
865
|
-
|
|
906
|
+
// Supported values including new scale-to-fit mode
|
|
907
|
+
const supportedResolutions = ["normal", "4:3", "16:9", "stretched", "fit-to-screen", "scale-to-fit"];
|
|
866
908
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
909
|
+
if (!supportedResolutions.includes(resolution)) {
|
|
910
|
+
if (this.options.debug) console.warn(`Resolution "${resolution}" not supported. Supported values: ${supportedResolutions.join(", ")}`);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
871
913
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
914
|
+
// Remove all previous resolution classes
|
|
915
|
+
const allResolutionClasses = [
|
|
916
|
+
"resolution-normal", "resolution-4-3", "resolution-16-9",
|
|
917
|
+
"resolution-stretched", "resolution-fit-to-screen", "resolution-scale-to-fit"
|
|
918
|
+
];
|
|
877
919
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
920
|
+
this.video.classList.remove(...allResolutionClasses);
|
|
921
|
+
if (this.container) {
|
|
922
|
+
this.container.classList.remove(...allResolutionClasses);
|
|
923
|
+
}
|
|
882
924
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
925
|
+
// Apply new resolution class
|
|
926
|
+
const cssClass = `resolution-${resolution.replace(":", "-")}`;
|
|
927
|
+
this.video.classList.add(cssClass);
|
|
928
|
+
if (this.container) {
|
|
929
|
+
this.container.classList.add(cssClass);
|
|
930
|
+
}
|
|
889
931
|
|
|
890
|
-
|
|
891
|
-
|
|
932
|
+
// Update option
|
|
933
|
+
this.options.resolution = resolution;
|
|
892
934
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
}
|
|
935
|
+
if (this.options.debug) {
|
|
936
|
+
console.log(`Resolution applied: ${resolution} (CSS class: ${cssClass})`);
|
|
896
937
|
}
|
|
938
|
+
}
|
|
897
939
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
940
|
+
getCurrentResolution() {
|
|
941
|
+
return this.options.resolution || "normal";
|
|
942
|
+
}
|
|
901
943
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}
|
|
944
|
+
initializeResolution() {
|
|
945
|
+
if (this.options.resolution && this.options.resolution !== "normal") {
|
|
946
|
+
this.setResolution(this.options.resolution);
|
|
906
947
|
}
|
|
948
|
+
}
|
|
907
949
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}
|
|
913
|
-
// Small delay to ensure video element is ready
|
|
914
|
-
setTimeout(() => {
|
|
915
|
-
this.setResolution(this.options.resolution);
|
|
916
|
-
}, 150);
|
|
950
|
+
restoreResolutionAfterQualityChange() {
|
|
951
|
+
if (this.options.resolution && this.options.resolution !== "normal") {
|
|
952
|
+
if (this.options.debug) {
|
|
953
|
+
console.log(`Restoring resolution "${this.options.resolution}" after quality change`);
|
|
917
954
|
}
|
|
955
|
+
// Small delay to ensure video element is ready
|
|
956
|
+
setTimeout(() => {
|
|
957
|
+
this.setResolution(this.options.resolution);
|
|
958
|
+
}, 150);
|
|
918
959
|
}
|
|
960
|
+
}
|
|
919
961
|
|
|
920
962
|
// Quality methods for main class
|
|
921
|
-
// All original functionality preserved exactly
|
|
963
|
+
// All original functionality preserved exactly
|