myetv-player 1.0.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.
Files changed (50) hide show
  1. package/.github/workflows/npm-publish.yml +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +866 -0
  4. package/build.js +189 -0
  5. package/css/README.md +1 -0
  6. package/css/myetv-player.css +13702 -0
  7. package/css/myetv-player.min.css +1 -0
  8. package/dist/README.md +1 -0
  9. package/dist/myetv-player.js +6408 -0
  10. package/dist/myetv-player.min.js +6183 -0
  11. package/package.json +27 -0
  12. package/plugins/README.md +1 -0
  13. package/plugins/google-analytics/README.md +1 -0
  14. package/plugins/google-analytics/myetv-player-g-analytics-plugin.js +548 -0
  15. package/plugins/youtube/README.md +1 -0
  16. package/plugins/youtube/myetv-player-youtube-plugin.js +418 -0
  17. package/scss/README.md +1 -0
  18. package/scss/_audio-player.scss +21 -0
  19. package/scss/_base.scss +131 -0
  20. package/scss/_controls.scss +30 -0
  21. package/scss/_loading.scss +111 -0
  22. package/scss/_menus.scss +4070 -0
  23. package/scss/_mixins.scss +112 -0
  24. package/scss/_poster.scss +8 -0
  25. package/scss/_progress-bar.scss +2203 -0
  26. package/scss/_resolution.scss +68 -0
  27. package/scss/_responsive.scss +1532 -0
  28. package/scss/_themes.scss +30 -0
  29. package/scss/_title-overlay.scss +2262 -0
  30. package/scss/_tooltips.scss +7 -0
  31. package/scss/_variables.scss +49 -0
  32. package/scss/_video.scss +2401 -0
  33. package/scss/_volume.scss +1981 -0
  34. package/scss/_watermark.scss +8 -0
  35. package/scss/myetv-player.scss +51 -0
  36. package/scss/package.json +16 -0
  37. package/src/README.md +1 -0
  38. package/src/chapters.js +521 -0
  39. package/src/controls.js +1005 -0
  40. package/src/core.js +1650 -0
  41. package/src/events.js +330 -0
  42. package/src/fullscreen.js +82 -0
  43. package/src/i18n.js +348 -0
  44. package/src/playlist.js +177 -0
  45. package/src/plugins.js +384 -0
  46. package/src/quality.js +921 -0
  47. package/src/streaming.js +346 -0
  48. package/src/subtitles.js +426 -0
  49. package/src/utils.js +51 -0
  50. package/src/watermark.js +195 -0
