myetv-player 1.2.0 → 1.4.0
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/css/myetv-player.css +242 -168
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +638 -203
- package/dist/myetv-player.min.js +548 -170
- package/package.json +35 -16
- package/plugins/twitch/myetv-player-twitch-plugin.js +125 -11
- package/plugins/vimeo/myetv-player-vimeo.js +80 -49
- package/plugins/youtube/README.md +5 -2
- package/plugins/youtube/myetv-player-youtube-plugin.js +766 -6
- package/.github/workflows/codeql.yml +0 -100
- package/.github/workflows/npm-publish.yml +0 -30
- package/SECURITY.md +0 -50
- package/build.js +0 -195
- package/scss/README.md +0 -161
- package/scss/_audio-player.scss +0 -21
- package/scss/_base.scss +0 -116
- package/scss/_controls.scss +0 -204
- package/scss/_loading.scss +0 -111
- package/scss/_menus.scss +0 -432
- package/scss/_mixins.scss +0 -112
- package/scss/_poster.scss +0 -8
- package/scss/_progress-bar.scss +0 -319
- package/scss/_resolution.scss +0 -68
- package/scss/_responsive.scss +0 -1368
- package/scss/_themes.scss +0 -30
- package/scss/_title-overlay.scss +0 -60
- package/scss/_tooltips.scss +0 -7
- package/scss/_variables.scss +0 -49
- package/scss/_video.scss +0 -221
- package/scss/_volume.scss +0 -122
- package/scss/_watermark.scss +0 -128
- package/scss/myetv-player.scss +0 -51
- package/scss/package.json +0 -16
- package/src/README.md +0 -560
- package/src/chapters.js +0 -521
- package/src/controls.js +0 -1242
- package/src/core.js +0 -1922
- package/src/events.js +0 -537
- package/src/fullscreen.js +0 -82
- package/src/i18n.js +0 -374
- package/src/playlist.js +0 -177
- package/src/plugins.js +0 -384
- package/src/quality.js +0 -963
- package/src/streaming.js +0 -346
- package/src/subtitles.js +0 -524
- package/src/utils.js +0 -65
- package/src/watermark.js +0 -246
package/src/events.js
DELETED
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
// Events Module for MYETV Video Player
|
|
2
|
-
// Conservative modularization - original code preserved exactly
|
|
3
|
-
// Created by https://www.myetv.tv https://oskarcosimo.com
|
|
4
|
-
|
|
5
|
-
addEventListener(eventType, callback) {
|
|
6
|
-
if (typeof callback !== 'function') {
|
|
7
|
-
if (this.options.debug) console.warn(`Callback for event '${eventType}' is not a function`);
|
|
8
|
-
return this;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
if (!this.eventCallbacks[eventType]) {
|
|
12
|
-
this.eventCallbacks[eventType] = [];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
this.eventCallbacks[eventType].push(callback);
|
|
16
|
-
if (this.options.debug) console.log(`Event '${eventType}' registered`);
|
|
17
|
-
return this;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
removeEventListener(eventType, callback) {
|
|
21
|
-
if (!this.eventCallbacks[eventType]) return this;
|
|
22
|
-
|
|
23
|
-
const index = this.eventCallbacks[eventType].indexOf(callback);
|
|
24
|
-
if (index > -1) {
|
|
25
|
-
this.eventCallbacks[eventType].splice(index, 1);
|
|
26
|
-
if (this.options.debug) console.log(`Event '${eventType}' removed`);
|
|
27
|
-
}
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
triggerEvent(eventType, data = {}) {
|
|
32
|
-
if (!this.eventCallbacks[eventType]) return;
|
|
33
|
-
|
|
34
|
-
this.eventCallbacks[eventType].forEach(callback => {
|
|
35
|
-
try {
|
|
36
|
-
callback({
|
|
37
|
-
type: eventType,
|
|
38
|
-
timestamp: Date.now(),
|
|
39
|
-
player: this,
|
|
40
|
-
...data
|
|
41
|
-
});
|
|
42
|
-
} catch (error) {
|
|
43
|
-
if (this.options.debug) console.error(`Error in event '${eventType}':`, error);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getEventData() {
|
|
49
|
-
const state = this.getPlayerState();
|
|
50
|
-
return {
|
|
51
|
-
played: state.isPlaying,
|
|
52
|
-
paused: state.isPaused,
|
|
53
|
-
subtitleEnabled: state.subtitlesEnabled,
|
|
54
|
-
pipMode: state.isPictureInPicture,
|
|
55
|
-
fullscreenMode: state.isFullscreen,
|
|
56
|
-
speed: state.playbackRate,
|
|
57
|
-
controlBarLength: state.currentTime,
|
|
58
|
-
volumeIsMuted: state.isMuted,
|
|
59
|
-
// Additional useful data
|
|
60
|
-
duration: state.duration,
|
|
61
|
-
volume: state.volume,
|
|
62
|
-
quality: state.currentQuality,
|
|
63
|
-
buffered: this.getBufferedTime()
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
bindSubtitleEvents() {
|
|
68
|
-
if (this.video.textTracks) {
|
|
69
|
-
for (let i = 0; i < this.video.textTracks.length; i++) {
|
|
70
|
-
const track = this.video.textTracks[i];
|
|
71
|
-
|
|
72
|
-
track.addEventListener('cuechange', () => {
|
|
73
|
-
if (this.options.debug) console.log('Cue change detected:', track.activeCues);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
setupVolumeTooltipEvents() {
|
|
80
|
-
const volumeSlider = this.controls?.querySelector('.volume-slider');
|
|
81
|
-
if (!volumeSlider) return;
|
|
82
|
-
|
|
83
|
-
let isMouseOverVolume = false;
|
|
84
|
-
let isDragging = false;
|
|
85
|
-
|
|
86
|
-
volumeSlider.addEventListener('mouseenter', () => {
|
|
87
|
-
isMouseOverVolume = true;
|
|
88
|
-
// IMPORTANT: Set position FIRST, then show tooltip
|
|
89
|
-
this.updateVolumeTooltipPosition(this.video.volume);
|
|
90
|
-
this.updateVolumeTooltip();
|
|
91
|
-
if (this.volumeTooltip) {
|
|
92
|
-
this.volumeTooltip.classList.add('visible');
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
volumeSlider.addEventListener('mouseleave', () => {
|
|
97
|
-
isMouseOverVolume = false;
|
|
98
|
-
if (!isDragging) {
|
|
99
|
-
setTimeout(() => {
|
|
100
|
-
if (!isMouseOverVolume && !isDragging && this.volumeTooltip) {
|
|
101
|
-
this.volumeTooltip.classList.remove('visible');
|
|
102
|
-
}
|
|
103
|
-
}, 150);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
volumeSlider.addEventListener('mousemove', (e) => {
|
|
108
|
-
if (isMouseOverVolume && this.volumeTooltip && !isDragging) {
|
|
109
|
-
const rect = volumeSlider.getBoundingClientRect();
|
|
110
|
-
const offsetX = e.clientX - rect.left;
|
|
111
|
-
const sliderWidth = rect.width;
|
|
112
|
-
|
|
113
|
-
const thumbSize = 14;
|
|
114
|
-
const availableWidth = sliderWidth - thumbSize;
|
|
115
|
-
let volumeAtPosition = (offsetX - thumbSize / 2) / availableWidth;
|
|
116
|
-
volumeAtPosition = Math.max(0, Math.min(1, volumeAtPosition));
|
|
117
|
-
|
|
118
|
-
const hoverVolume = Math.round(volumeAtPosition * 100);
|
|
119
|
-
|
|
120
|
-
// Set position first, then update text to avoid flicker
|
|
121
|
-
this.updateVolumeTooltipPosition(volumeAtPosition);
|
|
122
|
-
this.volumeTooltip.textContent = hoverVolume + '%';
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// During dragging - set position immediately
|
|
127
|
-
volumeSlider.addEventListener('mousedown', () => {
|
|
128
|
-
isDragging = true;
|
|
129
|
-
if (this.volumeTooltip) {
|
|
130
|
-
// Ensure position is set before showing
|
|
131
|
-
this.updateVolumeTooltipPosition(this.video.volume);
|
|
132
|
-
this.volumeTooltip.classList.add('visible');
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
document.addEventListener('mouseup', () => {
|
|
137
|
-
if (isDragging) {
|
|
138
|
-
isDragging = false;
|
|
139
|
-
setTimeout(() => {
|
|
140
|
-
if (!isMouseOverVolume && this.volumeTooltip) {
|
|
141
|
-
this.volumeTooltip.classList.remove('visible');
|
|
142
|
-
}
|
|
143
|
-
}, 500);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
volumeSlider.addEventListener('input', (e) => {
|
|
148
|
-
const volumeValue = parseFloat(e.target.value);
|
|
149
|
-
const volume = Math.round(volumeValue * 100);
|
|
150
|
-
if (this.volumeTooltip) {
|
|
151
|
-
// Update position first, then text
|
|
152
|
-
this.updateVolumeTooltipPosition(volumeValue);
|
|
153
|
-
this.volumeTooltip.textContent = volume + '%';
|
|
154
|
-
}
|
|
155
|
-
isDragging = true;
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
volumeSlider.addEventListener('change', () => {
|
|
159
|
-
// Ensure final position is correct
|
|
160
|
-
this.updateVolumeTooltip();
|
|
161
|
-
setTimeout(() => {
|
|
162
|
-
isDragging = false;
|
|
163
|
-
}, 100);
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
bindEvents() {
|
|
168
|
-
if (this.video) {
|
|
169
|
-
|
|
170
|
-
// Playback events
|
|
171
|
-
this.video.addEventListener('playing', () => {
|
|
172
|
-
this.hideLoading();
|
|
173
|
-
this.closeAllMenus();
|
|
174
|
-
|
|
175
|
-
// Update play/pause button when video actually starts playing
|
|
176
|
-
if (this.playIcon && this.pauseIcon) {
|
|
177
|
-
this.playIcon.classList.add('hidden');
|
|
178
|
-
this.pauseIcon.classList.remove('hidden');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Trigger playing event - video is now actually playing
|
|
182
|
-
this.triggerEvent('playing', {
|
|
183
|
-
currentTime: this.getCurrentTime(),
|
|
184
|
-
duration: this.getDuration()
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
this.video.addEventListener('waiting', () => {
|
|
189
|
-
if (!this.isChangingQuality) {
|
|
190
|
-
this.showLoading();
|
|
191
|
-
// Trigger waiting event - video is buffering
|
|
192
|
-
this.triggerEvent('waiting', {
|
|
193
|
-
currentTime: this.getCurrentTime()
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
this.video.addEventListener('seeking', () => {
|
|
199
|
-
// Trigger seeking event - seek operation started
|
|
200
|
-
this.triggerEvent('seeking', {
|
|
201
|
-
currentTime: this.getCurrentTime(),
|
|
202
|
-
targetTime: this.video.currentTime
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
this.video.addEventListener('seeked', () => {
|
|
207
|
-
// Trigger seeked event - seek operation completed
|
|
208
|
-
this.triggerEvent('seeked', {
|
|
209
|
-
currentTime: this.getCurrentTime()
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// Loading events
|
|
214
|
-
this.video.addEventListener('loadstart', () => {
|
|
215
|
-
if (!this.isChangingQuality) {
|
|
216
|
-
this.showLoading();
|
|
217
|
-
}
|
|
218
|
-
// Trigger loadstart event - browser started loading media
|
|
219
|
-
this.triggerEvent('loadstart');
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
this.video.addEventListener('loadedmetadata', () => {
|
|
223
|
-
this.updateDuration();
|
|
224
|
-
|
|
225
|
-
// Trigger loadedmetadata event - video metadata loaded
|
|
226
|
-
this.triggerEvent('loadedmetadata', {
|
|
227
|
-
duration: this.getDuration(),
|
|
228
|
-
videoWidth: this.video.videoWidth,
|
|
229
|
-
videoHeight: this.video.videoHeight
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// Initialize subtitles after metadata is loaded
|
|
233
|
-
setTimeout(() => {
|
|
234
|
-
this.initializeSubtitles();
|
|
235
|
-
}, 100);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
this.video.addEventListener('loadeddata', () => {
|
|
239
|
-
if (!this.isChangingQuality) {
|
|
240
|
-
this.hideLoading();
|
|
241
|
-
}
|
|
242
|
-
// Trigger loadeddata event - current frame data loaded
|
|
243
|
-
this.triggerEvent('loadeddata', {
|
|
244
|
-
currentTime: this.getCurrentTime()
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
this.video.addEventListener('canplay', () => {
|
|
249
|
-
if (!this.isChangingQuality) {
|
|
250
|
-
this.hideLoading();
|
|
251
|
-
}
|
|
252
|
-
// Trigger canplay event - video can start playing
|
|
253
|
-
this.triggerEvent('canplay', {
|
|
254
|
-
currentTime: this.getCurrentTime(),
|
|
255
|
-
duration: this.getDuration()
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
this.video.addEventListener('progress', () => {
|
|
260
|
-
this.updateBuffer();
|
|
261
|
-
// Trigger progress event - browser is downloading media
|
|
262
|
-
this.triggerEvent('progress', {
|
|
263
|
-
buffered: this.getBufferedTime(),
|
|
264
|
-
duration: this.getDuration()
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
this.video.addEventListener('durationchange', () => {
|
|
269
|
-
this.updateDuration();
|
|
270
|
-
// Trigger durationchange event - video duration changed
|
|
271
|
-
this.triggerEvent('durationchange', {
|
|
272
|
-
duration: this.getDuration()
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Error events
|
|
277
|
-
this.video.addEventListener('error', (e) => {
|
|
278
|
-
this.onVideoError(e);
|
|
279
|
-
// Trigger error event - media loading/playback error occurred
|
|
280
|
-
this.triggerEvent('error', {
|
|
281
|
-
code: this.video.error?.code,
|
|
282
|
-
message: this.video.error?.message,
|
|
283
|
-
src: this.video.currentSrc || this.video.src
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
this.video.addEventListener('stalled', () => {
|
|
288
|
-
// Trigger stalled event - browser is trying to fetch data but it's not available
|
|
289
|
-
this.triggerEvent('stalled', {
|
|
290
|
-
currentTime: this.getCurrentTime()
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.video.addEventListener('timeupdate', () => this.updateProgress());
|
|
296
|
-
|
|
297
|
-
this.video.addEventListener('ended', () => this.onVideoEnded());
|
|
298
|
-
|
|
299
|
-
// Complete video click logic with doubleTapPause support (DESKTOP)
|
|
300
|
-
this.video.addEventListener('click', () => {
|
|
301
|
-
if (!this.options.pauseClick) return;
|
|
302
|
-
|
|
303
|
-
if (this.options.doubleTapPause) {
|
|
304
|
-
// DOUBLE TAP MODE: primo click mostra controlli, secondo pausa
|
|
305
|
-
const controlsVisible = this.controls && this.controls.classList.contains('show');
|
|
306
|
-
|
|
307
|
-
if (controlsVisible) {
|
|
308
|
-
// Controlbar VISIBILE - pausa video
|
|
309
|
-
this.togglePlayPause();
|
|
310
|
-
} else {
|
|
311
|
-
// Controlbar NASCOSTA - solo mostra controlli
|
|
312
|
-
this.showControlsNow();
|
|
313
|
-
this.resetAutoHideTimer();
|
|
314
|
-
}
|
|
315
|
-
} else {
|
|
316
|
-
// NORMAL MODE: sempre pausa (comportamento originale)
|
|
317
|
-
this.togglePlayPause();
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
this.video.addEventListener('volumechange', () => this.updateVolumeSliderVisual());
|
|
321
|
-
|
|
322
|
-
// Complete touch logic with doubleTapPause support (MOBILE)
|
|
323
|
-
this.video.addEventListener('touchend', (e) => {
|
|
324
|
-
// Prevent click event from firing after touchend
|
|
325
|
-
e.preventDefault();
|
|
326
|
-
|
|
327
|
-
if (!this.options.pauseClick) return;
|
|
328
|
-
|
|
329
|
-
if (this.options.doubleTapPause) {
|
|
330
|
-
// DOUBLE TAP MODE: primo tap mostra controlli, secondo pausa (SAME as desktop)
|
|
331
|
-
const controlsVisible = this.controls && this.controls.classList.contains('show');
|
|
332
|
-
|
|
333
|
-
if (controlsVisible) {
|
|
334
|
-
// Controlbar VISIBILE - pausa video
|
|
335
|
-
this.togglePlayPause();
|
|
336
|
-
} else {
|
|
337
|
-
// Controlbar NASCOSTA - solo mostra controlli
|
|
338
|
-
this.showControlsNow();
|
|
339
|
-
this.resetAutoHideTimer();
|
|
340
|
-
}
|
|
341
|
-
} else {
|
|
342
|
-
// NORMAL MODE: sempre pausa (comportamento originale, SAME as desktop)
|
|
343
|
-
this.togglePlayPause();
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// CRITICAL: Start auto-hide when video starts playing
|
|
348
|
-
this.video.addEventListener('play', () => {
|
|
349
|
-
if (this.options.autoHide && this.autoHideInitialized) {
|
|
350
|
-
this.showControlsNow();
|
|
351
|
-
this.resetAutoHideTimer();
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
// Picture-in-Picture Events
|
|
356
|
-
this.video.addEventListener('enterpictureinpicture', () => {
|
|
357
|
-
this.onEnterPiP();
|
|
358
|
-
this.triggerEvent('pipchange', {
|
|
359
|
-
active: true,
|
|
360
|
-
mode: 'enter'
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
this.video.addEventListener('leavepictureinpicture', () => {
|
|
365
|
-
this.onLeavePiP();
|
|
366
|
-
this.triggerEvent('pipchange', {
|
|
367
|
-
active: false,
|
|
368
|
-
mode: 'exit'
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (this.playPauseBtn) {
|
|
374
|
-
this.playPauseBtn.addEventListener('click', () => this.togglePlayPause());
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (this.muteBtn) {
|
|
378
|
-
this.muteBtn.addEventListener('click', () => this.toggleMute());
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (this.fullscreenBtn) {
|
|
382
|
-
this.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (this.pipBtn) {
|
|
386
|
-
this.pipBtn.addEventListener('click', () => this.togglePictureInPicture());
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
if (this.volumeSlider) {
|
|
390
|
-
let isDraggingVolume = false;
|
|
391
|
-
|
|
392
|
-
// Input event
|
|
393
|
-
this.volumeSlider.addEventListener('input', (e) => {
|
|
394
|
-
this.updateVolume(e.target.value);
|
|
395
|
-
this.updateVolumeSliderVisual();
|
|
396
|
-
this.initVolumeTooltip();
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
// MOUSE DRAG - Start
|
|
400
|
-
this.volumeSlider.addEventListener('mousedown', (e) => {
|
|
401
|
-
isDraggingVolume = true;
|
|
402
|
-
if (this.volumeTooltip) {
|
|
403
|
-
this.volumeTooltip.classList.add('visible');
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
// MOUSE DRAG - Move
|
|
408
|
-
document.addEventListener('mousemove', (e) => {
|
|
409
|
-
if (isDraggingVolume && this.volumeSlider) {
|
|
410
|
-
const rect = this.volumeSlider.getBoundingClientRect();
|
|
411
|
-
const clickX = e.clientX - rect.left;
|
|
412
|
-
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
413
|
-
const value = Math.round(percentage * 100);
|
|
414
|
-
|
|
415
|
-
this.volumeSlider.value = value;
|
|
416
|
-
this.updateVolume(value);
|
|
417
|
-
this.updateVolumeSliderVisual();
|
|
418
|
-
if (this.volumeTooltip) {
|
|
419
|
-
this.updateVolumeTooltipPosition(value / 100);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
// MOUSE DRAG - End
|
|
425
|
-
document.addEventListener('mouseup', () => {
|
|
426
|
-
if (isDraggingVolume) {
|
|
427
|
-
isDraggingVolume = false;
|
|
428
|
-
if (this.volumeTooltip) {
|
|
429
|
-
setTimeout(() => {
|
|
430
|
-
this.volumeTooltip.classList.remove('visible');
|
|
431
|
-
}, 300);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
// TOUCH DRAG - Start
|
|
437
|
-
this.volumeSlider.addEventListener('touchstart', (e) => {
|
|
438
|
-
isDraggingVolume = true;
|
|
439
|
-
if (this.volumeTooltip) {
|
|
440
|
-
this.volumeTooltip.classList.add('visible');
|
|
441
|
-
}
|
|
442
|
-
}, { passive: true });
|
|
443
|
-
|
|
444
|
-
// TOUCH DRAG - Move
|
|
445
|
-
this.volumeSlider.addEventListener('touchmove', (e) => {
|
|
446
|
-
if (isDraggingVolume) {
|
|
447
|
-
const touch = e.touches[0];
|
|
448
|
-
const rect = this.volumeSlider.getBoundingClientRect();
|
|
449
|
-
const touchX = touch.clientX - rect.left;
|
|
450
|
-
const percentage = Math.max(0, Math.min(1, touchX / rect.width));
|
|
451
|
-
const value = Math.round(percentage * 100);
|
|
452
|
-
|
|
453
|
-
this.volumeSlider.value = value;
|
|
454
|
-
this.updateVolume(value);
|
|
455
|
-
this.updateVolumeSliderVisual();
|
|
456
|
-
if (this.volumeTooltip) {
|
|
457
|
-
this.updateVolumeTooltipPosition(value / 100);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}, { passive: true });
|
|
461
|
-
|
|
462
|
-
// TOUCH DRAG - End
|
|
463
|
-
this.volumeSlider.addEventListener('touchend', () => {
|
|
464
|
-
if (isDraggingVolume) {
|
|
465
|
-
isDraggingVolume = false;
|
|
466
|
-
if (this.volumeTooltip) {
|
|
467
|
-
setTimeout(() => {
|
|
468
|
-
this.volumeTooltip.classList.remove('visible');
|
|
469
|
-
}, 300);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}, { passive: true });
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
if (this.progressContainer) {
|
|
476
|
-
// Mouse events (desktop)
|
|
477
|
-
this.progressContainer.addEventListener('click', (e) => this.seek(e));
|
|
478
|
-
this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
|
|
479
|
-
if (this.progressHandle) {
|
|
480
|
-
this.progressHandle.addEventListener('mousedown', this.startSeeking.bind(this));
|
|
481
|
-
this.progressHandle.addEventListener('touchstart', this.startSeeking.bind(this), { passive: false });
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Touch events (mobile)
|
|
485
|
-
this.progressContainer.addEventListener('touchstart', (e) => {
|
|
486
|
-
e.preventDefault(); // Prevent scrolling when touching the seek bar
|
|
487
|
-
this.startSeeking(e);
|
|
488
|
-
}, { passive: false });
|
|
489
|
-
|
|
490
|
-
this.setupSeekTooltip();
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Add touch events directly on the handle for better mobile dragging
|
|
494
|
-
if (this.progressHandle) {
|
|
495
|
-
this.progressHandle.addEventListener('touchstart', (e) => {
|
|
496
|
-
e.preventDefault(); // Prevent default touch behavior
|
|
497
|
-
e.stopPropagation(); // Stop event from bubbling to progressContainer
|
|
498
|
-
this.startSeeking(e);
|
|
499
|
-
}, { passive: false });
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// NOTE: Auto-hide events are handled in initAutoHide() after everything is ready
|
|
503
|
-
|
|
504
|
-
if (this.speedMenu) {
|
|
505
|
-
this.speedMenu.addEventListener('click', (e) => this.changeSpeed(e));
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (this.qualityMenu) {
|
|
509
|
-
this.qualityMenu.addEventListener('click', (e) => this.changeQuality(e));
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (this.subtitlesMenu) {
|
|
513
|
-
this.subtitlesMenu.addEventListener('click', (e) => this.handleSubtitlesMenuClick(e));
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
document.addEventListener('fullscreenchange', () => this.updateFullscreenButton());
|
|
517
|
-
document.addEventListener('webkitfullscreenchange', () => this.updateFullscreenButton());
|
|
518
|
-
document.addEventListener('mozfullscreenchange', () => this.updateFullscreenButton());
|
|
519
|
-
|
|
520
|
-
document.addEventListener('mousemove', (e) => this.continueSeeking(e));
|
|
521
|
-
document.addEventListener('mouseup', () => this.endSeeking());
|
|
522
|
-
|
|
523
|
-
// Touch events for seeking (mobile)
|
|
524
|
-
document.addEventListener('touchmove', (e) => {
|
|
525
|
-
if (this.isUserSeeking) {
|
|
526
|
-
e.preventDefault(); // Prevent scrolling while seeking
|
|
527
|
-
this.continueSeeking(e);
|
|
528
|
-
}
|
|
529
|
-
}, { passive: false });
|
|
530
|
-
|
|
531
|
-
document.addEventListener('touchend', () => this.endSeeking());
|
|
532
|
-
document.addEventListener('touchcancel', () => this.endSeeking());
|
|
533
|
-
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Events methods for main class
|
|
537
|
-
// All original functionality preserved exactly
|
package/src/fullscreen.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
// Fullscreen Module for MYETV Video Player
|
|
2
|
-
// Conservative modularization - original code preserved exactly
|
|
3
|
-
// Created by https://www.myetv.tv https://oskarcosimo.com
|
|
4
|
-
|
|
5
|
-
isFullscreenActive() {
|
|
6
|
-
return !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
checkPiPSupport() {
|
|
10
|
-
return 'pictureInPictureEnabled' in document;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
enterFullscreen() {
|
|
14
|
-
const element = this.container.parentElement || this.container;
|
|
15
|
-
|
|
16
|
-
if (element.requestFullscreen) {
|
|
17
|
-
element.requestFullscreen();
|
|
18
|
-
} else if (element.webkitRequestFullscreen) {
|
|
19
|
-
element.webkitRequestFullscreen();
|
|
20
|
-
} else if (element.mozRequestFullScreen) {
|
|
21
|
-
element.mozRequestFullScreen();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
exitFullscreen() {
|
|
26
|
-
if (document.exitFullscreen) {
|
|
27
|
-
document.exitFullscreen();
|
|
28
|
-
} else if (document.webkitExitFullscreen) {
|
|
29
|
-
document.webkitExitFullscreen();
|
|
30
|
-
} else if (document.mozCancelFullScreen) {
|
|
31
|
-
document.mozCancelFullScreen();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async enterPictureInPicture() {
|
|
36
|
-
if (!this.isPiPSupported || !this.video) return;
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
await this.video.requestPictureInPicture();
|
|
40
|
-
} catch (error) {
|
|
41
|
-
if (this.options.debug) console.error('Errore avvio Picture-in-Picture:', error);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async exitPictureInPicture() {
|
|
46
|
-
if (!this.isPiPSupported) return;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
await document.exitPictureInPicture();
|
|
50
|
-
} catch (error) {
|
|
51
|
-
if (this.options.debug) console.error('Errore uscita Picture-in-Picture:', error);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
onEnterPiP() {
|
|
56
|
-
if (this.pipIcon) this.pipIcon.classList.add('hidden');
|
|
57
|
-
if (this.pipExitIcon) this.pipExitIcon.classList.remove('hidden');
|
|
58
|
-
|
|
59
|
-
if (this.controls) {
|
|
60
|
-
this.controls.style.opacity = '0';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (this.titleOverlay) {
|
|
64
|
-
this.titleOverlay.style.opacity = '0';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
onLeavePiP() {
|
|
69
|
-
if (this.pipIcon) this.pipIcon.classList.remove('hidden');
|
|
70
|
-
if (this.pipExitIcon) this.pipExitIcon.classList.add('hidden');
|
|
71
|
-
|
|
72
|
-
if (this.controls) {
|
|
73
|
-
this.controls.style.opacity = '';
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (this.titleOverlay) {
|
|
77
|
-
this.titleOverlay.style.opacity = '';
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Fullscreen methods for main class
|
|
82
|
-
// All original functionality preserved exactly
|