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.
Files changed (37) hide show
  1. package/.github/workflows/codeql.yml +100 -0
  2. package/README.md +36 -58
  3. package/SECURITY.md +50 -0
  4. package/css/myetv-player.css +301 -218
  5. package/css/myetv-player.min.css +1 -1
  6. package/dist/myetv-player.js +1713 -1503
  7. package/dist/myetv-player.min.js +1670 -1471
  8. package/package.json +6 -1
  9. package/plugins/README.md +1016 -0
  10. package/plugins/cloudflare/README.md +1068 -0
  11. package/plugins/cloudflare/myetv-player-cloudflare-stream-plugin.js +556 -0
  12. package/plugins/facebook/README.md +1024 -0
  13. package/plugins/facebook/myetv-player-facebook-plugin.js +437 -0
  14. package/plugins/gamepad-remote-controller/README.md +816 -0
  15. package/plugins/gamepad-remote-controller/myetv-player-gamepad-remote-plugin.js +678 -0
  16. package/plugins/google-adsense-ads/README.md +1 -0
  17. package/plugins/google-adsense-ads/g-adsense-ads-plugin.js +158 -0
  18. package/plugins/google-ima-ads/README.md +1 -0
  19. package/plugins/google-ima-ads/g-ima-ads-plugin.js +355 -0
  20. package/plugins/twitch/README.md +1185 -0
  21. package/plugins/twitch/myetv-player-twitch-plugin.js +569 -0
  22. package/plugins/vast-vpaid-ads/README.md +1 -0
  23. package/plugins/vast-vpaid-ads/vast-vpaid-ads-plugin.js +346 -0
  24. package/plugins/vimeo/README.md +1416 -0
  25. package/plugins/vimeo/myetv-player-vimeo.js +640 -0
  26. package/plugins/youtube/README.md +851 -0
  27. package/plugins/youtube/myetv-player-youtube-plugin.js +1714 -210
  28. package/scss/README.md +160 -0
  29. package/scss/_menus.scss +840 -672
  30. package/scss/_responsive.scss +67 -105
  31. package/scss/_volume.scss +67 -105
  32. package/src/README.md +559 -0
  33. package/src/controls.js +16 -4
  34. package/src/core.js +1192 -1062
  35. package/src/i18n.js +27 -1
  36. package/src/quality.js +478 -436
  37. package/src/subtitles.js +2 -2