package/README.md ADDED
@@ -0,0 +1,866 @@
1
+ # MYETV Audio/Video Player Open Source
2
+
3
+ A modern and complete HTML5 + JavaScript + css video player with custom controls, multiple quality support, subtitles, Picture-in-Picture and much more.
4
+
5
+ ## Features
6
+
7
+ - **Custom controls** with intelligent auto-hide
8
+ - Built to work with both .mp4 or .webm or .mp3 (**download streaming**) but also with hls or dash (**adaptive streaming**)
9
+ - **Multiple video qualities** with automatic selection based on connection
10
+ - **Subtitles** with multiple track support
11
+ - **Chapters** with images and customized colors
12
+ - Custom **Plugins** to enhance player's functionality
13
+ - **Picture-in-Picture** mode (where supported)
14
+ - **Complete keyboard controls**
15
+ - **Internationalization** (i18n) multilingual
16
+ - **Responsive design** for mobile and desktop devices
17
+ - **Customizable** title overlay
18
+ - **Customizable** brand logo in the controlbar
19
+ - **Debug mode** for developers
20
+ - **Extensive APIs** for programmatic control
21
+
22
+ ## Demo page
23
+ [MYETV Video Player Demo Page: https://oskarcosimo.com/myetv-video-player/myetv-player-demo.html](https://oskarcosimo.com/myetv-video-player/myetv-player-demo.html)
24
+
25
+ ## Installation
26
+
27
+ ### Include Required Files
28
+
29
+ ```
30
+ <!-- Player CSS -->
31
+ <link rel="stylesheet" href="css/myetv-player.min.css">
32
+ <!-- Player JavaScript -->
33
+ <script src="dist/myetv-player.min.js"></script>
34
+ ```
35
+
36
+ ## Basic Usage
37
+
38
+ ### HTML
39
+
40
+ ```
41
+ <video id="my-video" width="800" height="450">
42
+ <source src="video-480p.mp4" type="video/mp4" data-quality="480p">
43
+ <source src="video-720p.mp4" type="video/mp4" data-quality="720p">
44
+ <source src="video-1080p.mp4" type="video/mp4" data-quality="1080p">
45
+ <!-- Optional subtitles -->
46
+ <track kind="subtitles" src="subtitles-en.vtt" srclang="en" label="English">
47
+ <track kind="subtitles" src="subtitles-it.vtt" srclang="it" label="Italiano">
48
+ </video>
49
+
50
+ // Basic initialization
51
+ const player = new MYETVvideoplayer('my-video');
52
+
53
+ // Initialization with options
54
+ const player = new MYETVvideoplayer('my-video', {
55
+ autoplay: true,
56
+ defaultQuality: '720p',
57
+ showTitleOverlay: true,
58
+ videoTitle: 'My Video',
59
+ language: 'en',
60
+ debug: false
61
+ });
62
+ ```
63
+
64
+ ## Initialization Options
65
+
66
+ | Option | Type | Default | Description |
67
+ |--------|------|---------|-------------|
68
+ | `poster` | string | `''` | URL of a cover image (optional) |
69
+ | `showPosterOnEnd` | boolean | `false` | Show the cover image when the video ends |
70
+ | `showQualitySelector` | boolean | `true` | Show video quality selector |
71
+ | `showSpeedControl` | boolean | `true` | Show playback speed controls |
72
+ | `showFullscreen` | boolean | `true` | Show fullscreen button |
73
+ | `showPictureInPicture` | boolean | `true` | Show Picture-in-Picture button |
74
+ | `showSubtitles` | boolean | `true` | Show subtitles controls (the button) - it is automatically true only if subtitles track are detected |
75
+ | `subtitlesEnabled` | boolean | `false` | Enable/Disable subtitles at player ready |
76
+ | `chapters` | string | json | Enable/Disable chapters: chapter can be in json format or string format (see below) |
77
+ | `plugins` | string | json | Add a customized plugin to the player to extend its functionality (see below) |
78
+ | `showSeekTooltip` | boolean | `true` | Show tooltip during seek |
79
+ | `volumeSlider` | string | `horizontal` | Volume slider 'horizontal' or 'vertical': the horizontal slider is always visible and have the automatic fallback to vertical under 550px of width; the vertical slider is only vertical at any width and automatically disapper if mouse is not hover the volume button |
80
+ | `autoplay` | boolean | `false` | Start video automatically |
81
+ | `loop` | boolean | `false` | Optional if the video should loop
82
+ | `resolution` | string | `normal` | resolution type: "normal" same resolution of the native video; "4:3"; "16:9"; "stretched" the video will be stretched in all the container; "fit-to-screen" the video will fit the screen but can be cutted; "scale-to-fit" fit the screen but preserve aspect ration and not cut |
83
+ | `autoHide` | boolean | `true` | Auto-hide controls |
84
+ | `autoHideDelay` | number | `3000` | Auto-hide delay in milliseconds |
85
+ | `pauseClick` | boolean | `true` | Enable or disable the click on the video to pause/resume |
86
+ | `doubleTapPause` | boolean | `true` | First touch shows controls, second touch pauses (usefull on touch devices) |
87
+ | `keyboardControls` | boolean | `true` | Enable keyboard controls |
88
+ | `defaultQuality` | string | `'auto'` | Default video quality |
89
+ | `showTitleOverlay` | boolean | `false` | Show video title overlay |
90
+ | `videoTitle` | string | `''` | Title to show in overlay |
91
+ | `persistentTitle` | boolean | `false` | Keep title always visible |
92
+ | `language` | string | `en` | Interface language code |
93
+ | `brandLogoEnabled` | boolean | `false` | Show/hide the brand logo in the controlbar |
94
+ | `brandLogoUrl` | string | `''` | Brand logo url in the controlbar (png, jpg, gif) - image height 44px - image width 120px |
95
+ | `brandLogoLinkUrl` | string | `''` | Optional URL to open in a new page when clicking the brand logo in the controlbar
96
+ | `watermarkUrl` | string | `''` | Optional URL of the image watermark over the video, reccomended dimension: width: 180px, height: 100px
97
+ | `watermarkLink` | string | `''` | Optional URL to open in a new page when clicking the watermark logo in the video
98
+ | `watermarkPosition` | string | `''` | Optional where to show the watermark logo in the video (values are: top-left, top-right, bottom-left, bottom-right)
99
+ | `watermarkTitle` | string | `''` | Optional title to show when the mouse is over the watermark logo in the video
100
+ | `hideWatermark` | boolean | `true` | Optional hide watermark logo with the controlbar or show the watermark logo always visible
101
+ | `playlistEnabled` | boolean | `false` | Optional if the playlist of video is enabled (html structured)
102
+ | `playlistAutoPlay` | boolean | `false` | Optional if the playlist should autoplay
103
+ | `playlistLoop` | boolean | `false` | Optional if the playlist should loop
104
+ | `adaptiveStreaming` | boolean | `false` | Enable HLS/DASH adaptive streaming
105
+ | `adaptiveQualityControl` | boolean | `false` | Enable the menu quality with adaptive streaming
106
+ | `audiofile` | boolean | `false` | Optional if the file is only audio (no video) |
107
+ | `audiowave` | boolean | `false` | Optional if the file is only audio, show the audio wave as video (with the browser Web Audio API) |
108
+ | `debug` | boolean | `false` | Enable debug logs |
109
+
110
+
111
+ ## API Methods
112
+ ### Basic Controls
113
+ ```
114
+ // Playback
115
+ player.play(); // Start playback
116
+ player.pause(); // Pause playback
117
+ player.togglePlayPause(); // Toggle play/pause
118
+
119
+ // Volume
120
+ player.setVolume(0.8); // Set volume (0-1)
121
+ player.getVolume(); // Get current volume
122
+ player.toggleMute(); // Toggle mute
123
+ player.setMuted(true); // Set mute
124
+ ```
125
+ ### Time Controls
126
+ ```
127
+ // Position
128
+ player.setCurrentTime(120); // Go to second 120
129
+ player.getCurrentTime(); // Current position
130
+ player.getDuration(); // Total duration
131
+ player.skipTime(10); // Skip 10 seconds forward
132
+ player.skipTime(-10); // Skip 10 seconds backward
133
+ ```
134
+ ### Poster Image (cover image)
135
+ ```
136
+ // Set poster after initialization
137
+ player.setPoster('https://example.com/poster.jpg');
138
+
139
+ // Get current poster
140
+ const currentPoster = player.getPoster();
141
+
142
+ // Remove poster
143
+ player.removePoster();
144
+
145
+ // Toggle poster visibility
146
+ player.togglePoster(true); // Show
147
+ player.togglePoster(false); // Hide
148
+
149
+ // Check if poster is visible
150
+ if (player.isPosterVisible()) {
151
+ console.log('Poster is visible');
152
+ }
153
+ ```
154
+ ### Quality Controls
155
+ ```
156
+ // Video quality
157
+ player.setDefaultQuality('720p'); // Set default quality
158
+ player.setQuality('1080p'); // Change quality
159
+ player.getSelectedQuality(); // Selected quality
160
+ player.getCurrentPlayingQuality(); // Actual playing quality
161
+ player.enableAutoQuality(); // Enable automatic selection
162
+ ```
163
+ ### Subtitle Controls
164
+ ```
165
+ // Subtitles
166
+ player.toggleSubtitles(); // Toggle subtitles
167
+ player.enableSubtitleTrack(0); // Enable subtitle track
168
+ player.disableSubtitles(); // Disable subtitles
169
+ player.getAvailableSubtitles(); // List available subtitles
170
+ ```
171
+ ### Chapters Controls
172
+ ```
173
+ // Get current chapter
174
+ const current = player.getCurrentChapter();
175
+
176
+ // Navigate chapters
177
+ player.nextChapter();
178
+ player.previousChapter();
179
+ player.jumpToChapter(2);
180
+
181
+ // Get all chapters
182
+ const allChapters = player.getChapters();
183
+
184
+ // Update chapters dynamically
185
+ player.setChapters([...]);
186
+
187
+ // Clear chapters
188
+ player.clearChapters();
189
+ ```
190
+ ### Plugins Controls
191
+ ```
192
+ // Add plugins dynamically
193
+ player.usePlugin('youtube', {
194
+ apiKey: 'your-api-key'
195
+ });
196
+
197
+ // Upload YouTube videos (based on the YouTube plugin example)
198
+ player.loadYouTubeVideo('dQw4w9WgXcQ');
199
+
200
+ // Check if a plugin is active
201
+ if (player.hasPlugin('youtube')) {
202
+ console.log('YouTube plugin is active');
203
+ }
204
+
205
+ // Get plugin instance
206
+ const youtubePlugin = player.getPlugin('youtube');
207
+
208
+ // Remove plugins from the player
209
+ player.removePlugin('youtube');
210
+ ```
211
+ ### Screen Controls
212
+ ```
213
+ // Fullscreen and Picture-in-Picture
214
+ player.toggleFullscreen(); // Toggle fullscreen
215
+ player.enterFullscreen(); // Enter fullscreen
216
+ player.exitFullscreen(); // Exit fullscreen
217
+ player.togglePictureInPicture(); // Toggle Picture-in-Picture
218
+ ```
219
+ ### Brand Logo Controls
220
+ ```
221
+ // Fullscreen and Picture-in-Picture
222
+ player.setBrandLogo(enabled, url, linkUrl) //change brand logo dynamically
223
+ player.getBrandLogoSettings() //get current brand logo settings
224
+ ```
225
+ ### Watermark Logo Controls
226
+ ```
227
+ // Change watermark dynamically
228
+ player.setWatermark(
229
+ 'https://example.com/new-logo.png',
230
+ 'https://example.com/promo',
231
+ 'topleft',
232
+ 'Special promotion'
233
+ );
234
+
235
+ // Change only position
236
+ player.setWatermarkPosition('topright');
237
+
238
+ // Get current settings
239
+ const settings = player.getWatermarkSettings();
240
+ console.log(settings);
241
+
242
+ // Remove watermark
243
+ player.removeWatermark();
244
+
245
+ //hide with the controlbar or always show the watermark logo
246
+ player.setWatermarkAutoHide(false);
247
+ ```
248
+ ### Playlist Controls
249
+ ```
250
+ player.nextVideo(); // Next Video
251
+ player.prevVideo(); // Previous Video
252
+ player.goToPlaylistIndex(2); // Go to the specific video
253
+ player.getPlaylistInfo(); // Info Playlist
254
+ player.setPlaylistOptions({loop:true}); // Playlist Options
255
+ ```
256
+ ### Resolution Controls
257
+ ```
258
+ player.setResolution("4:3"); // Change to 4:3
259
+ player.setResolution("16:9"); // Change to 16:9
260
+ player.setResolution("stretched"); // Change to stretched: Stretch the video to the entire container
261
+ player.setResolution("fit-to-screen"); // Change to fit to screen: It fits the screen, can cut parts of the video
262
+ player.setResolution("scale-to-fit"); // Intelligently fit to screen without cut video parts
263
+ console.log(player.getCurrentResolution()); // Get current resolution
264
+ ```
265
+ ## API Events
266
+ The MYETV Video Player includes a comprehensive custom event system that allows you to monitor all player state changes in real-time.
267
+ ### on played
268
+ Description: Triggered when the video starts playing
269
+ When: User presses play or video starts automatically
270
+ ```
271
+ player.addEventListener('played', (event) => {
272
+ console.log('Video started!', {
273
+ currentTime: event.currentTime,
274
+ duration: event.duration
275
+ });
276
+ });
277
+ ```
278
+ ### on paused
279
+ Description: Triggered when the video is pause
280
+ When: User presses pause or video stops
281
+ ```
282
+ player.addEventListener('paused', (event) => {
283
+ console.log('Video paused at:', event.currentTime + 's');
284
+ });
285
+ ```
286
+ ### on ended
287
+ Description: Triggered when the video is ended
288
+ When: Video is ended
289
+ ```
290
+ player.addEventListener('ended', (e) => {
291
+ console.log('Video terminato!', e.currentTime, e.duration, e.playlistInfo);
292
+ });
293
+ ```
294
+ ### on subtitle change
295
+ Description: Triggered when subtitles are enabled/disabled or track changes
296
+ When: User toggles subtitles or switches subtitle tracks
297
+ ```
298
+ player.addEventListener('subtitlechange', (event) => {
299
+ if (event.enabled) {
300
+ console.log('Subtitles enabled:', event.trackLabel);
301
+ } else {
302
+ console.log('Subtitles disabled');
303
+ }
304
+ });
305
+ ```
306
+ ### on chapters change
307
+ Description: Triggered when chapters are changes
308
+ When: User switches chapters tracks
309
+ ```
310
+ player.on('chapterchange', (data) => {
311
+ console.log('Chapter changed:', data.chapter.title);
312
+ });
313
+ ```
314
+ ### on pip change
315
+ Description: Triggered when Picture-in-Picture mode changes
316
+ When: Video enters or exits PiP mode
317
+ ```
318
+ player.addEventListener('pipchange', (event) => {
319
+ console.log('Picture-in-Picture:', event.active ? 'Activated' : 'Deactivated');
320
+ });
321
+ ```
322
+ ### on fullscreen change
323
+ Description: Triggered when fullscreen mode changes
324
+ When: Player enters or exits fullscreen mode
325
+ ```
326
+ player.addEventListener('fullscreenchange', (event) => {
327
+ console.log('Fullscreen:', event.active ? 'Activated' : 'Deactivated');
328
+ });
329
+ ```
330
+ ### on speed change
331
+ Description: Triggered when playback speed changes
332
+ When: User modifies playback speed (0.5x, 1x, 1.5x, 2x, etc.)
333
+ ```
334
+ player.addEventListener('speedchange', (event) => {
335
+ console.log('Speed changed to:', event.speed + 'x');
336
+ });
337
+ ```
338
+ ### on time update
339
+ Description: Triggered during playback to update progress
340
+ When: Every 250ms during playback (throttled for performance)
341
+ ```
342
+ player.addEventListener('timeupdate', (event) => {
343
+ console.log('Progress:', event.progress.toFixed(1) + '%');
344
+ // Update custom progress bar
345
+ updateProgressBar(event.progress);
346
+ });
347
+ ```
348
+ ### on volumechange
349
+ Description: Triggered when volume or mute state changes
350
+ When: User modifies volume or toggles mute
351
+ ```
352
+ player.addEventListener('volumechange', (event) => {
353
+ if (event.muted) {
354
+ console.log('Audio muted');
355
+ } else {
356
+ console.log('Volume:', Math.round(event.volume * 100) + '%');
357
+ }
358
+ });
359
+ ```
360
+ ### Playlist API
361
+ ```
362
+ player.addEventListener('playlistchange', (e) => {
363
+ console.log(`From "${e.fromTitle}" to "${e.toTitle}"`);
364
+ });
365
+ ```
366
+ ### Main APIs
367
+ getEventData()
368
+ Returns all requested state data in a single object:
369
+ ```
370
+ const state = player.getEventData();
371
+ console.log(state);
372
+ /* Output:
373
+ {
374
+ played: true,
375
+ paused: false,
376
+ subtitleEnabled: false,
377
+ pipMode: false,
378
+ fullscreenMode: false,
379
+ speed: 1,
380
+ controlBarLength: 45.23,
381
+ volumeIsMuted: false,
382
+ duration: 3600,
383
+ volume: 0.8,
384
+ quality: "1080p",
385
+ buffered: 120.5
386
+ }
387
+ */
388
+ ```
389
+ ### Event Listener Management
390
+ ```
391
+ // Add listener
392
+ player.addEventListener('played', callback);
393
+
394
+ // Remove listener
395
+ player.removeEventListener('played', callback);
396
+
397
+ // Complete example
398
+ const onVideoPlay = (event) => {
399
+ console.log('Video started!', event.currentTime);
400
+ };
401
+
402
+ player.addEventListener('played', onVideoPlay);
403
+ // ... later
404
+ player.removeEventListener('played', onVideoPlay);
405
+ ```
406
+ ### Complete Example
407
+ ```
408
+ // Initialize the player
409
+ const player = new MYETVvideoplayer('myVideo', {
410
+ debug: true,
411
+ autoplay: false
412
+ });
413
+
414
+ // Monitor all main events
415
+ player.addEventListener('played', (e) => {
416
+ updateUI('playing', e.currentTime);
417
+ });
418
+
419
+ player.addEventListener('paused', (e) => {
420
+ updateUI('paused', e.currentTime);
421
+ });
422
+
423
+ player.addEventListener('timeupdate', (e) => {
424
+ document.getElementById('progress').textContent =
425
+ `${e.currentTime.toFixed(0)}s / ${e.duration.toFixed(0)}s`;
426
+ });
427
+
428
+ player.addEventListener('volumechange', (e) => {
429
+ document.getElementById('volume-indicator').textContent =
430
+ e.muted ? 'muted' : `volume: ${Math.round(e.volume * 100)}%`;
431
+ });
432
+
433
+ // Helper function to update UI
434
+ function updateUI(state, time) {
435
+ document.getElementById('player-status').textContent =
436
+ `Status: ${state} at ${time.toFixed(1)}s`;
437
+ }
438
+ ```
439
+ ### Technical Notes
440
+ Performance: The timeupdate event is throttled to 250ms to avoid overload
441
+
442
+ Compatibility: All events maintain compatibility with existing code
443
+
444
+ Debug: Enable debug: true in options to see event logs
445
+
446
+ Error Handling: Errors in callbacks don't interrupt the player
447
+
448
+ ### Event Data Reference
449
+
450
+ | Property | Type | Description |
451
+ |:---------|:----:|:------------|
452
+ | `played` | `boolean` | Video is currently playing |
453
+ | `paused` | `boolean` | Video is currently paused |
454
+ | `subtitleEnabled` | `boolean` | Subtitles are enabled |
455
+ | `pipMode` | `boolean` | Picture-in-Picture is active |
456
+ | `fullscreenMode` | `boolean` | Fullscreen mode is active |
457
+ | `speed` | `number` | Current playback speed |
458
+ | `controlBarLength` | `number` | Current video time in seconds |
459
+ | `volumeIsMuted` | `boolean` | Audio is muted |
460
+ | `duration` | `number` | Total video duration |
461
+ | `volume` | `number` | Volume level (0-1) |
462
+ | `quality` | `string` | Current video quality |
463
+ | `buffered` | `number` | Buffered time in seconds |
464
+
465
+ ## Keyboard Controls
466
+
467
+ | Key | Action |
468
+ |-----|--------|
469
+ | `Space` | Play/Pause |
470
+ | `M` | Mute/Unmute |
471
+ | `F` | Fullscreen |
472
+ | `P` | Picture-in-Picture |
473
+ | `S` | Toggle subtitles |
474
+ | `T` | Toggle title overlay |
475
+ | `N` | Next video in playlist |
476
+ | `P` | Previous video in playlist |
477
+ | `D` | Enable/disable debug |
478
+ | `←` | Backward 10 seconds |
479
+ | `→` | Forward 10 seconds |
480
+ | `↑` | Increase volume |
481
+ | `↓` | Decrease volume |
482
+
483
+ ## CSS Customization
484
+
485
+ The MYETV Video Player is fully customizable using CSS variables and themes. The player includes a comprehensive set of CSS custom properties that allow you to modify colors, sizes, spacing, and animations without touching the core stylesheet.
486
+
487
+ ### CSS Variables
488
+
489
+ The player uses CSS custom properties (variables) for easy theming:
490
+ ```
491
+ .video-wrapper {
492
+ /* Primary Colors */
493
+ --player-primary-color: goldenrod;
494
+ --player-primary-hover: #daa520;
495
+ --player-primary-dark: #b8860b;
496
+ /* Control Colors */
497
+ --player-button-color: white;
498
+ --player-button-hover: rgba(255, 255, 255, 0.1);
499
+ --player-button-active: rgba(255, 255, 255, 0.2);
500
+
501
+ /* Text Colors */
502
+ --player-text-color: white;
503
+ --player-text-secondary: rgba(255, 255, 255, 0.8);
504
+
505
+ /* Background Colors */
506
+ --player-bg-primary: #000;
507
+ --player-bg-controls: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.8) 100%);
508
+ --player-bg-title-overlay: linear-gradient(180deg, rgba(0, 0, 0, 0.8) 0%, transparent 100%);
509
+ --player-bg-menu: rgba(20, 20, 20, 0.95);
510
+
511
+ /* Dimensions */
512
+ --player-border-radius: 12px;
513
+ --player-progress-height: 6px;
514
+ --player-volume-height: 4px;
515
+ --player-icon-size: 20px;
516
+
517
+ /* Transitions */
518
+ --player-transition-fast: 0.2s ease;
519
+ --player-transition-normal: 0.3s ease;
520
+ }
521
+ ```
522
+ ### Pre-built Themes
523
+
524
+ The player includes several pre-built themes that you can apply:
525
+
526
+ #### Blue Theme
527
+ ```
528
+ .video-wrapper.player-theme-blue {
529
+ /* Automatically uses blue color scheme */
530
+ }
531
+ ```
532
+ #### Green Theme
533
+ ```
534
+ .video-wrapper.player-theme-green {
535
+ /* Automatically uses green color scheme */
536
+ }
537
+ ```
538
+ #### Red Theme
539
+ ```
540
+ .video-wrapper.player-theme-red {
541
+ /* Automatically uses red color scheme */
542
+ }
543
+ ```
544
+ #### Dark Theme
545
+ ```
546
+ .video-wrapper.player-theme-dark {
547
+ /* Enhanced dark mode with improved contrast */
548
+ }
549
+ ```
550
+ ### Control Size Variants
551
+
552
+ #### Large Controls
553
+ ```
554
+ .video-wrapper.player-large-controls {
555
+ /* Bigger buttons and controls for better accessibility */
556
+ }
557
+ ```
558
+ #### Compact Controls
559
+ ```
560
+ .video-wrapper.player-compact-controls {
561
+ /* Smaller, space-efficient controls */
562
+ }
563
+ ```
564
+ ### Custom Theme Examples
565
+
566
+ #### Custom Purple Theme
567
+ ```
568
+ .video-wrapper.my-purple-theme {
569
+ --player-primary-color: #9c27b0;
570
+ --player-primary-hover: #7b1fa2;
571
+ --player-primary-dark: #6a1b9a;
572
+ --player-bg-primary: #1a0d1a;
573
+ --player-bg-controls: linear-gradient(180deg, transparent 0%, rgba(26, 13, 26, 0.9) 100%);
574
+ }
575
+ ```
576
+ #### High Contrast Theme
577
+ ```
578
+ .video-wrapper.high-contrast-theme {
579
+ --player-primary-color: #ffff00;
580
+ --player-primary-hover: #ffeb3b;
581
+ --player-text-color: #ffffff;
582
+ --player-bg-primary: #000000;
583
+ --player-bg-controls: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.95) 100%);
584
+ --player-border-radius: 0; /* Sharp corners for accessibility */
585
+ }
586
+ ```
587
+ #### Minimal Theme
588
+ ```
589
+ .video-wrapper.minimal-theme {
590
+ --player-bg-controls: rgba(0, 0, 0, 0.3);
591
+ --player-bg-title-overlay: rgba(0, 0, 0, 0.3);
592
+ --player-border-radius: 0;
593
+ --player-progress-height: 3px;
594
+ --player-volume-height: 2px;
595
+ --player-button-padding: 4px;
596
+ }
597
+ ```
598
+ ### Responsive Customization
599
+
600
+ The player automatically adapts to different screen sizes. You can customize the responsive behavior:
601
+ ```
602
+ /* Custom mobile adjustments /
603
+ @media (max-width: 768px) {
604
+ .video-wrapper {
605
+ --player-icon-size: 18px;
606
+ --player-progress-height: 8px; / Thicker for touch /
607
+ --player-button-padding: 12px; / Larger touch targets */
608
+ }
609
+ }
610
+
611
+ @media (max-width: 480px) {
612
+ .video-wrapper {
613
+ --player-controls-padding: 12px 8px 8px;
614
+ --player-border-radius: 0; /* Full width on small screens */
615
+ }
616
+ }
617
+ ```
618
+ ### Custom Subtitle Styling
619
+
620
+ Customize the appearance of subtitles:
621
+ ```
622
+ .video-player::cue {
623
+ background: rgba(0, 0, 0, 0.9);
624
+ color: #ffffff;
625
+ font-size: 18px;
626
+ font-family: 'Your Custom Font', sans-serif;
627
+ padding: 10px 15px;
628
+ border-radius: 8px;
629
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
630
+ }
631
+
632
+ /* Highlighted subtitle text */
633
+ .video-player::cue(.highlight) {
634
+ background: var(--player-primary-color);
635
+ color: black;
636
+ }
637
+ ```
638
+ ### Animation Customization
639
+
640
+ Control the player's animations:
641
+ ```
642
+ .video-wrapper {
643
+ /* Faster animations */
644
+ --player-transition-fast: 0.1s ease;
645
+ --player-transition-normal: 0.15s ease;
646
+ }
647
+
648
+ /* Disable animations (accessibility) */
649
+ .video-wrapper.no-animations {
650
+ --player-transition-fast: 0s;
651
+ --player-transition-normal: 0s;
652
+ }
653
+
654
+ .video-wrapper.no-animations * {
655
+ transition: none !important;
656
+ animation: none !important;
657
+ }
658
+ ```
659
+ ### Quality Selector Customization
660
+
661
+ The dual-quality indicator can be customized:
662
+ ```
663
+ .quality-btn {
664
+ min-height: 40px; /* More space for two lines */
665
+ }
666
+
667
+ .selected-quality {
668
+ font-size: 16px; /* Larger selected quality text */
669
+ font-weight: 600;
670
+ }
671
+
672
+ .current-quality {
673
+ font-size: 11px; /* Current playing quality */
674
+ opacity: 0.7;
675
+ }
676
+ ```
677
+ ### Usage Examples
678
+
679
+ #### Apply Theme via JavaScript
680
+ ```
681
+ // Apply theme when initializing
682
+ const player = new MYETVvideoplayer('video', {
683
+ // ... other options
684
+ });
685
+
686
+ // Add theme class
687
+ document.querySelector('.video-wrapper').classList.add('player-theme-blue');
688
+ ```
689
+ #### Apply Theme via HTML
690
+ ```<div class="video-wrapper player-theme-dark player-large-controls"> <video id="my-video"> <!-- video sources --> </video> </div> ```
691
+
692
+ #### Dynamic Theme Switching
693
+ ```
694
+ function switchTheme(themeName) {
695
+ const wrapper = document.querySelector('.video-wrapper');
696
+
697
+ // Remove existing theme classes
698
+ wrapper.className = wrapper.className.replace(/player-theme-\w+/g, '');
699
+
700
+ // Add new theme
701
+ if (themeName !== 'default') {
702
+ wrapper.classList.add(`player-theme-${themeName}`);
703
+ }
704
+ }
705
+
706
+ // Usage
707
+ switchTheme('blue'); // Switch to blue theme
708
+ switchTheme('dark'); // Switch to dark theme
709
+ switchTheme('default'); // Switch to default theme
710
+ ```
711
+ ## Browser Compatibility
712
+ The CSS uses modern features with fallbacks:
713
+
714
+ CSS Custom Properties: Supported in all modern browsers
715
+
716
+ CSS Grid/Flexbox: Full support in Chrome 60+, Firefox 55+, Safari 11+
717
+
718
+ Backdrop Filter: Enhanced blur effects where supported
719
+
720
+ CSS Variables: Graceful fallback to default values
721
+
722
+ Performance Tips
723
+ Use transform and opacity for animations (GPU accelerated)
724
+
725
+ CSS variables are cached by the browser for better performance
726
+
727
+ Minimal DOM manipulation thanks to CSS-based theming
728
+
729
+ Hardware-accelerated transitions for smooth playback
730
+
731
+ ## Plguins feature
732
+ The player supports custom plugins to extend its functionality. Every plugins must have its own documentation to clearly known how to use it. Plugins are modular so you can add or remove any plugins whenever you want. This is just an example based on two plugins.
733
+ ### Add a plguin to the player
734
+ ```
735
+ <!-- Google Analytics 4 -->
736
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
737
+ <script>
738
+ window.dataLayer = window.dataLayer || [];
739
+ function gtag(){dataLayer.push(arguments);}
740
+ gtag('js', new Date());
741
+ gtag('config', 'G-XXXXXXXXXX');
742
+ </script>
743
+ <!-- Player bundle -->
744
+ <script src="dist/myetv-player.min.js"></script>
745
+ <!-- Plugin bundle -->
746
+ <script src="plugins/youtube/myetv-player-youtube-plugin.js"></script>
747
+ <script src="plugins/google-analytics/myetv-player-g-analytics-plugin.js"></script>
748
+ ```
749
+ ### Initialization exmples with plugins
750
+ ```
751
+ const player = new MYETVPlayer('my-video', {
752
+ debug: true,
753
+ plugins: {
754
+ youtube: {
755
+ videoId: 'dQw4w9WgXcQ', // Video ID of YouTube (example)
756
+ apiKey: 'your-api-key',
757
+ autoplay: false
758
+ },
759
+ analytics: {
760
+ platform: 'ga4',
761
+ videoTitle: 'My Awesome Video',
762
+ videoCategory: 'Tutorial',
763
+ videoId: 'video-001'
764
+ }
765
+ }
766
+ });
767
+ ```
768
+
769
+ ## Chapters feature
770
+ Supports flexible time formats (HH:MM:SS, MM:SS, or seconds) and images url (optional)
771
+ ### JSON format
772
+ ```
773
+ const player = new VideoPlayer('myVideo', {
774
+ chapters: [
775
+ {
776
+ time: 0,
777
+ title: "Introduction",
778
+ image: "https://example.com/intro.jpg"
779
+ },
780
+ {
781
+ time: 120,
782
+ title: "Main Content",
783
+ image: "https://example.com/main.jpg",
784
+ color: "#FF5722" // Custom color (optional)
785
+ },
786
+ {
787
+ time: '8:30',
788
+ title: "Conclusion"
789
+ // No image (optional)
790
+ }
791
+ ]
792
+ });
793
+ ```
794
+ ### String format
795
+ ```
796
+ const player = new VideoPlayer('myVideo', {
797
+ chapters: "0:00:00|Introduction|intro.jpg,0:02:00|Main Content|main.jpg,0:05:00|Conclusion"
798
+ });
799
+ ```
800
+ ## Playlist feature
801
+ ### Playlist Detection System
802
+ The playlist detection will work through HTML attributes on your video elements:
803
+ ```
804
+ <!-- Example playlist setup -->
805
+ <video id="myVideo" class="video-player"
806
+ data-playlist-id="my-series"
807
+ data-playlist-index="0">
808
+ <source src="video1-720p.mp4" type="video/mp4" data-quality="720p">
809
+ <source src="video1-480p.mp4" type="video/mp4" data-quality="480p">
810
+ </video>
811
+
812
+ <!-- Next video in playlist -->
813
+ <video id="video2" class="video-player"
814
+ data-playlist-id="my-series"
815
+ data-playlist-index="1">
816
+ <source src="video2-720p.mp4" type="video/mp4" data-quality="720p">
817
+ <source src="video2-480p.mp4" type="video/mp4" data-quality="480p">
818
+ </video>
819
+ ```
820
+ ## Adaptive streaming (HLS/DASH)
821
+ ### Adaptive Streaming APIs
822
+ ```
823
+ // Info adaptive streaming
824
+ player.getAdaptiveStreamingInfo();
825
+
826
+ // Change quality of adaptive streaming
827
+ player.setAdaptiveQuality(1); // Specify quality
828
+ player.setAdaptiveQuality('auto'); // Auto-switching
829
+ ```
830
+ ### Example Playlist+Adaptive Streaming
831
+ ```
832
+ <!-- Video 1: DASH -->
833
+ <video data-playlist-id="series" data-playlist-index="0" src="ep1.mpd">
834
+
835
+ <!-- Video 2: HLS -->
836
+ <video data-playlist-id="series" data-playlist-index="1" src="ep2.m3u8">
837
+
838
+ <!-- Video 3: Traditional -->
839
+ <video data-playlist-id="series" data-playlist-index="2">
840
+ <source src="ep3-1080p.mp4" data-quality="1080p">
841
+ <source src="ep3-720p.mp4" data-quality="720p">
842
+ </video>
843
+ ```
844
+ ## Supported Browsers
845
+ Chrome 60+
846
+ Firefox 55+
847
+ Safari 11+
848
+ Edge 79+
849
+ Mobile browsers with HTML5 support
850
+
851
+ ## License
852
+ This project is released under the MIT License.
853
+
854
+ ## Contributing
855
+ Fork the repository
856
+
857
+ Create a feature branch (git checkout -b feature/feature-name)
858
+
859
+ Commit your changes (git commit -am 'Add feature')
860
+
861
+ Push to the branch (git push origin feature/feature-name)
862
+
863
+ Create a Pull Request
864
+
865
+ ## Bug Reports
866
+ To report bugs or request features, open an issue in the repository.