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
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MYETV Player - Cloudflare Stream Plugin
|
|
3
|
+
* File: myetv-player-cloudflare-stream-plugin.js
|
|
4
|
+
* Integrates Cloudflare Stream videos with full API control
|
|
5
|
+
* Created by https://www.myetv.tv https://oskarcosimo.com
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
(function () {
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
class CloudflareStreamPlugin {
|
|
12
|
+
constructor(player, options = {}) {
|
|
13
|
+
this.player = player;
|
|
14
|
+
this.options = {
|
|
15
|
+
// Video source
|
|
16
|
+
videoId: options.videoId || null, // Cloudflare Stream video ID
|
|
17
|
+
videoUrl: options.videoUrl || null, // Full video URL
|
|
18
|
+
signedUrl: options.signedUrl || null, // Signed URL for private videos
|
|
19
|
+
|
|
20
|
+
// Account/Domain
|
|
21
|
+
customerCode: options.customerCode || null, // Your Cloudflare account subdomain
|
|
22
|
+
|
|
23
|
+
// Playback options
|
|
24
|
+
autoplay: options.autoplay || false,
|
|
25
|
+
muted: options.muted || false,
|
|
26
|
+
loop: options.loop || false,
|
|
27
|
+
preload: options.preload || 'metadata', // 'none', 'metadata', 'auto'
|
|
28
|
+
controls: options.controls !== false,
|
|
29
|
+
defaultTextTrack: options.defaultTextTrack || null,
|
|
30
|
+
|
|
31
|
+
// Player customization
|
|
32
|
+
poster: options.poster || null, // Custom poster image
|
|
33
|
+
primaryColor: options.primaryColor || null, // Custom player color
|
|
34
|
+
letterboxColor: options.letterboxColor || 'black',
|
|
35
|
+
|
|
36
|
+
// Advanced options
|
|
37
|
+
startTime: options.startTime || 0, // Start position in seconds
|
|
38
|
+
adUrl: options.adUrl || null, // VAST ad tag URL
|
|
39
|
+
|
|
40
|
+
// Plugin options
|
|
41
|
+
debug: options.debug || false,
|
|
42
|
+
replaceNativePlayer: options.replaceNativePlayer !== false,
|
|
43
|
+
autoLoadFromData: options.autoLoadFromData !== false,
|
|
44
|
+
responsive: options.responsive !== false,
|
|
45
|
+
|
|
46
|
+
...options
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
this.streamPlayer = null;
|
|
50
|
+
this.streamIframe = null;
|
|
51
|
+
this.streamContainer = null;
|
|
52
|
+
this.isPlayerReady = false;
|
|
53
|
+
|
|
54
|
+
// Get plugin API
|
|
55
|
+
this.api = player.getPluginAPI ? player.getPluginAPI() : {
|
|
56
|
+
player: player,
|
|
57
|
+
video: player.video,
|
|
58
|
+
container: player.container,
|
|
59
|
+
controls: player.controls,
|
|
60
|
+
debug: (msg) => {
|
|
61
|
+
if (this.options.debug) console.log('☁️ Cloudflare Stream:', msg);
|
|
62
|
+
},
|
|
63
|
+
triggerEvent: (event, data) => player.triggerEvent(event, data)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Setup plugin
|
|
69
|
+
*/
|
|
70
|
+
setup() {
|
|
71
|
+
this.api.debug('Setup started');
|
|
72
|
+
|
|
73
|
+
// Auto-detect from data attributes
|
|
74
|
+
if (this.options.autoLoadFromData) {
|
|
75
|
+
this.autoDetectSource();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Create player if we have a source
|
|
79
|
+
if (this.options.videoId || this.options.videoUrl || this.options.signedUrl) {
|
|
80
|
+
this.createStreamPlayer();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Add custom methods
|
|
84
|
+
this.addCustomMethods();
|
|
85
|
+
|
|
86
|
+
this.api.debug('Setup completed');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Auto-detect source from data attributes
|
|
91
|
+
*/
|
|
92
|
+
autoDetectSource() {
|
|
93
|
+
// Check data attributes
|
|
94
|
+
const dataVideoId = this.api.video.getAttribute('data-cloudflare-video-id');
|
|
95
|
+
const dataCustomerCode = this.api.video.getAttribute('data-cloudflare-customer');
|
|
96
|
+
const dataVideoType = this.api.video.getAttribute('data-video-type');
|
|
97
|
+
|
|
98
|
+
if (dataVideoId && dataVideoType === 'cloudflare') {
|
|
99
|
+
this.options.videoId = dataVideoId;
|
|
100
|
+
if (dataCustomerCode) {
|
|
101
|
+
this.options.customerCode = dataCustomerCode;
|
|
102
|
+
}
|
|
103
|
+
this.api.debug('Video ID detected: ' + dataVideoId);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check video src
|
|
108
|
+
const src = this.api.video.src || this.api.video.currentSrc;
|
|
109
|
+
if (src && this.isCloudflareUrl(src)) {
|
|
110
|
+
this.extractFromUrl(src);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check source elements
|
|
115
|
+
const sources = this.api.video.querySelectorAll('source');
|
|
116
|
+
for (const source of sources) {
|
|
117
|
+
const sourceSrc = source.getAttribute('src');
|
|
118
|
+
const sourceType = source.getAttribute('type');
|
|
119
|
+
|
|
120
|
+
if ((sourceType === 'video/cloudflare' || this.isCloudflareUrl(sourceSrc)) && sourceSrc) {
|
|
121
|
+
this.extractFromUrl(sourceSrc);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if URL is a Cloudflare Stream URL
|
|
129
|
+
*/
|
|
130
|
+
isCloudflareUrl(url) {
|
|
131
|
+
if (!url) return false;
|
|
132
|
+
return /cloudflarestream\.com|videodelivery\.net/.test(url);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Extract video ID and customer code from URL
|
|
137
|
+
*/
|
|
138
|
+
extractFromUrl(url) {
|
|
139
|
+
// Extract video ID from various URL formats
|
|
140
|
+
// https://customer-code.cloudflarestream.com/video-id/manifest/video.m3u8
|
|
141
|
+
// https://videodelivery.net/video-id
|
|
142
|
+
|
|
143
|
+
const match1 = url.match(/cloudflarestream\.com\/([a-f0-9]+)/);
|
|
144
|
+
const match2 = url.match(/videodelivery\.net\/([a-f0-9]+)/);
|
|
145
|
+
const match3 = url.match(/([a-z0-9-]+)\.cloudflarestream\.com/);
|
|
146
|
+
|
|
147
|
+
if (match1) {
|
|
148
|
+
this.options.videoId = match1[1];
|
|
149
|
+
} else if (match2) {
|
|
150
|
+
this.options.videoId = match2[1];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (match3) {
|
|
154
|
+
this.options.customerCode = match3[1];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.api.debug('Extracted - Video ID: ' + this.options.videoId + ', Customer: ' + this.options.customerCode);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Create Cloudflare Stream player
|
|
162
|
+
*/
|
|
163
|
+
createStreamPlayer() {
|
|
164
|
+
if (!this.options.videoId && !this.options.videoUrl && !this.options.signedUrl) {
|
|
165
|
+
this.api.debug('No video source provided');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Hide native player
|
|
170
|
+
if (this.options.replaceNativePlayer) {
|
|
171
|
+
this.api.video.style.display = 'none';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Create container
|
|
175
|
+
this.streamContainer = document.createElement('div');
|
|
176
|
+
this.streamContainer.className = 'cloudflare-stream-container';
|
|
177
|
+
this.streamContainer.style.cssText = `
|
|
178
|
+
position: absolute;
|
|
179
|
+
top: 0;
|
|
180
|
+
left: 0;
|
|
181
|
+
width: 100%;
|
|
182
|
+
height: 100%;
|
|
183
|
+
z-index: 100;
|
|
184
|
+
`;
|
|
185
|
+
|
|
186
|
+
// Build iframe URL
|
|
187
|
+
const iframeSrc = this.buildIframeUrl();
|
|
188
|
+
|
|
189
|
+
// Create iframe
|
|
190
|
+
this.streamIframe = document.createElement('iframe');
|
|
191
|
+
this.streamIframe.src = iframeSrc;
|
|
192
|
+
this.streamIframe.style.cssText = `
|
|
193
|
+
border: none;
|
|
194
|
+
width: 100%;
|
|
195
|
+
height: 100%;
|
|
196
|
+
`;
|
|
197
|
+
this.streamIframe.allow = 'accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;';
|
|
198
|
+
this.streamIframe.allowFullscreen = true;
|
|
199
|
+
|
|
200
|
+
this.streamContainer.appendChild(this.streamIframe);
|
|
201
|
+
this.api.container.appendChild(this.streamContainer);
|
|
202
|
+
|
|
203
|
+
// Setup Stream object for API access
|
|
204
|
+
this.setupStreamAPI();
|
|
205
|
+
|
|
206
|
+
this.api.debug('Cloudflare Stream player created');
|
|
207
|
+
this.api.triggerEvent('cloudflare:playerready', {
|
|
208
|
+
videoId: this.options.videoId
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Build iframe URL with all options
|
|
214
|
+
*/
|
|
215
|
+
buildIframeUrl() {
|
|
216
|
+
let baseUrl;
|
|
217
|
+
|
|
218
|
+
// Priority 1: Signed URL (for private videos)
|
|
219
|
+
if (this.options.signedUrl) {
|
|
220
|
+
return this.options.signedUrl;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Priority 2: Full video URL
|
|
224
|
+
if (this.options.videoUrl) {
|
|
225
|
+
baseUrl = this.options.videoUrl;
|
|
226
|
+
}
|
|
227
|
+
// Priority 3: Build from video ID
|
|
228
|
+
else if (this.options.videoId) {
|
|
229
|
+
if (this.options.customerCode) {
|
|
230
|
+
baseUrl = `https://customer-${this.options.customerCode}.cloudflarestream.com/${this.options.videoId}/iframe`;
|
|
231
|
+
} else {
|
|
232
|
+
baseUrl = `https://iframe.videodelivery.net/${this.options.videoId}`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Add query parameters
|
|
237
|
+
const params = new URLSearchParams();
|
|
238
|
+
|
|
239
|
+
if (this.options.autoplay) params.append('autoplay', 'true');
|
|
240
|
+
if (this.options.muted) params.append('muted', 'true');
|
|
241
|
+
if (this.options.loop) params.append('loop', 'true');
|
|
242
|
+
if (this.options.controls === false) params.append('controls', 'false');
|
|
243
|
+
if (this.options.preload) params.append('preload', this.options.preload);
|
|
244
|
+
if (this.options.poster) params.append('poster', encodeURIComponent(this.options.poster));
|
|
245
|
+
if (this.options.primaryColor) params.append('primaryColor', this.options.primaryColor.replace('#', ''));
|
|
246
|
+
if (this.options.startTime) params.append('startTime', this.options.startTime);
|
|
247
|
+
if (this.options.adUrl) params.append('ad-url', encodeURIComponent(this.options.adUrl));
|
|
248
|
+
if (this.options.defaultTextTrack) params.append('defaultTextTrack', this.options.defaultTextTrack);
|
|
249
|
+
|
|
250
|
+
const queryString = params.toString();
|
|
251
|
+
return queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Setup Stream API for iframe communication
|
|
256
|
+
*/
|
|
257
|
+
setupStreamAPI() {
|
|
258
|
+
// Create Stream object wrapper
|
|
259
|
+
this.streamPlayer = {
|
|
260
|
+
iframe: this.streamIframe,
|
|
261
|
+
|
|
262
|
+
// Playback control
|
|
263
|
+
play: () => this.sendCommand('play'),
|
|
264
|
+
pause: () => this.sendCommand('pause'),
|
|
265
|
+
|
|
266
|
+
// Volume
|
|
267
|
+
mute: () => this.sendCommand('mute'),
|
|
268
|
+
unmute: () => this.sendCommand('unmute'),
|
|
269
|
+
|
|
270
|
+
// Seeking
|
|
271
|
+
seek: (time) => this.sendCommand('seek', time),
|
|
272
|
+
|
|
273
|
+
// Properties (these require message passing)
|
|
274
|
+
getCurrentTime: () => this.getProperty('currentTime'),
|
|
275
|
+
getDuration: () => this.getProperty('duration'),
|
|
276
|
+
getVolume: () => this.getProperty('volume'),
|
|
277
|
+
getPaused: () => this.getProperty('paused'),
|
|
278
|
+
getMuted: () => this.getProperty('muted'),
|
|
279
|
+
|
|
280
|
+
// Setters
|
|
281
|
+
setVolume: (volume) => this.sendCommand('volume', volume),
|
|
282
|
+
setPlaybackRate: (rate) => this.sendCommand('playbackRate', rate)
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// Listen for messages from iframe
|
|
286
|
+
this.setupMessageListener();
|
|
287
|
+
|
|
288
|
+
this.isPlayerReady = true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Send command to iframe
|
|
293
|
+
*/
|
|
294
|
+
sendCommand(command, value) {
|
|
295
|
+
if (!this.streamIframe || !this.streamIframe.contentWindow) {
|
|
296
|
+
return Promise.reject('Player not ready');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const message = value !== undefined
|
|
300
|
+
? { event: command, value: value }
|
|
301
|
+
: { event: command };
|
|
302
|
+
|
|
303
|
+
this.streamIframe.contentWindow.postMessage(message, '*');
|
|
304
|
+
return Promise.resolve();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get property from iframe
|
|
309
|
+
*/
|
|
310
|
+
getProperty(property) {
|
|
311
|
+
return new Promise((resolve) => {
|
|
312
|
+
// Note: Cloudflare Stream uses standard video events
|
|
313
|
+
// Property getters work via event listeners
|
|
314
|
+
const handler = (e) => {
|
|
315
|
+
if (e.data && e.data.event === property) {
|
|
316
|
+
window.removeEventListener('message', handler);
|
|
317
|
+
resolve(e.data.value);
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
window.addEventListener('message', handler);
|
|
321
|
+
this.sendCommand('get' + property.charAt(0).toUpperCase() + property.slice(1));
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Setup message listener for iframe events
|
|
327
|
+
*/
|
|
328
|
+
setupMessageListener() {
|
|
329
|
+
window.addEventListener('message', (event) => {
|
|
330
|
+
if (!event.data || !event.data.event) return;
|
|
331
|
+
|
|
332
|
+
const data = event.data;
|
|
333
|
+
|
|
334
|
+
// Map Cloudflare Stream events to standard events
|
|
335
|
+
switch (data.event) {
|
|
336
|
+
case 'play':
|
|
337
|
+
this.api.triggerEvent('play', {});
|
|
338
|
+
this.api.triggerEvent('playing', {});
|
|
339
|
+
break;
|
|
340
|
+
case 'pause':
|
|
341
|
+
this.api.triggerEvent('pause', {});
|
|
342
|
+
break;
|
|
343
|
+
case 'ended':
|
|
344
|
+
this.api.triggerEvent('ended', {});
|
|
345
|
+
break;
|
|
346
|
+
case 'timeupdate':
|
|
347
|
+
this.api.triggerEvent('timeupdate', {
|
|
348
|
+
currentTime: data.currentTime,
|
|
349
|
+
duration: data.duration
|
|
350
|
+
});
|
|
351
|
+
break;
|
|
352
|
+
case 'volumechange':
|
|
353
|
+
this.api.triggerEvent('volumechange', {
|
|
354
|
+
volume: data.volume,
|
|
355
|
+
muted: data.muted
|
|
356
|
+
});
|
|
357
|
+
break;
|
|
358
|
+
case 'loadedmetadata':
|
|
359
|
+
this.api.triggerEvent('loadedmetadata', data);
|
|
360
|
+
this.api.triggerEvent('cloudflare:ready', {});
|
|
361
|
+
break;
|
|
362
|
+
case 'error':
|
|
363
|
+
this.api.triggerEvent('error', data);
|
|
364
|
+
this.api.triggerEvent('cloudflare:error', data);
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Add custom methods to player
|
|
372
|
+
*/
|
|
373
|
+
addCustomMethods() {
|
|
374
|
+
// Load video
|
|
375
|
+
this.api.player.loadCloudflareVideo = (videoId, customerCode) => {
|
|
376
|
+
return this.loadVideo(videoId, customerCode);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// Get Stream player
|
|
380
|
+
this.api.player.getCloudflarePlayer = () => {
|
|
381
|
+
return this.streamPlayer;
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Play
|
|
387
|
+
*/
|
|
388
|
+
play() {
|
|
389
|
+
if (!this.streamPlayer) {
|
|
390
|
+
return Promise.reject('Player not initialized');
|
|
391
|
+
}
|
|
392
|
+
return this.streamPlayer.play();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Pause
|
|
397
|
+
*/
|
|
398
|
+
pause() {
|
|
399
|
+
if (!this.streamPlayer) {
|
|
400
|
+
return Promise.reject('Player not initialized');
|
|
401
|
+
}
|
|
402
|
+
return this.streamPlayer.pause();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Seek
|
|
407
|
+
*/
|
|
408
|
+
seek(seconds) {
|
|
409
|
+
if (!this.streamPlayer) {
|
|
410
|
+
return Promise.reject('Player not initialized');
|
|
411
|
+
}
|
|
412
|
+
return this.streamPlayer.seek(seconds);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Get current time
|
|
417
|
+
*/
|
|
418
|
+
getCurrentTime() {
|
|
419
|
+
if (!this.streamPlayer) {
|
|
420
|
+
return Promise.reject('Player not initialized');
|
|
421
|
+
}
|
|
422
|
+
return this.streamPlayer.getCurrentTime();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Get duration
|
|
427
|
+
*/
|
|
428
|
+
getDuration() {
|
|
429
|
+
if (!this.streamPlayer) {
|
|
430
|
+
return Promise.reject('Player not initialized');
|
|
431
|
+
}
|
|
432
|
+
return this.streamPlayer.getDuration();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Set volume
|
|
437
|
+
*/
|
|
438
|
+
setVolume(volume) {
|
|
439
|
+
if (!this.streamPlayer) {
|
|
440
|
+
return Promise.reject('Player not initialized');
|
|
441
|
+
}
|
|
442
|
+
return this.streamPlayer.setVolume(volume);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Get volume
|
|
447
|
+
*/
|
|
448
|
+
getVolume() {
|
|
449
|
+
if (!this.streamPlayer) {
|
|
450
|
+
return Promise.reject('Player not initialized');
|
|
451
|
+
}
|
|
452
|
+
return this.streamPlayer.getVolume();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Mute
|
|
457
|
+
*/
|
|
458
|
+
mute() {
|
|
459
|
+
if (!this.streamPlayer) {
|
|
460
|
+
return Promise.reject('Player not initialized');
|
|
461
|
+
}
|
|
462
|
+
return this.streamPlayer.mute();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Unmute
|
|
467
|
+
*/
|
|
468
|
+
unmute() {
|
|
469
|
+
if (!this.streamPlayer) {
|
|
470
|
+
return Promise.reject('Player not initialized');
|
|
471
|
+
}
|
|
472
|
+
return this.streamPlayer.unmute();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Get muted state
|
|
477
|
+
*/
|
|
478
|
+
getMuted() {
|
|
479
|
+
if (!this.streamPlayer) {
|
|
480
|
+
return Promise.reject('Player not initialized');
|
|
481
|
+
}
|
|
482
|
+
return this.streamPlayer.getMuted();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Get paused state
|
|
487
|
+
*/
|
|
488
|
+
getPaused() {
|
|
489
|
+
if (!this.streamPlayer) {
|
|
490
|
+
return Promise.reject('Player not initialized');
|
|
491
|
+
}
|
|
492
|
+
return this.streamPlayer.getPaused();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Set playback rate
|
|
497
|
+
*/
|
|
498
|
+
setPlaybackRate(rate) {
|
|
499
|
+
if (!this.streamPlayer) {
|
|
500
|
+
return Promise.reject('Player not initialized');
|
|
501
|
+
}
|
|
502
|
+
return this.streamPlayer.setPlaybackRate(rate);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Load new video
|
|
507
|
+
*/
|
|
508
|
+
loadVideo(videoId, customerCode) {
|
|
509
|
+
this.options.videoId = videoId;
|
|
510
|
+
if (customerCode) {
|
|
511
|
+
this.options.customerCode = customerCode;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Remove existing player
|
|
515
|
+
if (this.streamContainer) {
|
|
516
|
+
this.streamContainer.remove();
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Create new player
|
|
520
|
+
this.createStreamPlayer();
|
|
521
|
+
|
|
522
|
+
this.api.triggerEvent('cloudflare:videoloaded', { videoId, customerCode });
|
|
523
|
+
return Promise.resolve(videoId);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Dispose plugin
|
|
528
|
+
*/
|
|
529
|
+
dispose() {
|
|
530
|
+
this.api.debug('Disposing plugin');
|
|
531
|
+
|
|
532
|
+
if (this.streamContainer) {
|
|
533
|
+
this.streamContainer.remove();
|
|
534
|
+
this.streamContainer = null;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
this.streamPlayer = null;
|
|
538
|
+
this.streamIframe = null;
|
|
539
|
+
|
|
540
|
+
// Restore native player
|
|
541
|
+
if (this.api.video && this.options.replaceNativePlayer) {
|
|
542
|
+
this.api.video.style.display = '';
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
this.api.debug('Plugin disposed');
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Register plugin globally
|
|
550
|
+
if (typeof window.registerMYETVPlugin === 'function') {
|
|
551
|
+
window.registerMYETVPlugin('cloudflare', CloudflareStreamPlugin);
|
|
552
|
+
} else {
|
|
553
|
+
console.error('☁️ MYETV Player plugin system not found');
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
})();
|