@@ -0,0 +1,346 @@
1
+ /* VAST/VPAID Custom Plugin for MYETV Video Player
2
+ * Custom implementation for VAST and VPAID ads
3
+ * No external dependencies
4
+ * Created by https://www.myetv.tv https://oskarcosimo.com
5
+ */
6
+
7
+ /* GLOBAL_START */
8
+ (function () {
9
+ 'use strict';
10
+
11
+ /**
12
+ * VAST/VPAID Custom Plugin
13
+ * Handles VAST and VPAID ads without external SDK
14
+ */
15
+ class VASTPlugin {
16
+ constructor(player, options = {}) {
17
+ this.player = player;
18
+ this.options = {
19
+ vastUrl: options.vastUrl || '',
20
+ vastXML: options.vastXML || null,
21
+ skipDelay: options.skipDelay || 5, // seconds
22
+ maxRedirects: options.maxRedirects || 5,
23
+ timeout: options.timeout || 8000,
24
+ debug: options.debug || false,
25
+ adLabel: options.adLabel || 'Pubblicità',
26
+ skipText: options.skipText || 'Salta annuncio',
27
+ ...options
28
+ };
29
+
30
+ this.adOverlay = null;
31
+ this.isAdPlaying = false;
32
+ this.currentAd = null;
33
+ this.skipButton = null;
34
+ }
35
+
36
+ /**
37
+ * Setup plugin
38
+ */
39
+ setup() {
40
+ this.createAdOverlay();
41
+ this.attachPlayerEvents();
42
+
43
+ if (this.options.debug) {
44
+ console.log('🎬 VAST Plugin initialized');
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Create ad overlay element
50
+ */
51
+ createAdOverlay() {
52
+ this.adOverlay = document.createElement('div');
53
+ this.adOverlay.className = 'vast-ad-overlay';
54
+ this.adOverlay.style.cssText = `
55
+ position: absolute;
56
+ top: 0;
57
+ left: 0;
58
+ width: 100%;
59
+ height: 100%;
60
+ background: #000;
61
+ z-index: 1000;
62
+ display: none;
63
+ flex-direction: column;
64
+ justify-content: center;
65
+ align-items: center;
66
+ `;
67
+
68
+ // Ad label
69
+ const adLabel = document.createElement('div');
70
+ adLabel.className = 'vast-ad-label';
71
+ adLabel.textContent = this.options.adLabel;
72
+ adLabel.style.cssText = `
73
+ position: absolute;
74
+ top: 10px;
75
+ left: 10px;
76
+ background: rgba(0, 0, 0, 0.7);
77
+ color: #fff;
78
+ padding: 5px 10px;
79
+ border-radius: 3px;
80
+ font-size: 12px;
81
+ z-index: 1001;
82
+ `;
83
+ this.adOverlay.appendChild(adLabel);
84
+
85
+ // Ad video element
86
+ this.adVideo = document.createElement('video');
87
+ this.adVideo.className = 'vast-ad-video';
88
+ this.adVideo.style.cssText = `
89
+ width: 100%;
90
+ height: 100%;
91
+ object-fit: contain;
92
+ `;
93
+ this.adOverlay.appendChild(this.adVideo);
94
+
95
+ // Skip button
96
+ this.skipButton = document.createElement('button');
97
+ this.skipButton.className = 'vast-skip-button';
98
+ this.skipButton.style.cssText = `
99
+ position: absolute;
100
+ bottom: 20px;
101
+ right: 20px;
102
+ background: rgba(0, 0, 0, 0.8);
103
+ color: #fff;
104
+ border: none;
105
+ padding: 10px 20px;
106
+ border-radius: 3px;
107
+ cursor: pointer;
108
+ font-size: 14px;
109
+ z-index: 1002;
110
+ display: none;
111
+ `;
112
+ this.skipButton.addEventListener('click', () => this.skipAd());
113
+ this.adOverlay.appendChild(this.skipButton);
114
+
115
+ this.player.container.appendChild(this.adOverlay);
116
+ }
117
+
118
+ /**
119
+ * Load and parse VAST
120
+ */
121
+ async loadVAST() {
122
+ try {
123
+ let vastXML;
124
+
125
+ if (this.options.vastXML) {
126
+ vastXML = this.options.vastXML;
127
+ } else if (this.options.vastUrl) {
128
+ const response = await fetch(this.options.vastUrl, {
129
+ method: 'GET',
130
+ timeout: this.options.timeout
131
+ });
132
+ vastXML = await response.text();
133
+ } else {
134
+ throw new Error('No VAST URL or XML provided');
135
+ }
136
+
137
+ const parser = new DOMParser();
138
+ const vastDoc = parser.parseFromString(vastXML, 'text/xml');
139
+
140
+ // Parse VAST document
141
+ this.currentAd = this.parseVAST(vastDoc);
142
+
143
+ if (this.currentAd) {
144
+ this.playAd();
145
+ } else {
146
+ throw new Error('No valid ad found in VAST response');
147
+ }
148
+
149
+ } catch (error) {
150
+ console.error('🎬 VAST loading error:', error);
151
+ this.player.triggerEvent('aderror', { error });
152
+ this.resumeContent();
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Parse VAST XML
158
+ */
159
+ parseVAST(vastDoc) {
160
+ const ad = vastDoc.querySelector('Ad');
161
+ if (!ad) return null;
162
+
163
+ const inline = ad.querySelector('InLine');
164
+ if (!inline) {
165
+ // Handle VAST wrapper
166
+ const wrapper = ad.querySelector('Wrapper');
167
+ if (wrapper) {
168
+ console.warn('🎬 VAST Wrapper not fully supported in this version');
169
+ }
170
+ return null;
171
+ }
172
+
173
+ // Get media file
174
+ const mediaFiles = inline.querySelectorAll('MediaFile');
175
+ let selectedMedia = null;
176
+
177
+ // Prefer MP4 format
178
+ for (const media of mediaFiles) {
179
+ const type = media.getAttribute('type');
180
+ if (type && type.includes('mp4')) {
181
+ selectedMedia = media;
182
+ break;
183
+ }
184
+ }
185
+
186
+ if (!selectedMedia && mediaFiles.length > 0) {
187
+ selectedMedia = mediaFiles[0];
188
+ }
189
+
190
+ if (!selectedMedia) return null;
191
+
192
+ // Get tracking URLs
193
+ const impressions = Array.from(inline.querySelectorAll('Impression'))
194
+ .map(imp => imp.textContent.trim());
195
+
196
+ const clickThrough = inline.querySelector('ClickThrough')?.textContent.trim();
197
+ const clickTracking = Array.from(inline.querySelectorAll('ClickTracking'))
198
+ .map(click => click.textContent.trim());
199
+
200
+ // Get skip offset
201
+ const skipOffset = inline.querySelector('Linear')?.getAttribute('skipoffset');
202
+
203
+ return {
204
+ mediaFileUrl: selectedMedia.textContent.trim(),
205
+ impressions,
206
+ clickThrough,
207
+ clickTracking,
208
+ skipOffset: this.parseSkipOffset(skipOffset),
209
+ duration: parseInt(inline.querySelector('Duration')?.textContent) || 0
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Parse skip offset
215
+ */
216
+ parseSkipOffset(offset) {
217
+ if (!offset) return this.options.skipDelay;
218
+
219
+ // Parse formats like "00:00:05" or "5s" or "25%"
220
+ if (offset.includes(':')) {
221
+ const parts = offset.split(':');
222
+ return parseInt(parts[parts.length - 1]);
223
+ } else if (offset.endsWith('s')) {
224
+ return parseInt(offset);
225
+ }
226
+
227
+ return this.options.skipDelay;
228
+ }
229
+
230
+ /**
231
+ * Play ad
232
+ */
233
+ playAd() {
234
+ if (!this.currentAd) return;
235
+
236
+ this.isAdPlaying = true;
237
+ this.adOverlay.style.display = 'flex';
238
+ this.player.video.pause();
239
+
240
+ // Set ad source
241
+ this.adVideo.src = this.currentAd.mediaFileUrl;
242
+ this.adVideo.play();
243
+
244
+ // Fire impression tracking
245
+ this.fireTrackingURLs(this.currentAd.impressions);
246
+
247
+ // Setup skip button
248
+ if (this.currentAd.skipOffset > 0) {
249
+ setTimeout(() => {
250
+ this.skipButton.textContent = this.options.skipText;
251
+ this.skipButton.style.display = 'block';
252
+ }, this.currentAd.skipOffset * 1000);
253
+ }
254
+
255
+ // Ad ended
256
+ this.adVideo.addEventListener('ended', () => {
257
+ this.adEnded();
258
+ });
259
+
260
+ // Ad click
261
+ this.adVideo.addEventListener('click', () => {
262
+ if (this.currentAd.clickThrough) {
263
+ window.open(this.currentAd.clickThrough, '_blank');
264
+ this.fireTrackingURLs(this.currentAd.clickTracking);
265
+ }
266
+ });
267
+
268
+ this.player.triggerEvent('adstarted');
269
+ }
270
+
271
+ /**
272
+ * Skip ad
273
+ */
274
+ skipAd() {
275
+ this.adVideo.pause();
276
+ this.adVideo.src = '';
277
+ this.adEnded();
278
+ this.player.triggerEvent('adskipped');
279
+ }
280
+
281
+ /**
282
+ * Ad ended
283
+ */
284
+ adEnded() {
285
+ this.isAdPlaying = false;
286
+ this.adOverlay.style.display = 'none';
287
+ this.skipButton.style.display = 'none';
288
+ this.currentAd = null;
289
+ this.resumeContent();
290
+ this.player.triggerEvent('adcomplete');
291
+ }
292
+
293
+ /**
294
+ * Resume content playback
295
+ */
296
+ resumeContent() {
297
+ this.player.video.play();
298
+ }
299
+
300
+ /**
301
+ * Fire tracking URLs
302
+ */
303
+ fireTrackingURLs(urls) {
304
+ if (!urls || urls.length === 0) return;
305
+
306
+ urls.forEach(url => {
307
+ if (url) {
308
+ // Use Image object for tracking pixels
309
+ const img = new Image();
310
+ img.src = url;
311
+ }
312
+ });
313
+ }
314
+
315
+ /**
316
+ * Attach player events
317
+ */
318
+ attachPlayerEvents() {
319
+ this.player.video.addEventListener('play', () => {
320
+ if (!this.isAdPlaying && this.options.vastUrl) {
321
+ this.loadVAST();
322
+ }
323
+ }, { once: true });
324
+ }
325
+
326
+ /**
327
+ * Dispose plugin
328
+ */
329
+ dispose() {
330
+ if (this.adOverlay) {
331
+ this.adOverlay.remove();
332
+ }
333
+
334
+ if (this.options.debug) {
335
+ console.log('🎬 VAST Plugin disposed');
336
+ }
337
+ }
338
+ }
339
+
340
+ // Register plugin globally
341
+ if (typeof window.registerMYETVPlugin === 'function') {
342
+ window.registerMYETVPlugin('vast', VASTPlugin);
343
+ }
344
+
345
+ })();
346
+ /* GLOBAL_END */