myetv-player 1.0.0 → 1.0.8

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 (38) hide show
  1. package/.github/workflows/codeql.yml +100 -0
  2. package/README.md +49 -58
  3. package/SECURITY.md +50 -0
  4. package/css/myetv-player.css +424 -219
  5. package/css/myetv-player.min.css +1 -1
  6. package/dist/myetv-player.js +1759 -1502
  7. package/dist/myetv-player.min.js +1705 -1469
  8. package/package.json +7 -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/_controls.scss +184 -30
  30. package/scss/_menus.scss +840 -672
  31. package/scss/_responsive.scss +67 -105
  32. package/scss/_volume.scss +67 -105
  33. package/src/README.md +559 -0
  34. package/src/controls.js +17 -5
  35. package/src/core.js +1237 -1060
  36. package/src/i18n.js +27 -1
  37. package/src/quality.js +478 -436
  38. package/src/subtitles.js +2 -2
@@ -0,0 +1,1185 @@
1
+ # MYETV Player - Twitch Plugin
2
+ Official Twitch integration plugin for MYETV Video Player. Embed live streams and VODs with full API control and interactive features.
3
+
4
+ ---
5
+
6
+ ## Table of Contents
7
+
8
+ - [Features](#features)
9
+ - [Installation](#installation)
10
+ - [Quick Start](#quick-start)
11
+ - [Configuration Options](#configuration-options)
12
+ - [Parent Domains (Important!)](#parent-domains-important)
13
+ - [Usage Methods](#usage-methods)
14
+ - [API Methods](#api-methods)
15
+ - [Events](#events)
16
+ - [Live Streams vs VODs](#live-streams-vs-vods)
17
+ - [Examples](#examples)
18
+ - [FAQ](#faq)
19
+ - [Troubleshooting](#troubleshooting)
20
+
21
+ ---
22
+
23
+ ## Features
24
+
25
+ - **Full Twitch Integration**: Embed live streams, VODs, and collections
26
+ - **Live Streaming**: Real-time live stream playback with chat support
27
+ - **Video on Demand**: Play Twitch VODs with full playback control
28
+ - **Smart Detection**: Auto-detects Twitch URLs from multiple sources
29
+ - **Quality Control**: Manage video quality with available stream qualities
30
+ - **Complete API**: Full control over playback, volume, seeking, and more
31
+ - **Playback Stats**: Access detailed playback statistics
32
+ - **Stream Status**: Detect when streams go online/offline
33
+ - **Easy Integration**: Seamless integration with MYETV Player
34
+ - **Responsive**: Works on desktop and mobile devices
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ ### Method 1: Direct Script Include
41
+
42
+ ```html
43
+ <!-- Load MYETV Player Core -->
44
+ <script src="dist/myetv-player.js"></script>
45
+
46
+ <!-- Load Twitch Plugin -->
47
+ <script src="plugins/myetv-player-twitch-plugin.js"></script>
48
+ ```
49
+
50
+ ### Method 2: Module Import
51
+
52
+ ```javascript
53
+ import MYETVPlayer from './myetv-player.js';
54
+ import './plugins/myetv-player-twitch-plugin.js';
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Quick Start
60
+
61
+ ### Live Stream Example
62
+
63
+ ```html
64
+ <!DOCTYPE html>
65
+ <html lang="en">
66
+ <head>
67
+ <meta charset="UTF-8">
68
+ <title>MYETV Player - Twitch Live Stream</title>
69
+ <link rel="stylesheet" href="dist/myetv-player.css">
70
+ </head>
71
+ <body>
72
+ <!-- Video Element -->
73
+ <video id="myVideo" class="video-player"></video>
74
+
75
+ <script src="dist/myetv-player.js"></script>
76
+ <script src="plugins/myetv-player-twitch-plugin.js"></script>
77
+
78
+ <script>
79
+ // Initialize player with Twitch live stream
80
+ const player = new MYETVPlayer('myVideo', {
81
+ debug: true,
82
+ plugins: {
83
+ twitch: {
84
+ channel: 'shroud', // Twitch channel name
85
+ parent: ['yourdomain.com'], // REQUIRED!
86
+ autoplay: true
87
+ }
88
+ }
89
+ });
90
+ </script>
91
+ </body>
92
+ </html>
93
+ ```
94
+
95
+ ### VOD (Video on Demand) Example
96
+
97
+ ```html
98
+ <script>
99
+ const player = new MYETVPlayer('myVideo', {
100
+ plugins: {
101
+ twitch: {
102
+ video: '1234567890', // Twitch VOD ID
103
+ parent: ['yourdomain.com'],
104
+ time: '1h30m0s' // Start at 1 hour 30 minutes
105
+ }
106
+ }
107
+ });
108
+ </script>
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Configuration Options
114
+
115
+ ```javascript
116
+ const player = new MYETVPlayer('myVideo', {
117
+ plugins: {
118
+ twitch: {
119
+ // ========== Video Source (choose one) ==========
120
+ // For live streams
121
+ channel: 'shroud',
122
+
123
+ // OR for VODs (Video on Demand)
124
+ video: '1234567890',
125
+
126
+ // OR for collections
127
+ collection: 'abc123xyz',
128
+
129
+ // ========== Parent Domains (REQUIRED!) ==========
130
+ parent: ['yourdomain.com', 'www.yourdomain.com'],
131
+
132
+ // ========== Player Dimensions ==========
133
+ width: '100%', // Width (pixels or percentage)
134
+ height: '100%', // Height (pixels or percentage)
135
+
136
+ // ========== Playback Options ==========
137
+ autoplay: true, // Auto-play on load
138
+ muted: false, // Start muted
139
+ time: '0h0m0s', // Start time for VODs (e.g., '1h30m45s')
140
+
141
+ // ========== UI Options ==========
142
+ allowfullscreen: true, // Enable fullscreen button
143
+
144
+ // ========== Plugin Options ==========
145
+ debug: false, // Enable debug logging
146
+ replaceNativePlayer: true, // Replace native video element
147
+ autoLoadFromData: true // Auto-detect from data attributes
148
+ }
149
+ }
150
+ });
151
+ ```
152
+
153
+ ### Time Format
154
+ For VODs, use format: `'1h30m45s'` where:
155
+ - `h` = hours
156
+ - `m` = minutes
157
+ - `s` = seconds
158
+
159
+ Examples:
160
+ - `'0h0m0s'` - Start from beginning
161
+ - `'1h0m0s'` - Start at 1 hour
162
+ - `'0h30m0s'` - Start at 30 minutes
163
+ - `'2h15m30s'` - Start at 2 hours, 15 minutes, 30 seconds
164
+
165
+ ---
166
+
167
+ ## Parent Domains (Important!)
168
+
169
+ **CRITICAL**: Twitch requires you to specify parent domains where the player will be embedded. This is a security feature.
170
+
171
+ ### What are parent domains?
172
+ The `parent` parameter must include all domains where your page will be loaded.
173
+
174
+ ### Examples:
175
+
176
+ ```javascript
177
+ // Single domain
178
+ parent: ['mysite.com']
179
+
180
+ // Multiple domains (with and without www)
181
+ parent: ['mysite.com', 'www.mysite.com']
182
+
183
+ // For development
184
+ parent: ['localhost']
185
+
186
+ // For embedded sites
187
+ parent: ['mysite.com', 'embed.mysite.com', 'cdn.mysite.com']
188
+ ```
189
+
190
+ ### Important Notes:
191
+ - **Do NOT include protocol** (http://, https://)
192
+ - **Do NOT include port** (localhost:3000)
193
+ - **Do NOT include paths** (/page/video)
194
+ - **Just the hostname**: `example.com`
195
+
196
+ ### What happens if parent is wrong?
197
+ The player will show an error: "This video is not available on this domain"
198
+
199
+ ---
200
+
201
+ ## Usage Methods
202
+
203
+ ### Method 1: Live Stream by Channel Name
204
+
205
+ ```html
206
+ <video id="myVideo" class="video-player"></video>
207
+
208
+ <script>
209
+ const player = new MYETVPlayer('myVideo', {
210
+ plugins: {
211
+ twitch: {
212
+ channel: 'shroud',
213
+ parent: ['yourdomain.com']
214
+ }
215
+ }
216
+ });
217
+ </script>
218
+ ```
219
+
220
+ ---
221
+
222
+ ### Method 2: VOD by Video ID
223
+
224
+ ```html
225
+ <video id="myVideo" class="video-player"></video>
226
+
227
+ <script>
228
+ const player = new MYETVPlayer('myVideo', {
229
+ plugins: {
230
+ twitch: {
231
+ video: '1234567890',
232
+ parent: ['yourdomain.com'],
233
+ time: '0h30m0s' // Start at 30 minutes
234
+ }
235
+ }
236
+ });
237
+ </script>
238
+ ```
239
+
240
+ ---
241
+
242
+ ### Method 3: Using Data Attributes
243
+
244
+ ```html
245
+ <video id="myVideo" class="video-player"
246
+ data-twitch-channel="shroud"
247
+ data-video-type="twitch">
248
+ </video>
249
+
250
+ <script>
251
+ const player = new MYETVPlayer('myVideo', {
252
+ plugins: {
253
+ twitch: {
254
+ parent: ['yourdomain.com'],
255
+ autoLoadFromData: true
256
+ }
257
+ }
258
+ });
259
+ </script>
260
+ ```
261
+
262
+ ---
263
+
264
+ ### Method 4: Using Twitch URLs
265
+
266
+ ```html
267
+ <video id="myVideo" class="video-player"
268
+ src="https://www.twitch.tv/shroud">
269
+ </video>
270
+
271
+ <script>
272
+ const player = new MYETVPlayer('myVideo', {
273
+ plugins: {
274
+ twitch: {
275
+ parent: ['yourdomain.com']
276
+ }
277
+ }
278
+ });
279
+ </script>
280
+ ```
281
+
282
+ **Supported URL Formats:**
283
+ - `https://www.twitch.tv/channelname` (live stream)
284
+ - `https://www.twitch.tv/videos/1234567890` (VOD)
285
+
286
+ ---
287
+
288
+ ### Method 5: Load Dynamically
289
+
290
+ ```html
291
+ <video id="myVideo" class="video-player"></video>
292
+
293
+ <script>
294
+ const player = new MYETVPlayer('myVideo', {
295
+ plugins: {
296
+ twitch: {
297
+ parent: ['yourdomain.com']
298
+ }
299
+ }
300
+ });
301
+
302
+ const twitchPlugin = player.getPlugin('twitch');
303
+
304
+ // Load channel
305
+ twitchPlugin.loadChannel('shroud');
306
+
307
+ // Or load VOD
308
+ twitchPlugin.loadVideo('1234567890', '1h0m0s');
309
+ </script>
310
+ ```
311
+
312
+ ---
313
+
314
+ ## API Methods
315
+
316
+ Get the plugin instance:
317
+ ```javascript
318
+ const twitchPlugin = player.getPlugin('twitch');
319
+ ```
320
+
321
+ ### Playback Control
322
+
323
+ #### `play()`
324
+ Play the stream/video.
325
+
326
+ ```javascript
327
+ twitchPlugin.play().then(() => {
328
+ console.log('Playing');
329
+ });
330
+ ```
331
+
332
+ **Returns:** Promise
333
+
334
+ ---
335
+
336
+ #### `pause()`
337
+ Pause the stream/video.
338
+
339
+ ```javascript
340
+ twitchPlugin.pause().then(() => {
341
+ console.log('Paused');
342
+ });
343
+ ```
344
+
345
+ **Returns:** Promise
346
+
347
+ ---
348
+
349
+ #### `seek(seconds)`
350
+ Seek to position (VODs only).
351
+
352
+ ```javascript
353
+ twitchPlugin.seek(120).then(() => {
354
+ console.log('Seeked to 2 minutes');
355
+ });
356
+ ```
357
+
358
+ **Parameters:**
359
+ - `seconds` (Number): Position in seconds
360
+
361
+ **Returns:** Promise
362
+
363
+ **Note:** Seeking only works for VODs, not live streams.
364
+
365
+ ---
366
+
367
+ #### `getCurrentTime()`
368
+ Get current playback position.
369
+
370
+ ```javascript
371
+ twitchPlugin.getCurrentTime().then(time => {
372
+ console.log('Current time:', time, 'seconds');
373
+ });
374
+ ```
375
+
376
+ **Returns:** Promise<Number>
377
+
378
+ ---
379
+
380
+ #### `getDuration()`
381
+ Get video duration (VODs only).
382
+
383
+ ```javascript
384
+ twitchPlugin.getDuration().then(duration => {
385
+ console.log('Duration:', duration, 'seconds');
386
+ });
387
+ ```
388
+
389
+ **Returns:** Promise<Number>
390
+
391
+ **Note:** Returns 0 for live streams.
392
+
393
+ ---
394
+
395
+ #### `isPaused()`
396
+ Check if player is paused.
397
+
398
+ ```javascript
399
+ twitchPlugin.isPaused().then(paused => {
400
+ console.log('Is paused:', paused);
401
+ });
402
+ ```
403
+
404
+ **Returns:** Promise<Boolean>
405
+
406
+ ---
407
+
408
+ ### Volume Control
409
+
410
+ #### `setVolume(volume)`
411
+ Set volume level.
412
+
413
+ ```javascript
414
+ twitchPlugin.setVolume(0.5).then(() => {
415
+ console.log('Volume set to 50%');
416
+ });
417
+ ```
418
+
419
+ **Parameters:**
420
+ - `volume` (Number): Volume level (0-1)
421
+
422
+ **Returns:** Promise
423
+
424
+ ---
425
+
426
+ #### `getVolume()`
427
+ Get current volume.
428
+
429
+ ```javascript
430
+ twitchPlugin.getVolume().then(volume => {
431
+ console.log('Current volume:', volume);
432
+ });
433
+ ```
434
+
435
+ **Returns:** Promise<Number>
436
+
437
+ ---
438
+
439
+ #### `setMuted(muted)`
440
+ Mute or unmute.
441
+
442
+ ```javascript
443
+ twitchPlugin.setMuted(true).then(() => {
444
+ console.log('Muted');
445
+ });
446
+ ```
447
+
448
+ **Parameters:**
449
+ - `muted` (Boolean): Mute state
450
+
451
+ **Returns:** Promise
452
+
453
+ ---
454
+
455
+ #### `getMuted()`
456
+ Get muted state.
457
+
458
+ ```javascript
459
+ twitchPlugin.getMuted().then(muted => {
460
+ console.log('Is muted:', muted);
461
+ });
462
+ ```
463
+
464
+ **Returns:** Promise<Boolean>
465
+
466
+ ---
467
+
468
+ ### Quality Control
469
+
470
+ #### `getQuality()`
471
+ Get current quality.
472
+
473
+ ```javascript
474
+ twitchPlugin.getQuality().then(quality => {
475
+ console.log('Current quality:', quality);
476
+ });
477
+ ```
478
+
479
+ **Returns:** Promise<String>
480
+
481
+ ---
482
+
483
+ #### `setQuality(quality)`
484
+ Set video quality.
485
+
486
+ ```javascript
487
+ twitchPlugin.setQuality('720p60').then(() => {
488
+ console.log('Quality set to 720p60');
489
+ });
490
+ ```
491
+
492
+ **Parameters:**
493
+ - `quality` (String): Quality identifier
494
+
495
+ **Returns:** Promise
496
+
497
+ ---
498
+
499
+ #### `getQualities()`
500
+ Get available qualities.
501
+
502
+ ```javascript
503
+ twitchPlugin.getQualities().then(qualities => {
504
+ console.log('Available qualities:', qualities);
505
+ });
506
+ ```
507
+
508
+ **Returns:** Promise<Array>
509
+
510
+ ---
511
+
512
+ ### Video/Channel Management
513
+
514
+ #### `loadChannel(channel)`
515
+ Load a Twitch channel (live stream).
516
+
517
+ ```javascript
518
+ twitchPlugin.loadChannel('shroud').then(() => {
519
+ console.log('Channel loaded');
520
+ });
521
+ ```
522
+
523
+ **Parameters:**
524
+ - `channel` (String): Twitch channel name
525
+
526
+ **Returns:** Promise
527
+
528
+ ---
529
+
530
+ #### `loadVideo(videoId, timestamp)`
531
+ Load a VOD.
532
+
533
+ ```javascript
534
+ twitchPlugin.loadVideo('1234567890', '1h30m0s').then(() => {
535
+ console.log('VOD loaded');
536
+ });
537
+ ```
538
+
539
+ **Parameters:**
540
+ - `videoId` (String): Twitch video ID
541
+ - `timestamp` (String): Start time (optional, default: '0h0m0s')
542
+
543
+ **Returns:** Promise
544
+
545
+ ---
546
+
547
+ #### `loadCollection(collectionId, videoId)`
548
+ Load a collection.
549
+
550
+ ```javascript
551
+ twitchPlugin.loadCollection('abc123', '1234567890').then(() => {
552
+ console.log('Collection loaded');
553
+ });
554
+ ```
555
+
556
+ **Parameters:**
557
+ - `collectionId` (String): Collection ID
558
+ - `videoId` (String): Video ID to start from (optional)
559
+
560
+ **Returns:** Promise
561
+
562
+ ---
563
+
564
+ #### `getChannel()`
565
+ Get current channel name.
566
+
567
+ ```javascript
568
+ twitchPlugin.getChannel().then(channel => {
569
+ console.log('Current channel:', channel);
570
+ });
571
+ ```
572
+
573
+ **Returns:** Promise<String>
574
+
575
+ ---
576
+
577
+ #### `getVideo()`
578
+ Get current video ID.
579
+
580
+ ```javascript
581
+ twitchPlugin.getVideo().then(video => {
582
+ console.log('Current video:', video);
583
+ });
584
+ ```
585
+
586
+ **Returns:** Promise<String>
587
+
588
+ ---
589
+
590
+ ### Playback Stats
591
+
592
+ #### `getPlaybackStats()`
593
+ Get detailed playback statistics.
594
+
595
+ ```javascript
596
+ twitchPlugin.getPlaybackStats().then(stats => {
597
+ console.log('Playback stats:', stats);
598
+ console.log('Backend version:', stats.backendVersion);
599
+ console.log('Buffer size:', stats.bufferSize);
600
+ console.log('Codecs:', stats.codecs);
601
+ console.log('Display resolution:', stats.displayResolution);
602
+ console.log('FPS:', stats.fps);
603
+ console.log('HLS latency:', stats.hlsLatencyBroadcaster);
604
+ console.log('Playback rate:', stats.playbackRate);
605
+ console.log('Skipped frames:', stats.skippedFrames);
606
+ console.log('Video resolution:', stats.videoResolution);
607
+ });
608
+ ```
609
+
610
+ **Returns:** Promise<Object>
611
+
612
+ ---
613
+
614
+ ## 📡 Events
615
+
616
+ ### Playback Events
617
+
618
+ #### `play`
619
+ Video started playing.
620
+
621
+ ```javascript
622
+ player.addEventListener('play', () => {
623
+ console.log('Playing');
624
+ });
625
+ ```
626
+
627
+ ---
628
+
629
+ #### `playing`
630
+ Video is actively playing.
631
+
632
+ ```javascript
633
+ player.addEventListener('playing', () => {
634
+ console.log('Playing');
635
+ });
636
+ ```
637
+
638
+ ---
639
+
640
+ #### `pause`
641
+ Video paused.
642
+
643
+ ```javascript
644
+ player.addEventListener('pause', () => {
645
+ console.log('Paused');
646
+ });
647
+ ```
648
+
649
+ ---
650
+
651
+ #### `ended`
652
+ Video ended (VODs only).
653
+
654
+ ```javascript
655
+ player.addEventListener('ended', () => {
656
+ console.log('Video ended');
657
+ });
658
+ ```
659
+
660
+ ---
661
+
662
+ ### Plugin-Specific Events
663
+
664
+ #### `twitchplugin:ready`
665
+ Player is ready.
666
+
667
+ ```javascript
668
+ player.addEventListener('twitchplugin:ready', () => {
669
+ console.log('Twitch player ready');
670
+ });
671
+ ```
672
+
673
+ ---
674
+
675
+ #### `twitchplugin:playerready`
676
+ Player created and initialized.
677
+
678
+ ```javascript
679
+ player.addEventListener('twitchplugin:playerready', (data) => {
680
+ console.log('Channel:', data.channel);
681
+ console.log('Video:', data.video);
682
+ console.log('Is live:', data.isLive);
683
+ });
684
+ ```
685
+
686
+ ---
687
+
688
+ #### `twitchplugin:online`
689
+ Stream went online (for channels).
690
+
691
+ ```javascript
692
+ player.addEventListener('twitchplugin:online', () => {
693
+ console.log('Stream is now live!');
694
+ });
695
+ ```
696
+
697
+ ---
698
+
699
+ #### `twitchplugin:offline`
700
+ Stream went offline.
701
+
702
+ ```javascript
703
+ player.addEventListener('twitchplugin:offline', () => {
704
+ console.log('Stream went offline');
705
+ });
706
+ ```
707
+
708
+ ---
709
+
710
+ #### `twitchplugin:playbackblocked`
711
+ Playback blocked by browser (autoplay restrictions).
712
+
713
+ ```javascript
714
+ player.addEventListener('twitchplugin:playbackblocked', () => {
715
+ console.log('Playback blocked - user interaction required');
716
+ // Show play button or prompt user
717
+ });
718
+ ```
719
+
720
+ ---
721
+
722
+ #### `twitchplugin:channelloaded`
723
+ New channel loaded.
724
+
725
+ ```javascript
726
+ player.addEventListener('twitchplugin:channelloaded', (data) => {
727
+ console.log('Channel loaded:', data.channel);
728
+ });
729
+ ```
730
+
731
+ ---
732
+
733
+ #### `twitchplugin:videoloaded`
734
+ New VOD loaded.
735
+
736
+ ```javascript
737
+ player.addEventListener('twitchplugin:videoloaded', (data) => {
738
+ console.log('Video loaded:', data.video);
739
+ console.log('Timestamp:', data.timestamp);
740
+ });
741
+ ```
742
+
743
+ ---
744
+
745
+ ## Live Streams vs VODs
746
+
747
+ ### Live Streams
748
+
749
+ **Characteristics:**
750
+ - Real-time playback
751
+ - No seeking (always "live")
752
+ - Duration is 0
753
+ - Can detect online/offline status
754
+ - May have latency
755
+
756
+ **Example:**
757
+ ```javascript
758
+ plugins: {
759
+ twitch: {
760
+ channel: 'shroud',
761
+ parent: ['yourdomain.com']
762
+ }
763
+ }
764
+ ```
765
+
766
+ ---
767
+
768
+ ### VODs (Video on Demand)
769
+
770
+ **Characteristics:**
771
+ - Full playback control
772
+ - Seeking available
773
+ - Has defined duration
774
+ - Start from specific timestamp
775
+ - No online/offline events
776
+
777
+ **Example:**
778
+ ```javascript
779
+ plugins: {
780
+ twitch: {
781
+ video: '1234567890',
782
+ time: '1h30m0s', // Start at 1h30m
783
+ parent: ['yourdomain.com']
784
+ }
785
+ }
786
+ ```
787
+
788
+ ---
789
+
790
+ ### Check Stream Type
791
+
792
+ ```javascript
793
+ const twitchPlugin = player.getPlugin('twitch');
794
+
795
+ if (player.isTwitchLive()) {
796
+ console.log('Watching a live stream');
797
+ } else {
798
+ console.log('Watching a VOD');
799
+ }
800
+ ```
801
+
802
+ ---
803
+
804
+ ## Examples
805
+
806
+ ### Example 1: Channel Switcher
807
+
808
+ ```html
809
+ <video id="myVideo" class="video-player"></video>
810
+
811
+ <div id="channel-switcher">
812
+ <button onclick="switchChannel('shroud')">Shroud</button>
813
+ <button onclick="switchChannel('ninja')">Ninja</button>
814
+ <button onclick="switchChannel('pokimane')">Pokimane</button>
815
+ </div>
816
+
817
+ <script>
818
+ const player = new MYETVPlayer('myVideo', {
819
+ plugins: {
820
+ twitch: {
821
+ parent: ['yourdomain.com']
822
+ }
823
+ }
824
+ });
825
+
826
+ const twitchPlugin = player.getPlugin('twitch');
827
+
828
+ function switchChannel(channel) {
829
+ twitchPlugin.loadChannel(channel).then(() => {
830
+ console.log('Switched to:', channel);
831
+ });
832
+ }
833
+ </script>
834
+ ```
835
+
836
+ ---
837
+
838
+ ### Example 2: VOD Player with Controls
839
+
840
+ ```html
841
+ <video id="myVideo" class="video-player"></video>
842
+
843
+ <div id="custom-controls">
844
+ <button id="playBtn">Play</button>
845
+ <button id="pauseBtn">Pause</button>
846
+ <input type="range" id="seekBar" min="0" max="100" value="0">
847
+ <span id="timeDisplay">0:00 / 0:00</span>
848
+ </div>
849
+
850
+ <script>
851
+ const player = new MYETVPlayer('myVideo', {
852
+ plugins: {
853
+ twitch: {
854
+ video: '1234567890',
855
+ parent: ['yourdomain.com']
856
+ }
857
+ }
858
+ });
859
+
860
+ const twitchPlugin = player.getPlugin('twitch');
861
+
862
+ // Play button
863
+ document.getElementById('playBtn').onclick = () => {
864
+ twitchPlugin.play();
865
+ };
866
+
867
+ // Pause button
868
+ document.getElementById('pauseBtn').onclick = () => {
869
+ twitchPlugin.pause();
870
+ };
871
+
872
+ // Seek bar
873
+ document.getElementById('seekBar').oninput = (e) => {
874
+ twitchPlugin.getDuration().then(duration => {
875
+ const seekTo = (e.target.value / 100) * duration;
876
+ twitchPlugin.seek(seekTo);
877
+ });
878
+ };
879
+
880
+ // Update time display
881
+ setInterval(() => {
882
+ Promise.all([
883
+ twitchPlugin.getCurrentTime(),
884
+ twitchPlugin.getDuration()
885
+ ]).then(([current, duration]) => {
886
+ const seekBar = document.getElementById('seekBar');
887
+ seekBar.value = (current / duration) * 100;
888
+
889
+ document.getElementById('timeDisplay').textContent =
890
+ `${formatTime(current)} / ${formatTime(duration)}`;
891
+ });
892
+ }, 1000);
893
+
894
+ function formatTime(seconds) {
895
+ const mins = Math.floor(seconds / 60);
896
+ const secs = Math.floor(seconds % 60);
897
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
898
+ }
899
+ </script>
900
+ ```
901
+
902
+ ---
903
+
904
+ ### Example 3: Stream Status Indicator
905
+
906
+ ```html
907
+ <video id="myVideo" class="video-player"></video>
908
+
909
+ <div id="status">
910
+ <span id="indicator" style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: gray;"></span>
911
+ <span id="status-text">Checking...</span>
912
+ </div>
913
+
914
+ <script>
915
+ const player = new MYETVPlayer('myVideo', {
916
+ plugins: {
917
+ twitch: {
918
+ channel: 'shroud',
919
+ parent: ['yourdomain.com']
920
+ }
921
+ }
922
+ });
923
+
924
+ const indicator = document.getElementById('indicator');
925
+ const statusText = document.getElementById('status-text');
926
+
927
+ // Stream went online
928
+ player.addEventListener('twitchplugin:online', () => {
929
+ indicator.style.background = '#00ff00';
930
+ statusText.textContent = 'LIVE';
931
+ });
932
+
933
+ // Stream went offline
934
+ player.addEventListener('twitchplugin:offline', () => {
935
+ indicator.style.background = '#ff0000';
936
+ statusText.textContent = 'OFFLINE';
937
+ });
938
+
939
+ // Player ready
940
+ player.addEventListener('twitchplugin:ready', () => {
941
+ indicator.style.background = '#00ff00';
942
+ statusText.textContent = 'LIVE';
943
+ });
944
+ </script>
945
+ ```
946
+
947
+ ---
948
+
949
+ ### Example 4: Quality Selector
950
+
951
+ ```html
952
+ <video id="myVideo" class="video-player"></video>
953
+
954
+ <div id="quality-selector"></div>
955
+
956
+ <script>
957
+ const player = new MYETVPlayer('myVideo', {
958
+ plugins: {
959
+ twitch: {
960
+ channel: 'shroud',
961
+ parent: ['yourdomain.com']
962
+ }
963
+ }
964
+ });
965
+
966
+ const twitchPlugin = player.getPlugin('twitch');
967
+
968
+ // Wait for player ready
969
+ player.addEventListener('twitchplugin:ready', () => {
970
+ // Get available qualities
971
+ twitchPlugin.getQualities().then(qualities => {
972
+ const selector = document.getElementById('quality-selector');
973
+
974
+ qualities.forEach(quality => {
975
+ const btn = document.createElement('button');
976
+ btn.textContent = quality;
977
+ btn.onclick = () => {
978
+ twitchPlugin.setQuality(quality).then(() => {
979
+ console.log('Quality set to:', quality);
980
+ });
981
+ };
982
+ selector.appendChild(btn);
983
+ });
984
+ });
985
+ });
986
+ </script>
987
+ ```
988
+
989
+ ---
990
+
991
+ ### Example 5: Playback Stats Display
992
+
993
+ ```html
994
+ <video id="myVideo" class="video-player"></video>
995
+
996
+ <div id="stats" style="font-family: monospace; font-size: 12px;"></div>
997
+
998
+ <script>
999
+ const player = new MYETVPlayer('myVideo', {
1000
+ plugins: {
1001
+ twitch: {
1002
+ channel: 'shroud',
1003
+ parent: ['yourdomain.com']
1004
+ }
1005
+ }
1006
+ });
1007
+
1008
+ const twitchPlugin = player.getPlugin('twitch');
1009
+
1010
+ // Update stats every second
1011
+ setInterval(() => {
1012
+ twitchPlugin.getPlaybackStats().then(stats => {
1013
+ const statsDiv = document.getElementById('stats');
1014
+ statsDiv.innerHTML = `
1015
+ Resolution: ${stats.videoResolution}<br>
1016
+ FPS: ${stats.fps}<br>
1017
+ Codecs: ${stats.codecs}<br>
1018
+ Bitrate: ${(stats.bitrate / 1000).toFixed(2)} Mbps<br>
1019
+ Buffer: ${stats.bufferSize}s<br>
1020
+ Skipped Frames: ${stats.skippedFrames}
1021
+ `;
1022
+ });
1023
+ }, 1000);
1024
+ </script>
1025
+ ```
1026
+
1027
+ ---
1028
+
1029
+ ## FAQ
1030
+
1031
+ ### Q: What's the difference between channel and video?
1032
+
1033
+ **A:**
1034
+ - `channel`: Live stream from a Twitch channel (e.g., 'shroud')
1035
+ - `video`: VOD (Video on Demand) with a specific ID (e.g., '1234567890')
1036
+
1037
+ ---
1038
+
1039
+ ### Q: Why do I see "This video is not available on this domain"?
1040
+
1041
+ **A:** Your `parent` domains are incorrect. Make sure to include the exact hostname where your page is hosted.
1042
+
1043
+ ```javascript
1044
+ // Correct
1045
+ parent: ['mysite.com']
1046
+
1047
+ // Wrong
1048
+ parent: ['https://mysite.com'] // No protocol!
1049
+ parent: ['mysite.com/page'] // No path!
1050
+ ```
1051
+
1052
+ ---
1053
+
1054
+ ### Q: Can I seek in live streams?
1055
+
1056
+ **A:** No, seeking only works for VODs. Live streams are always "live" at the current moment.
1057
+
1058
+ ---
1059
+
1060
+ ### Q: How do I get a video ID from a Twitch URL?
1061
+
1062
+ **A:** The video ID is in the URL:
1063
+ - URL: `https://www.twitch.tv/videos/1234567890`
1064
+ - ID: `1234567890`
1065
+
1066
+ ---
1067
+
1068
+ ### Q: Can I hide Twitch branding?
1069
+
1070
+ **A:** No, Twitch requires their branding to remain visible as per their Terms of Service.
1071
+
1072
+ ---
1073
+
1074
+ ### Q: Does this work with Twitch clips?
1075
+
1076
+ **A:** The current version supports channels (live) and videos (VODs). Clips support may be added in a future update.
1077
+
1078
+ ---
1079
+
1080
+ ### Q: How do I handle autoplay restrictions?
1081
+
1082
+ **A:** Modern browsers block autoplay. Listen for the `playbackblocked` event:
1083
+
1084
+ ```javascript
1085
+ player.addEventListener('twitchplugin:playbackblocked', () => {
1086
+ // Show a play button or prompt user
1087
+ alert('Click to play');
1088
+ });
1089
+ ```
1090
+
1091
+ ---
1092
+
1093
+ ## Troubleshooting
1094
+
1095
+ ### Issue: Player not loading
1096
+
1097
+ **Solution:**
1098
+ 1. Check `parent` domains are correct
1099
+ 2. Verify channel name or video ID is valid
1100
+ 3. Check browser console for errors
1101
+ 4. Ensure Twitch isn't blocked by firewall/adblocker
1102
+
1103
+ ---
1104
+
1105
+ ### Issue: "This video is not available"
1106
+
1107
+ **Solution:**
1108
+ - Add correct parent domains
1109
+ - Include both `example.com` and `www.example.com` if needed
1110
+ - Don't include protocol or paths
1111
+
1112
+ ---
1113
+
1114
+ ### Issue: Seeking not working
1115
+
1116
+ **Solution:**
1117
+ - Seeking only works for VODs, not live streams
1118
+ - Check if `player.isTwitchLive()` returns `false`
1119
+
1120
+ ---
1121
+
1122
+ ### Issue: Autoplay not working
1123
+
1124
+ **Solution:**
1125
+ - Browsers block autoplay
1126
+ - Set `muted: true` for autoplay to work
1127
+ - Handle `playbackblocked` event
1128
+
1129
+ ```javascript
1130
+ plugins: {
1131
+ twitch: {
1132
+ channel: 'shroud',
1133
+ parent: ['yourdomain.com'],
1134
+ autoplay: true,
1135
+ muted: true // Required for autoplay
1136
+ }
1137
+ }
1138
+ ```
1139
+
1140
+ ---
1141
+
1142
+ ### Debug Mode
1143
+
1144
+ Enable detailed logging:
1145
+
1146
+ ```javascript
1147
+ const player = new MYETVPlayer('myVideo', {
1148
+ debug: true,
1149
+ plugins: {
1150
+ twitch: {
1151
+ channel: 'shroud',
1152
+ parent: ['yourdomain.com'],
1153
+ debug: true
1154
+ }
1155
+ }
1156
+ });
1157
+ ```
1158
+
1159
+ Debug messages appear with `Twitch Plugin:` prefix.
1160
+
1161
+ ---
1162
+
1163
+ ## Resources
1164
+
1165
+ - **MYETV Player**: [https://www.myetv.tv](https://www.myetv.tv)
1166
+ - **Twitch Developers**: [https://dev.twitch.tv/docs/embed/](https://dev.twitch.tv/docs/embed/)
1167
+ - **Twitch Player SDK**: [Twitch Embed Documentation](https://dev.twitch.tv/docs/embed/video-and-clips/)
1168
+ - **GitHub**: [MYETV Video Player Open Source](https://github.com/OskarCosimo/myetv-video-player-opensource)
1169
+ - **Author**: [https://oskarcosimo.com](https://oskarcosimo.com)
1170
+
1171
+ ---
1172
+
1173
+ ## License
1174
+
1175
+ MIT License - See main project for details.
1176
+
1177
+ ---
1178
+
1179
+ ## Contributing
1180
+
1181
+ Contributions are welcome! Please submit pull requests or open issues on GitHub.
1182
+
1183
+ ---
1184
+
1185
+ **Happy streaming!**