myetv-player 1.4.0 → 1.6.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.
@@ -0,0 +1,927 @@
1
+ /**
2
+ * SoundCloud Plugin for MYETV Video Player
3
+ * Adds SoundCloud integration with fixed logo in controlbar
4
+ * Created by https://www.myetv.tv - https://oskarcosimo.com
5
+ */
6
+
7
+ (function () {
8
+ 'use strict';
9
+
10
+ /**
11
+ * SoundCloud Plugin Class
12
+ */
13
+ class SoundCloudPlugin {
14
+ constructor(player, options = {}) {
15
+ this.player = player;
16
+ this.options = {
17
+ enabled: true,
18
+ soundcloudUrl: '', // SoundCloud track URL
19
+ controlsDisplayTime: 10000, // Time to show SoundCloud controls (10 seconds)
20
+
21
+ // SoundCloud embed options
22
+ color: 'ff5500', // Player color (hex without #)
23
+ autoPlay: false, // Auto play track
24
+ hideRelated: true, // Hide related tracks
25
+ showComments: false, // Show comments
26
+ showUser: true, // Show user info
27
+ showReposts: false, // Show reposts
28
+ showTeaser: false, // Show teaser
29
+ visualMode: false, // Visual mode (waveform)
30
+ showArtwork: true, // Show artwork
31
+ buying: false, // Show buy button
32
+ sharing: false, // Show share button
33
+ download: false, // Show download button
34
+ showPlaycount: false, // Show play count
35
+
36
+ debug: false,
37
+ ...options
38
+ };
39
+
40
+ this.soundcloudLogo = null;
41
+ this.soundcloudContainer = null;
42
+ this.soundcloudIframe = null;
43
+ this.invisibleOverlay = null;
44
+ this.soundcloudControlsTimeout = null;
45
+ this.widget = null;
46
+ this.isInitialized = false;
47
+ this.isPlaying = false;
48
+ this.duration = 0;
49
+ this.currentTime = 0;
50
+ this.lastVolume = 1.0;
51
+ this.wasAutoHideEnabled = false;
52
+ this.mouseBlocker = null;
53
+ this.volumeCheckInterval = null; // Track volume polling interval
54
+ this.tooltipUpdateInterval = null;
55
+ this.api = player.getPluginAPI ? player.getPluginAPI() : null;
56
+
57
+ if (this.options.debug) {
58
+ console.log('SoundCloud Plugin initialized with options:', this.options);
59
+ }
60
+
61
+ // Load SoundCloud Widget API
62
+ this.loadSoundCloudWidgetAPI(() => {
63
+ setTimeout(() => {
64
+ this.setup();
65
+ }, 100);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Load SoundCloud Widget API
71
+ */
72
+ loadSoundCloudWidgetAPI(callback) {
73
+ if (window.SC && window.SC.Widget) {
74
+ if (this.options.debug) {
75
+ console.log('SoundCloud Widget API already loaded');
76
+ }
77
+ callback();
78
+ return;
79
+ }
80
+
81
+ const script = document.createElement('script');
82
+ script.src = 'https://w.soundcloud.com/player/api.js';
83
+ script.onload = () => {
84
+ if (this.options.debug) {
85
+ console.log('SoundCloud Widget API loaded');
86
+ }
87
+ callback();
88
+ };
89
+ document.head.appendChild(script);
90
+ }
91
+
92
+ /**
93
+ * Setup the plugin
94
+ */
95
+ setup() {
96
+ if (!this.options.enabled) {
97
+ if (this.options.debug) {
98
+ console.log('SoundCloud Plugin is disabled');
99
+ }
100
+ return;
101
+ }
102
+
103
+ try {
104
+ // Create SoundCloud player (always visible)
105
+ if (this.options.soundcloudUrl) {
106
+ this.createPermanentSoundCloudPlayer();
107
+ this.addSoundCloudLogo();
108
+ }
109
+
110
+ // Intercept play/pause buttons
111
+ this.interceptPlayPauseButtons();
112
+
113
+ // Intercept volume controls
114
+ this.interceptVolumeControls();
115
+
116
+ // Intercept progress bar
117
+ this.interceptProgressBar();
118
+
119
+ this.hideSpeedPiPButtons();
120
+
121
+ this.isInitialized = true;
122
+
123
+ if (this.options.debug) {
124
+ console.log('SoundCloud Plugin setup completed');
125
+ }
126
+ } catch (error) {
127
+ console.error('SoundCloud Plugin setup error:', error);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Create permanent SoundCloud player (always visible)
133
+ */
134
+ createPermanentSoundCloudPlayer() {
135
+ const container = this.api ? this.api.container : this.player.container;
136
+ if (!container) return;
137
+
138
+ const embedUrl = this.buildEmbedUrl(false);
139
+ if (!embedUrl) return;
140
+
141
+ // Create container for SoundCloud
142
+ const soundcloudContainer = document.createElement('div');
143
+ soundcloudContainer.className = 'soundcloud-container';
144
+ soundcloudContainer.style.cssText = `
145
+ position: absolute;
146
+ top: 0;
147
+ left: 0;
148
+ width: 100%;
149
+ height: 100%;
150
+ z-index: 1;
151
+ background: #000;
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ padding: 20px;
156
+ box-sizing: border-box;
157
+ `;
158
+
159
+ // Create iframe
160
+ const iframe = document.createElement('iframe');
161
+ iframe.width = '100%';
162
+ iframe.height = this.options.visualMode ? '450' : '166';
163
+ iframe.scrolling = 'no';
164
+ iframe.frameBorder = 'no';
165
+ iframe.allow = 'autoplay';
166
+ iframe.src = embedUrl;
167
+ iframe.id = 'soundcloud-iframe-' + Date.now();
168
+ iframe.style.cssText = `
169
+ max-width: ${this.options.visualMode ? '800px' : '600px'};
170
+ border: none;
171
+ border-radius: 8px;
172
+ `;
173
+
174
+ soundcloudContainer.appendChild(iframe);
175
+
176
+ // Create invisible overlay
177
+ const invisibleOverlay = document.createElement('div');
178
+ invisibleOverlay.className = 'soundcloud-invisible-overlay';
179
+ invisibleOverlay.style.cssText = `
180
+ position: absolute;
181
+ top: 0;
182
+ left: 0;
183
+ width: 100%;
184
+ height: 100%;
185
+ z-index: 2;
186
+ background: transparent;
187
+ cursor: pointer;
188
+ pointer-events: auto;
189
+ transition: opacity 0.3s ease;
190
+ `;
191
+
192
+ // Click on overlay toggles play/pause
193
+ invisibleOverlay.addEventListener('click', (e) => {
194
+ e.preventDefault();
195
+ e.stopPropagation();
196
+ this.togglePlayPause();
197
+ });
198
+
199
+ // Get controls element
200
+ const controls = container.querySelector('.controls');
201
+
202
+ // Insert BEFORE controls
203
+ if (controls) {
204
+ container.insertBefore(soundcloudContainer, controls);
205
+ container.insertBefore(invisibleOverlay, controls);
206
+ controls.style.zIndex = '10';
207
+ controls.style.position = 'absolute';
208
+ controls.style.pointerEvents = 'auto';
209
+ } else {
210
+ container.appendChild(soundcloudContainer);
211
+ container.appendChild(invisibleOverlay);
212
+ }
213
+
214
+ this.soundcloudContainer = soundcloudContainer;
215
+ this.soundcloudIframe = iframe;
216
+ this.invisibleOverlay = invisibleOverlay;
217
+
218
+ // Initialize Widget API
219
+ setTimeout(() => {
220
+ this.initializeWidget();
221
+ }, 1000);
222
+
223
+ if (this.options.debug) {
224
+ console.log('SoundCloud Plugin: Permanent player created');
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Initialize SoundCloud Widget
230
+ */
231
+ initializeWidget() {
232
+ if (!window.SC || !window.SC.Widget) {
233
+ console.error('SoundCloud Widget API not available');
234
+ return;
235
+ }
236
+
237
+ this.widget = window.SC.Widget(this.soundcloudIframe);
238
+
239
+ // Bind widget events
240
+ this.widget.bind(window.SC.Widget.Events.READY, () => {
241
+ if (this.options.debug) {
242
+ console.log('SoundCloud Widget ready');
243
+ }
244
+
245
+ // Set initial volume to 100%
246
+ this.widget.setVolume(100);
247
+
248
+ if (this.options.debug) {
249
+ console.log('Widget volume set to 100%');
250
+ }
251
+
252
+ // Get duration
253
+ this.widget.getDuration((duration) => {
254
+ this.duration = duration / 1000;
255
+ this.updateDurationDisplay();
256
+ });
257
+
258
+ if (this.soundcloudIframe) {
259
+ const logoContainer = this.soundcloudIframe.parentElement;
260
+ const logoIcon = this.soundcloudIframe.contentDocument?.querySelector('svg, img, .sc-logo');
261
+
262
+ if (logoContainer) {
263
+ logoContainer.style.marginRight = '4px';
264
+ logoContainer.style.paddingLeft = '2px';
265
+ }
266
+
267
+ if (logoIcon) {
268
+ logoIcon.style.marginLeft = '0';
269
+ logoIcon.style.paddingLeft = '0';
270
+ logoIcon.style.display = 'block';
271
+ logoIcon.style.maxHeight = '32px';
272
+ logoIcon.style.width = 'auto';
273
+ }
274
+
275
+ if (this.options.debug) {
276
+ console.log('🎨 SoundCloud logo container and icon spacing fixed');
277
+ }
278
+ }
279
+
280
+ // Listen to play/pause events
281
+ this.widget.bind(window.SC.Widget.Events.PLAY, () => {
282
+ this.isPlaying = true;
283
+ this.updatePlayPauseButton();
284
+ this.startTimeUpdate();
285
+ });
286
+
287
+ this.widget.bind(window.SC.Widget.Events.PAUSE, () => {
288
+ this.isPlaying = false;
289
+ this.updatePlayPauseButton();
290
+ this.stopTimeUpdate();
291
+ });
292
+
293
+ this.widget.bind(window.SC.Widget.Events.FINISH, () => {
294
+ this.isPlaying = false;
295
+ this.updatePlayPauseButton();
296
+ this.stopTimeUpdate();
297
+ });
298
+ });
299
+ }
300
+
301
+ /**
302
+ * Start time update interval
303
+ */
304
+ startTimeUpdate() {
305
+ this.stopTimeUpdate();
306
+
307
+ this.timeUpdateInterval = setInterval(() => {
308
+ if (this.widget) {
309
+ this.widget.getPosition((position) => {
310
+ this.currentTime = position / 1000;
311
+ this.updateTimeDisplay();
312
+ this.updateProgressBar();
313
+ });
314
+ }
315
+ }, 100);
316
+ }
317
+
318
+ /**
319
+ * Stop time update interval
320
+ */
321
+ stopTimeUpdate() {
322
+ if (this.timeUpdateInterval) {
323
+ clearInterval(this.timeUpdateInterval);
324
+ this.timeUpdateInterval = null;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Update duration display
330
+ */
331
+ updateDurationDisplay() {
332
+ const durationEl = this.player.controls?.querySelector('.duration');
333
+ if (durationEl) {
334
+ durationEl.textContent = this.formatTime(this.duration);
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Update time display
340
+ */
341
+ updateTimeDisplay() {
342
+ const currentTimeEl = this.player.controls?.querySelector('.current-time');
343
+ if (currentTimeEl) {
344
+ currentTimeEl.textContent = this.formatTime(this.currentTime);
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Update progress bar
350
+ */
351
+ updateProgressBar() {
352
+ if (this.duration === 0) return;
353
+
354
+ const percentage = (this.currentTime / this.duration) * 100;
355
+
356
+ const progressFilled = this.player.controls?.querySelector('.progress-filled');
357
+ if (progressFilled) {
358
+ progressFilled.style.width = percentage + '%';
359
+ }
360
+
361
+ const progressHandle = this.player.controls?.querySelector('.progress-handle');
362
+ if (progressHandle) {
363
+ progressHandle.style.left = percentage + '%';
364
+ }
365
+ }
366
+
367
+ hideSpeedPiPButtons() {
368
+ // speed
369
+ const speedElements = this.player.controls?.querySelectorAll(
370
+ '[data-speed], .speed-btn, .playback-speed, .speed-control, .playback-rate, .speed-menu, .speed-container, .speed-select'
371
+ );
372
+
373
+ speedElements?.forEach(el => {
374
+ el.style.display = 'none';
375
+ if (this.options.debug) console.log('Speed control HIDDEN');
376
+ });
377
+
378
+ // PiP
379
+ const pipElements = this.player.controls?.querySelectorAll(
380
+ '[data-pip], .pip-btn, .picture-in-picture, .pip-icon'
381
+ );
382
+
383
+ pipElements?.forEach(el => {
384
+ el.style.display = 'none';
385
+ if (this.options.debug) console.log('PiP control HIDDEN');
386
+ });
387
+ }
388
+
389
+ showSpeedPiPButtons() {
390
+ // SPEED - reenable
391
+ const speedElements = this.player.controls?.querySelectorAll(
392
+ '[data-speed], .speed-btn, .playback-speed, .speed-control, .playback-rate, .speed-menu, .speed-container, .speed-select'
393
+ );
394
+
395
+ speedElements?.forEach(el => {
396
+ el.style.display = '';
397
+ el.style.opacity = '1';
398
+ el.style.pointerEvents = 'auto';
399
+ });
400
+
401
+ // PiP
402
+ const pipElements = this.player.controls?.querySelectorAll(
403
+ '[data-pip], .pip-btn, .picture-in-picture, .pip-icon'
404
+ );
405
+
406
+ pipElements?.forEach(el => {
407
+ el.style.display = '';
408
+ el.style.opacity = '1';
409
+ el.style.pointerEvents = 'auto';
410
+ });
411
+ }
412
+
413
+ /**
414
+ * Update play/pause button
415
+ */
416
+ updatePlayPauseButton() {
417
+ const playIcon = this.player.controls?.querySelector('.play-icon');
418
+ const pauseIcon = this.player.controls?.querySelector('.pause-icon');
419
+
420
+ if (this.isPlaying) {
421
+ if (playIcon) playIcon.classList.add('hidden');
422
+ if (pauseIcon) pauseIcon.classList.remove('hidden');
423
+ } else {
424
+ if (playIcon) playIcon.classList.remove('hidden');
425
+ if (pauseIcon) pauseIcon.classList.add('hidden');
426
+ }
427
+ }
428
+
429
+ /**
430
+ * Format time in MM:SS
431
+ */
432
+ formatTime(seconds) {
433
+ if (isNaN(seconds) || seconds === 0) return '0:00';
434
+
435
+ const mins = Math.floor(seconds / 60);
436
+ const secs = Math.floor(seconds % 60);
437
+ return mins + ':' + (secs < 10 ? '0' : '') + secs;
438
+ }
439
+
440
+ /**
441
+ * Toggle play/pause
442
+ */
443
+ togglePlayPause() {
444
+ if (!this.widget) return;
445
+
446
+ if (this.isPlaying) {
447
+ this.widget.pause();
448
+ } else {
449
+ this.widget.play();
450
+ }
451
+ }
452
+
453
+ /**
454
+ * Intercept play/pause buttons
455
+ */
456
+ interceptPlayPauseButtons() {
457
+ const playPauseBtn = this.player.controls?.querySelector('.play-pause-btn');
458
+ if (!playPauseBtn) return;
459
+
460
+ playPauseBtn.addEventListener('click', (e) => {
461
+ e.stopPropagation();
462
+ e.preventDefault();
463
+ this.togglePlayPause();
464
+ }, true);
465
+ }
466
+
467
+ /**
468
+ * Intercept volume controls
469
+ */
470
+ interceptVolumeControls() {
471
+ const volumeSlider = this.player.controls?.querySelector('.volume-slider');
472
+ if (volumeSlider) {
473
+ const volumeHandler = (e) => {
474
+ const volume = parseFloat(e.target.value);
475
+
476
+ if (this.widget) {
477
+ this.widget.setVolume(volume);
478
+
479
+ if (this.options.debug) {
480
+ console.log('🎚️ Volume set to:', volume, '(0-1 range)');
481
+ }
482
+ }
483
+ };
484
+
485
+ volumeSlider.addEventListener('input', volumeHandler, false);
486
+ volumeSlider.addEventListener('change', volumeHandler, false);
487
+ volumeSlider.addEventListener('mouseup', volumeHandler, false);
488
+ }
489
+
490
+ // MUTE BUTTON
491
+ const muteBtn = this.player.controls?.querySelector('.mute-btn');
492
+ if (muteBtn) {
493
+ muteBtn.addEventListener('click', () => {
494
+ setTimeout(() => {
495
+ const volumeSlider = this.player.controls?.querySelector('.volume-slider');
496
+ if (volumeSlider && this.widget) {
497
+ const currentVolume = parseFloat(volumeSlider.value); // 0-1
498
+ this.widget.setVolume(currentVolume); // USA 0-1
499
+
500
+ if (this.options.debug) {
501
+ console.log('Mute synced, volume:', currentVolume);
502
+ }
503
+ }
504
+ }, 50);
505
+ });
506
+ }
507
+ }
508
+
509
+ /**
510
+ * Intercept progress bar
511
+ */
512
+ interceptProgressBar() {
513
+ const progressContainer = this.player.controls?.querySelector('.progress-container');
514
+ if (!progressContainer) return;
515
+
516
+ // Click to seek
517
+ progressContainer.addEventListener('click', (e) => {
518
+ e.stopPropagation();
519
+ e.preventDefault();
520
+
521
+ if (!this.widget || this.duration === 0) return;
522
+
523
+ const rect = progressContainer.getBoundingClientRect();
524
+ const clickX = e.clientX - rect.left;
525
+ const percentage = clickX / rect.width;
526
+ const seekTime = percentage * this.duration;
527
+
528
+ this.widget.seekTo(seekTime * 1000);
529
+ }, true);
530
+
531
+ // Find or create tooltip
532
+ let tooltip = progressContainer.querySelector('.seek-tooltip');
533
+ if (!tooltip) {
534
+ tooltip = document.createElement('div');
535
+ tooltip.className = 'seek-tooltip';
536
+ progressContainer.appendChild(tooltip);
537
+ }
538
+
539
+ // Update tooltip on mousemove
540
+ progressContainer.addEventListener('mousemove', (e) => {
541
+ if (!this.widget || this.duration === 0) return;
542
+
543
+ const rect = progressContainer.getBoundingClientRect();
544
+ const mouseX = e.clientX - rect.left;
545
+ const percentage = Math.max(0, Math.min(1, mouseX / rect.width));
546
+ const time = percentage * this.duration;
547
+
548
+ // Update tooltip text and position
549
+ tooltip.textContent = this.formatTime(time);
550
+ tooltip.style.left = mouseX + 'px';
551
+ tooltip.style.visibility = 'visible';
552
+ });
553
+
554
+ // Hide tooltip on mouseleave
555
+ progressContainer.addEventListener('mouseleave', () => {
556
+ if (tooltip) {
557
+ tooltip.style.visibility = 'hidden';
558
+ }
559
+ });
560
+ }
561
+
562
+ /**
563
+ * Add SoundCloud logo to controlbar
564
+ */
565
+ addSoundCloudLogo() {
566
+ if (!this.player.controls) {
567
+ console.error('SoundCloud Plugin: Controls not available');
568
+ return;
569
+ }
570
+
571
+ if (this.player.controls.querySelector('.soundcloud-logo-link')) {
572
+ return;
573
+ }
574
+
575
+ const logoButton = document.createElement('button');
576
+ logoButton.className = 'control-btn soundcloud-logo-link';
577
+ logoButton.title = 'SoundCloud Player';
578
+ logoButton.setAttribute('aria-label', 'SoundCloud Player');
579
+ logoButton.type = 'button';
580
+
581
+ logoButton.style.cssText = `
582
+ display: flex !important;
583
+ align-items: center;
584
+ justify-content: center;
585
+ background: none;
586
+ border: none;
587
+ padding: 8px;
588
+ opacity: 0.85;
589
+ transition: all 0.3s ease;
590
+ cursor: pointer;
591
+ flex-shrink: 0;
592
+ margin: 0 2px 0 6px;
593
+ border-radius: 6px;
594
+ visibility: visible !important;
595
+ `;
596
+
597
+ logoButton.innerHTML = `
598
+ <svg viewBox="0 0 120 50" xmlns="http://www.w3.org/2000/svg" style="display: block; width: 50px; height: 24px; color: #ff5500; transition: all 0.3s ease;">
599
+ <title>SoundCloud</title>
600
+ <rect x="5" y="20" width="3" height="10" rx="1.5" fill="currentColor" opacity="0.5"/>
601
+ <rect x="12" y="16" width="3" height="18" rx="1.5" fill="currentColor" opacity="0.6"/>
602
+ <rect x="19" y="12" width="3" height="26" rx="1.5" fill="currentColor" opacity="0.7"/>
603
+ <rect x="26" y="8" width="3" height="34" rx="1.5" fill="currentColor" opacity="0.8"/>
604
+ <rect x="33" y="14" width="3" height="22" rx="1.5" fill="currentColor" opacity="0.7"/>
605
+ <rect x="40" y="18" width="3" height="14" rx="1.5" fill="currentColor" opacity="0.6"/>
606
+ <rect x="47" y="22" width="3" height="6" rx="1.5" fill="currentColor" opacity="0.5"/>
607
+ <path d="M 58 25 Q 58 20 62 20 Q 64 20 65 21 Q 67 18 72 18 Q 78 18 80 23 Q 84 22 87 25 Q 90 28 87 32 Q 84 35 80 34 L 62 34 Q 58 34 58 30 Q 58 28 58 25 Z"
608
+ fill="currentColor"
609
+ opacity="0.9"/>
610
+ </svg>
611
+ `;
612
+
613
+ logoButton.addEventListener('mouseenter', () => {
614
+ logoButton.style.opacity = '1';
615
+ logoButton.style.transform = 'scale(1.1)';
616
+ logoButton.style.background = 'rgba(255, 255, 255, 0.1)';
617
+ const svg = logoButton.querySelector('svg');
618
+ if (svg) {
619
+ svg.style.color = '#ff7700';
620
+ svg.style.filter = 'drop-shadow(0 0 8px rgba(255, 85, 0, 0.6))';
621
+ }
622
+ });
623
+
624
+ logoButton.addEventListener('mouseleave', () => {
625
+ logoButton.style.opacity = '0.85';
626
+ logoButton.style.transform = 'scale(1)';
627
+ logoButton.style.background = 'none';
628
+ const svg = logoButton.querySelector('svg');
629
+ if (svg) {
630
+ svg.style.color = '#ff5500';
631
+ svg.style.filter = 'none';
632
+ }
633
+ });
634
+
635
+ logoButton.addEventListener('click', (e) => {
636
+ e.preventDefault();
637
+ e.stopPropagation();
638
+ this.showDirectSoundCloudControls();
639
+ });
640
+
641
+ const rightControls = this.player.controls.querySelector('.controls-right');
642
+
643
+ if (rightControls) {
644
+ const settingsBtn = rightControls.querySelector('.settings-btn');
645
+
646
+ if (settingsBtn) {
647
+ rightControls.insertBefore(logoButton, settingsBtn.parentElement);
648
+ } else {
649
+ if (rightControls.firstChild) {
650
+ rightControls.insertBefore(logoButton, rightControls.firstChild);
651
+ } else {
652
+ rightControls.appendChild(logoButton);
653
+ }
654
+ }
655
+
656
+ this.soundcloudLogo = logoButton;
657
+
658
+ if (this.options.debug) {
659
+ console.log('SoundCloud Plugin: Logo added');
660
+ }
661
+ }
662
+ }
663
+
664
+ /**
665
+ * Show direct SoundCloud controls
666
+ */
667
+ showDirectSoundCloudControls() {
668
+ const container = this.api ? this.api.container : this.player.container;
669
+ if (!container) return;
670
+
671
+ // INCREASE z-index of SoundCloud container to bring it to front
672
+ if (this.soundcloudContainer) {
673
+ this.soundcloudContainer.style.zIndex = '9999';
674
+ }
675
+
676
+ // Hide the invisible overlay that blocks clicks
677
+ if (this.invisibleOverlay) {
678
+ this.invisibleOverlay.style.display = 'none';
679
+ this.invisibleOverlay.style.pointerEvents = 'none';
680
+ }
681
+
682
+ // Hide ALL player overlays with pointer-events
683
+ const loadingOverlay = container.querySelector('.loading-overlay');
684
+ if (loadingOverlay) {
685
+ loadingOverlay.style.display = 'none';
686
+ loadingOverlay.style.pointerEvents = 'none';
687
+ }
688
+
689
+ const initialLoading = container.querySelector('.initial-loading');
690
+ if (initialLoading) {
691
+ initialLoading.style.display = 'none';
692
+ initialLoading.style.pointerEvents = 'none';
693
+ }
694
+
695
+ const titleOverlay = container.querySelector('.title-overlay');
696
+ if (titleOverlay) {
697
+ titleOverlay.style.display = 'none';
698
+ titleOverlay.style.pointerEvents = 'none';
699
+ }
700
+
701
+ const posterOverlay = container.querySelector('.video-poster-overlay');
702
+ if (posterOverlay) {
703
+ posterOverlay.style.display = 'none';
704
+ posterOverlay.style.pointerEvents = 'none';
705
+ }
706
+
707
+ const watermark = container.querySelector('.watermark');
708
+ if (watermark) {
709
+ watermark.style.display = 'none';
710
+ watermark.style.pointerEvents = 'none';
711
+ }
712
+
713
+ // Hide ANY other overlay
714
+ const allOverlays = container.querySelectorAll('[class*="overlay"]');
715
+ allOverlays.forEach(overlay => {
716
+ if (!overlay.classList.contains('soundcloud-container') &&
717
+ !overlay.classList.contains('soundcloud-invisible-overlay')) {
718
+ overlay.style.display = 'none';
719
+ overlay.style.pointerEvents = 'none';
720
+ overlay.style.zIndex = '-1';
721
+ }
722
+ });
723
+
724
+ // Hide MYETV controlbar completely
725
+ const controls = this.player.controls;
726
+ if (controls) {
727
+ controls.classList.remove('show');
728
+ controls.style.opacity = '0';
729
+ controls.style.visibility = 'hidden';
730
+ controls.style.pointerEvents = 'none';
731
+ }
732
+
733
+ // Disable auto-hide
734
+ if (this.player.options.autoHide) {
735
+ this.wasAutoHideEnabled = true;
736
+ this.player.options.autoHide = false;
737
+ }
738
+
739
+ // Disable mouse handlers
740
+ this.disableMouseHandlers();
741
+
742
+ if (this.options.debug) {
743
+ console.log('SoundCloud Plugin: Direct controls mode - z-index boosted, all overlays disabled');
744
+ }
745
+
746
+ // Clear existing timeout
747
+ if (this.soundcloudControlsTimeout) {
748
+ clearTimeout(this.soundcloudControlsTimeout);
749
+ }
750
+
751
+ // Restore after X seconds
752
+ this.soundcloudControlsTimeout = setTimeout(() => {
753
+ // RESTORE z-index of SoundCloud container
754
+ if (this.soundcloudContainer) {
755
+ this.soundcloudContainer.style.zIndex = '1';
756
+ }
757
+
758
+ // Restore invisible overlay
759
+ if (this.invisibleOverlay) {
760
+ this.invisibleOverlay.style.display = 'block';
761
+ this.invisibleOverlay.style.pointerEvents = 'auto';
762
+ }
763
+
764
+ // Restore controlbar
765
+ if (controls) {
766
+ controls.classList.add('show');
767
+ controls.style.opacity = '';
768
+ controls.style.visibility = '';
769
+ controls.style.pointerEvents = '';
770
+ }
771
+
772
+ // Re-enable auto-hide
773
+ if (this.wasAutoHideEnabled) {
774
+ this.player.options.autoHide = true;
775
+ this.wasAutoHideEnabled = false;
776
+ }
777
+
778
+ // Re-enable mouse handlers
779
+ this.enableMouseHandlers();
780
+
781
+ if (this.options.debug) {
782
+ console.log('SoundCloud Plugin: Normal mode restored');
783
+ }
784
+
785
+ }, this.options.controlsDisplayTime);
786
+ }
787
+
788
+ /**
789
+ * Disable mouse handlers
790
+ */
791
+ disableMouseHandlers() {
792
+ // Temporarily disable player's mouse handlers
793
+ if (this.player.onMouseMove) {
794
+ this.player._originalOnMouseMove = this.player.onMouseMove;
795
+ this.player.onMouseMove = () => { };
796
+ }
797
+
798
+ if (this.player.showControlsNow) {
799
+ this.player._originalShowControlsNow = this.player.showControlsNow;
800
+ this.player.showControlsNow = () => { };
801
+ }
802
+
803
+ if (this.player.resetAutoHideTimer) {
804
+ this.player._originalResetAutoHideTimer = this.player.resetAutoHideTimer;
805
+ this.player.resetAutoHideTimer = () => { };
806
+ }
807
+
808
+ if (this.options.debug) {
809
+ console.log('Mouse handlers disabled');
810
+ }
811
+ }
812
+
813
+ /**
814
+ * Enable mouse handlers
815
+ */
816
+ enableMouseHandlers() {
817
+ // Restore player's mouse handlers
818
+ if (this.player._originalOnMouseMove) {
819
+ this.player.onMouseMove = this.player._originalOnMouseMove;
820
+ delete this.player._originalOnMouseMove;
821
+ }
822
+
823
+ if (this.player._originalShowControlsNow) {
824
+ this.player.showControlsNow = this.player._originalShowControlsNow;
825
+ delete this.player._originalShowControlsNow;
826
+ }
827
+
828
+ if (this.player._originalResetAutoHideTimer) {
829
+ this.player.resetAutoHideTimer = this.player._originalResetAutoHideTimer;
830
+ delete this.player._originalResetAutoHideTimer;
831
+ }
832
+
833
+ if (this.options.debug) {
834
+ console.log('Mouse handlers enabled');
835
+ }
836
+ }
837
+
838
+ /**
839
+ * Build SoundCloud embed URL
840
+ */
841
+ buildEmbedUrl(autoplay = false) {
842
+ const trackId = this.extractSoundCloudTrackId(this.options.soundcloudUrl);
843
+
844
+ if (!trackId) {
845
+ console.error('SoundCloud Plugin: Invalid SoundCloud URL');
846
+ return null;
847
+ }
848
+
849
+ const params = new URLSearchParams({
850
+ url: `https://soundcloud.com/${trackId}`,
851
+ color: this.options.color.replace('#', ''),
852
+ auto_play: autoplay ? 'true' : 'false',
853
+ hide_related: this.options.hideRelated ? 'true' : 'false',
854
+ show_comments: this.options.showComments ? 'true' : 'false',
855
+ show_user: this.options.showUser ? 'true' : 'false',
856
+ show_reposts: this.options.showReposts ? 'true' : 'false',
857
+ show_teaser: this.options.showTeaser ? 'true' : 'false',
858
+ visual: this.options.visualMode ? 'true' : 'false'
859
+ });
860
+
861
+ if (!this.options.showArtwork) {
862
+ params.append('show_artwork', 'false');
863
+ }
864
+
865
+ return `https://w.soundcloud.com/player/?${params.toString()}`;
866
+ }
867
+
868
+ /**
869
+ * Extract track ID
870
+ */
871
+ extractSoundCloudTrackId(url) {
872
+ if (!url) return null;
873
+ try {
874
+ const urlObj = new URL(url);
875
+ return urlObj.pathname.substring(1);
876
+ } catch (error) {
877
+ return null;
878
+ }
879
+ }
880
+
881
+ /**
882
+ * Dispose
883
+ */
884
+ dispose() {
885
+ this.stopTimeUpdate();
886
+
887
+ if (this.soundcloudLogo && this.soundcloudLogo.parentNode) {
888
+ this.soundcloudLogo.parentNode.removeChild(this.soundcloudLogo);
889
+ }
890
+ if (this.soundcloudContainer && this.soundcloudContainer.parentNode) {
891
+ this.soundcloudContainer.parentNode.removeChild(this.soundcloudContainer);
892
+ }
893
+ if (this.invisibleOverlay && this.invisibleOverlay.parentNode) {
894
+ this.invisibleOverlay.parentNode.removeChild(this.invisibleOverlay);
895
+ }
896
+ if (this.soundcloudControlsTimeout) {
897
+ clearTimeout(this.soundcloudControlsTimeout);
898
+ }
899
+
900
+ // Restore mouse handlers if disabled
901
+ this.enableMouseHandlers();
902
+
903
+ // Restore auto-hide if it was disabled
904
+ if (this.wasAutoHideEnabled) {
905
+ this.player.options.autoHide = true;
906
+ }
907
+ // Stop volume polling
908
+ if (this.volumeCheckInterval) {
909
+ clearInterval(this.volumeCheckInterval);
910
+ this.volumeCheckInterval = null;
911
+ }
912
+ // Stop tooltip polling
913
+ if (this.tooltipUpdateInterval) {
914
+ clearInterval(this.tooltipUpdateInterval);
915
+ this.tooltipUpdateInterval = null;
916
+ }
917
+ this.showSpeedPiPButtons();
918
+ }
919
+ }
920
+
921
+ // Register plugin
922
+ if (typeof window.registerMYETVPlugin === 'function') {
923
+ window.registerMYETVPlugin('soundcloud', SoundCloudPlugin);
924
+ console.log('✓ SoundCloud Plugin registered successfully');
925
+ }
926
+
927
+ })();