myetv-player 1.4.0 → 1.5.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/README.md +1 -1
- package/css/myetv-player.css +7 -2
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +200 -59
- package/dist/myetv-player.min.js +152 -34
- package/package.json +2 -1
- package/plugins/soundcloud/myetv-player-soundcloud-plugin.js +927 -0
- package/plugins/soundcloud/readme.md +89 -0
- package/plugins/youtube/myetv-player-youtube-plugin.js +83 -125
|
@@ -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
|
+
})();
|