vidply 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,21 +2,44 @@
2
2
 
3
3
  **Universal, Accessible Video & Audio Player**
4
4
 
5
- A modern, feature-rich video player built with vanilla ES6 JavaScript. Combines the best accessibility features from AblePlayer with the streaming capabilities of MediaElement.js.
5
+ A modern, feature-rich media player built with vanilla ES6 JavaScript. Combines the best accessibility features from AblePlayer with the streaming capabilities of MediaElement.js. Fully internationalized with support for 5 languages and complete WCAG 2.1 AA compliance.
6
6
 
7
7
  ![License](https://img.shields.io/badge/license-GPL--2.0--or--later-blue.svg)
8
8
  ![ES6](https://img.shields.io/badge/ES6-Module-yellow.svg)
9
9
  ![WCAG](https://img.shields.io/badge/WCAG-2.1%20AA-green.svg)
10
+ ![Version](https://img.shields.io/badge/version-1.0.4-brightgreen.svg)
11
+
12
+ ## Live Demos
13
+
14
+ Try VidPly in action:
15
+ - **[Main Demo](https://matthiaspeltzer.github.io/vidply/demo/demo.html)** - Full-featured video player showcase
16
+ - **[Audio Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-audio.html)** - Audio player with playlist support
17
+ - **[Video Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-video.html)** - Video playlist with thumbnails
18
+ - **[HLS Streaming](https://matthiaspeltzer.github.io/vidply/demo/hls-test.html)** - Adaptive bitrate streaming demo
19
+ - **[Sign Language](https://matthiaspeltzer.github.io/vidply/demo/sign-language-demo.html)** - Sign language overlay demo
20
+
21
+ ## Why VidPly?
22
+
23
+ - **Zero Dependencies** - Pure vanilla JavaScript, no frameworks required
24
+ - **Accessibility First** - WCAG 2.1 AA compliant with full keyboard and screen reader support
25
+ - **Multilingual** - Built-in translations for 5 languages with easy extensibility
26
+ - **Fully Customizable** - CSS variables and comprehensive API
27
+ - **Modern Build** - ES6 modules with tree-shaking support
28
+ - **Production Ready** - Thoroughly tested with real-world media content
10
29
 
11
30
  ## Features
12
31
 
13
32
  ### Core Media Support
14
- - **Audio & Video Playback** - Native HTML5 support
15
- - **Multiple Formats** - MP3, OGG, WAV, MP4, WebM
16
- - **YouTube Integration** - Embed with unified controls
17
- - **Vimeo Integration** - Seamless Vimeo support
18
- - **HLS Streaming** - Adaptive bitrate streaming
19
- - **Playlists** - Audio/video playlist support
33
+ - **Audio & Video Playback** - Native HTML5 support for both media types
34
+ - **Multiple Formats** - MP3, OGG, WAV (audio) / MP4, WebM (video)
35
+ - **YouTube Integration** - Embed YouTube videos with unified controls
36
+ - **Vimeo Integration** - Seamless Vimeo player integration
37
+ - **HLS Streaming** - Adaptive bitrate streaming with quality selection
38
+ - **Playlists** - Full playlist support with auto-advance and navigation
39
+ - Audio playlists with track info
40
+ - Video playlists with thumbnails
41
+ - Previous/Next controls
42
+ - Visual playlist panel
20
43
 
21
44
  ### Accessibility Features
22
45
  - **Full Keyboard Navigation** - WCAG 2.1 compliant
@@ -46,13 +69,21 @@ A modern, feature-rich video player built with vanilla ES6 JavaScript. Combines
46
69
  - **Picture-in-Picture** - PiP support
47
70
 
48
71
  ### Internationalization
49
- Built-in support for:
50
- - English
51
- - Spanish
52
- - French
53
- - German
54
- - Japanese
55
- - Easy to add more languages
72
+ Built-in support for 5 languages:
73
+ - 🇬🇧 English
74
+ - 🇪🇸 Spanish (Español)
75
+ - 🇫🇷 French (Français)
76
+ - 🇩🇪 German (Deutsch)
77
+ - 🇯🇵 Japanese (日本語)
78
+
79
+ All UI elements are fully translated, including:
80
+ - Control buttons and menus
81
+ - Time display and duration formatting
82
+ - Keyboard shortcuts
83
+ - Error messages and notifications
84
+ - ARIA labels for screen readers
85
+
86
+ Easy to add more languages via the i18n system
56
87
 
57
88
  ## Installation
58
89
 
@@ -363,6 +394,52 @@ player.disableSignLanguage() // Hide sign language overlay
363
394
  player.toggleSignLanguage() // Toggle sign language
364
395
  ```
365
396
 
397
+ ### Playlists
398
+
399
+ ```javascript
400
+ import { Player, PlaylistManager } from './dist/vidply.esm.js';
401
+
402
+ // Create player
403
+ const player = new Player('#my-player');
404
+
405
+ // Create playlist manager
406
+ const playlist = new PlaylistManager(player, {
407
+ autoAdvance: true, // Auto-play next track
408
+ loop: false, // Loop back to start
409
+ showPanel: true // Show playlist UI
410
+ });
411
+
412
+ // Load tracks
413
+ playlist.loadPlaylist([
414
+ {
415
+ src: 'track1.mp3',
416
+ title: 'Track 1',
417
+ artist: 'Artist Name',
418
+ poster: 'thumb1.jpg'
419
+ },
420
+ {
421
+ src: 'track2.mp3',
422
+ title: 'Track 2',
423
+ artist: 'Artist Name',
424
+ tracks: [
425
+ { src: 'captions.vtt', kind: 'captions', srclang: 'en' }
426
+ ]
427
+ }
428
+ ]);
429
+
430
+ // Control playlist
431
+ playlist.next() // Go to next track
432
+ playlist.previous() // Go to previous track
433
+ playlist.goToTrack(2) // Jump to specific track
434
+ playlist.hasNext() // Check if next track exists
435
+ playlist.hasPrevious() // Check if previous track exists
436
+
437
+ // Listen for track changes
438
+ player.on('playlisttrackchange', (e) => {
439
+ console.log('Now playing:', e.item.title);
440
+ });
441
+ ```
442
+
366
443
  ### Settings
367
444
 
368
445
  ```javascript
@@ -482,7 +559,7 @@ See [BUILD.md](docs/BUILD.md) for detailed build documentation.
482
559
 
483
560
  GNU General Public License v2.0 or later
484
561
 
485
- Copyright (C) 2024 Matthias Peltzer
562
+ Copyright (C) 2025 Matthias Peltzer
486
563
 
487
564
  This program is free software; you can redistribute it and/or modify
488
565
  it under the terms of the GNU General Public License as published by
@@ -495,16 +572,15 @@ See [LICENSE](LICENSE) for full license text.
495
572
 
496
573
  Contributions are welcome! Please feel free to submit a Pull Request.
497
574
 
498
- ## Support
575
+ ## Documentation
499
576
 
500
- - Getting Started: See [GETTING_STARTED.md](docs/GETTING_STARTED.md)
501
- - Usage Examples: See [USAGE.md](docs/USAGE.md)
502
- - Playlist Guide: See [PLAYLIST.md](docs/PLAYLIST.md)
503
- - Transcript Guide: See [TRANSCRIPT.md](docs/TRANSCRIPT.md)
504
- - Build Guide: See [BUILD.md](docs/BUILD.md)
505
- - Changelog: See [CHANGELOG.md](docs/CHANGELOG.md)
506
- - Issues: Report on GitHub
507
- - Discussions: GitHub Discussions
577
+ - [Getting Started Guide](docs/GETTING_STARTED.md) - Basic setup and usage
578
+ - [Usage Guide](docs/USAGE.md) - Detailed usage examples
579
+ - [Playlist Guide](docs/PLAYLIST.md) - Audio/video playlists
580
+ - [Transcript Guide](docs/TRANSCRIPT.md) - Interactive transcripts
581
+ - [Keyboard Shortcuts](docs/KEYBOARD.md) - Complete keyboard reference
582
+ - [Build Guide](docs/BUILD.md) - Build system and development
583
+ - [Changelog](docs/CHANGELOG.md) - Version history and updates
508
584
 
509
585
  ## Credits
510
586
 
@@ -402,184 +402,6 @@ var DOMUtils = {
402
402
  }
403
403
  };
404
404
 
405
- // src/utils/TimeUtils.js
406
- var TimeUtils = {
407
- /**
408
- * Format seconds to time string (HH:MM:SS or MM:SS)
409
- */
410
- formatTime(seconds, alwaysShowHours = false) {
411
- if (!isFinite(seconds) || seconds < 0) {
412
- return alwaysShowHours ? "00:00:00" : "00:00";
413
- }
414
- const hours = Math.floor(seconds / 3600);
415
- const minutes = Math.floor(seconds % 3600 / 60);
416
- const secs = Math.floor(seconds % 60);
417
- const pad = (num) => String(num).padStart(2, "0");
418
- if (hours > 0 || alwaysShowHours) {
419
- return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
420
- }
421
- return `${pad(minutes)}:${pad(secs)}`;
422
- },
423
- /**
424
- * Parse time string to seconds
425
- */
426
- parseTime(timeString) {
427
- const parts = timeString.split(":").map((p) => parseInt(p, 10));
428
- if (parts.length === 3) {
429
- return parts[0] * 3600 + parts[1] * 60 + parts[2];
430
- } else if (parts.length === 2) {
431
- return parts[0] * 60 + parts[1];
432
- } else if (parts.length === 1) {
433
- return parts[0];
434
- }
435
- return 0;
436
- },
437
- /**
438
- * Format seconds to readable duration
439
- */
440
- formatDuration(seconds) {
441
- if (!isFinite(seconds) || seconds < 0) {
442
- return "0 seconds";
443
- }
444
- const hours = Math.floor(seconds / 3600);
445
- const minutes = Math.floor(seconds % 3600 / 60);
446
- const secs = Math.floor(seconds % 60);
447
- const parts = [];
448
- if (hours > 0) {
449
- parts.push(`${hours} hour${hours !== 1 ? "s" : ""}`);
450
- }
451
- if (minutes > 0) {
452
- parts.push(`${minutes} minute${minutes !== 1 ? "s" : ""}`);
453
- }
454
- if (secs > 0 || parts.length === 0) {
455
- parts.push(`${secs} second${secs !== 1 ? "s" : ""}`);
456
- }
457
- return parts.join(", ");
458
- },
459
- /**
460
- * Format percentage
461
- */
462
- formatPercentage(value, total) {
463
- if (total === 0) return 0;
464
- return Math.round(value / total * 100);
465
- }
466
- };
467
-
468
- // src/icons/Icons.js
469
- var iconPaths = {
470
- play: `<path d="M8 5v14l11-7z"/>`,
471
- pause: `<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>`,
472
- stop: `<rect x="6" y="6" width="12" height="12"/>`,
473
- rewind: `<path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/>`,
474
- forward: `<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/>`,
475
- skipPrevious: `<path d="M6 6h2v12H6V6zm3 6l8.5 6V6L9 12z"/>`,
476
- skipNext: `<path d="M16 6h2v12h-2V6zM6 6l8.5 6L6 18V6z"/>`,
477
- restart: `<path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>`,
478
- volumeHigh: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>`,
479
- volumeMedium: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
480
- volumeLow: `<path d="M7 9v6h4l5 5V4l-5 5H7z"/>`,
481
- volumeMuted: `<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>`,
482
- fullscreen: `<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>`,
483
- fullscreenExit: `<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>`,
484
- settings: `<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94L14.4 2.81c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>`,
485
- captions: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/>`,
486
- captionsOff: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/><path d="M0 0h24v24H0z" fill="none"/>`,
487
- pip: `<path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/>`,
488
- speed: `<path d="M20.38 8.57l-1.23 1.85a8 8 0 0 1-.22 7.58H5.07A8 8 0 0 1 15.58 6.85l1.85-1.23A10 10 0 0 0 3.35 19a2 2 0 0 0 1.72 1h13.85a2 2 0 0 0 1.74-1 10 10 0 0 0-.27-10.44z"/><path d="M10.59 15.41a2 2 0 0 0 2.83 0l5.66-8.49-8.49 5.66a2 2 0 0 0 0 2.83z"/>`,
489
- close: `<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>`,
490
- check: `<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>`,
491
- arrowUp: `<path d="M7 14l5-5 5 5z"/>`,
492
- arrowDown: `<path d="M7 10l5 5 5-5z"/>`,
493
- arrowLeft: `<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>`,
494
- arrowRight: `<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>`,
495
- loading: `<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/>`,
496
- error: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>`,
497
- download: `<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>`,
498
- link: `<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>`,
499
- playlist: `<path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"/>`,
500
- language: `<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>`,
501
- hd: `<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-8 12H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/>`,
502
- transcript: `<path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>`,
503
- audioDescription: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
504
- audioDescriptionOn: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/><circle cx="19" cy="16" r="3" fill="#3b82f6"/><path d="M18.5 17.5l1-1 1.5 1.5" stroke="white" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
505
- signLanguage: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
506
- signLanguageOn: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
507
- speaker: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
508
- music: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7zm-1.5 16c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
509
- moreVertical: `<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`,
510
- moreHorizontal: `<path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`
511
- };
512
- var svgWrapper = (paths) => `<svg viewBox="0 0 24 24" fill="currentColor">${paths}</svg>`;
513
- var Icons = Object.fromEntries(
514
- Object.entries(iconPaths).map(([key, value]) => [key, svgWrapper(value)])
515
- );
516
- function getIcon(name) {
517
- return Icons[name] || Icons.play;
518
- }
519
- function createIconElement(name, className = "") {
520
- const wrapper = document.createElement("span");
521
- wrapper.className = `vidply-icon ${className}`.trim();
522
- wrapper.innerHTML = getIcon(name);
523
- wrapper.setAttribute("aria-hidden", "true");
524
- return wrapper;
525
- }
526
- function createPlayOverlay() {
527
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
528
- svg.setAttribute("class", "vidply-play-overlay");
529
- svg.setAttribute("viewBox", "0 0 80 80");
530
- svg.setAttribute("width", "80");
531
- svg.setAttribute("height", "80");
532
- svg.setAttribute("aria-hidden", "true");
533
- svg.setAttribute("role", "presentation");
534
- svg.style.cursor = "pointer";
535
- const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
536
- const filterId = `vidply-play-shadow-${Math.random().toString(36).substr(2, 9)}`;
537
- const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
538
- filter.setAttribute("id", filterId);
539
- filter.setAttribute("x", "-50%");
540
- filter.setAttribute("y", "-50%");
541
- filter.setAttribute("width", "200%");
542
- filter.setAttribute("height", "200%");
543
- const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
544
- feGaussianBlur.setAttribute("in", "SourceAlpha");
545
- feGaussianBlur.setAttribute("stdDeviation", "3");
546
- const feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
547
- feOffset.setAttribute("dx", "0");
548
- feOffset.setAttribute("dy", "2");
549
- feOffset.setAttribute("result", "offsetblur");
550
- const feComponentTransfer = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
551
- const feFuncA = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
552
- feFuncA.setAttribute("type", "linear");
553
- feFuncA.setAttribute("slope", "0.3");
554
- feComponentTransfer.appendChild(feFuncA);
555
- const feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
556
- const feMergeNode1 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
557
- const feMergeNode2 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
558
- feMergeNode2.setAttribute("in", "SourceGraphic");
559
- feMerge.appendChild(feMergeNode1);
560
- feMerge.appendChild(feMergeNode2);
561
- filter.appendChild(feGaussianBlur);
562
- filter.appendChild(feOffset);
563
- filter.appendChild(feComponentTransfer);
564
- filter.appendChild(feMerge);
565
- defs.appendChild(filter);
566
- svg.appendChild(defs);
567
- const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
568
- circle.setAttribute("cx", "40");
569
- circle.setAttribute("cy", "40");
570
- circle.setAttribute("r", "40");
571
- circle.setAttribute("fill", "rgba(255, 255, 255, 0.95)");
572
- circle.setAttribute("filter", `url(#${filterId})`);
573
- circle.setAttribute("class", "vidply-play-overlay-bg");
574
- svg.appendChild(circle);
575
- const playTriangle = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
576
- playTriangle.setAttribute("points", "32,28 32,52 54,40");
577
- playTriangle.setAttribute("fill", "#0a406e");
578
- playTriangle.setAttribute("class", "vidply-play-overlay-icon");
579
- svg.appendChild(playTriangle);
580
- return svg;
581
- }
582
-
583
405
  // src/i18n/translations.js
584
406
  var translations = {
585
407
  en: {
@@ -680,6 +502,16 @@ var translations = {
680
502
  },
681
503
  speeds: {
682
504
  normal: "Normal"
505
+ },
506
+ time: {
507
+ display: "Time display",
508
+ durationPrefix: "Duration: ",
509
+ hour: "{count} hour",
510
+ hours: "{count} hours",
511
+ minute: "{count} minute",
512
+ minutes: "{count} minutes",
513
+ second: "{count} second",
514
+ seconds: "{count} seconds"
683
515
  }
684
516
  },
685
517
  de: {
@@ -780,6 +612,16 @@ var translations = {
780
612
  },
781
613
  speeds: {
782
614
  normal: "Normal"
615
+ },
616
+ time: {
617
+ display: "Zeitanzeige",
618
+ durationPrefix: "Dauer: ",
619
+ hour: "{count} Stunde",
620
+ hours: "{count} Stunden",
621
+ minute: "{count} Minute",
622
+ minutes: "{count} Minuten",
623
+ second: "{count} Sekunde",
624
+ seconds: "{count} Sekunden"
783
625
  }
784
626
  },
785
627
  es: {
@@ -880,6 +722,16 @@ var translations = {
880
722
  },
881
723
  speeds: {
882
724
  normal: "Normal"
725
+ },
726
+ time: {
727
+ display: "Visualizaci\xF3n de tiempo",
728
+ durationPrefix: "Duraci\xF3n: ",
729
+ hour: "{count} hora",
730
+ hours: "{count} horas",
731
+ minute: "{count} minuto",
732
+ minutes: "{count} minutos",
733
+ second: "{count} segundo",
734
+ seconds: "{count} segundos"
883
735
  }
884
736
  },
885
737
  fr: {
@@ -980,6 +832,16 @@ var translations = {
980
832
  },
981
833
  speeds: {
982
834
  normal: "Normal"
835
+ },
836
+ time: {
837
+ display: "Affichage du temps",
838
+ durationPrefix: "Dur\xE9e : ",
839
+ hour: "{count} heure",
840
+ hours: "{count} heures",
841
+ minute: "{count} minute",
842
+ minutes: "{count} minutes",
843
+ second: "{count} seconde",
844
+ seconds: "{count} secondes"
983
845
  }
984
846
  },
985
847
  ja: {
@@ -1080,6 +942,16 @@ var translations = {
1080
942
  },
1081
943
  speeds: {
1082
944
  normal: "\u901A\u5E38"
945
+ },
946
+ time: {
947
+ display: "\u6642\u9593\u8868\u793A",
948
+ durationPrefix: "\u518D\u751F\u6642\u9593: ",
949
+ hour: "{count}\u6642\u9593",
950
+ hours: "{count}\u6642\u9593",
951
+ minute: "{count}\u5206",
952
+ minutes: "{count}\u5206",
953
+ second: "{count}\u79D2",
954
+ seconds: "{count}\u79D2"
1083
955
  }
1084
956
  }
1085
957
  };
@@ -1135,6 +1007,187 @@ var I18n = class {
1135
1007
  };
1136
1008
  var i18n = new I18n();
1137
1009
 
1010
+ // src/utils/TimeUtils.js
1011
+ var TimeUtils = {
1012
+ /**
1013
+ * Format seconds to time string (HH:MM:SS or MM:SS)
1014
+ */
1015
+ formatTime(seconds, alwaysShowHours = false) {
1016
+ if (!isFinite(seconds) || seconds < 0) {
1017
+ return alwaysShowHours ? "00:00:00" : "00:00";
1018
+ }
1019
+ const hours = Math.floor(seconds / 3600);
1020
+ const minutes = Math.floor(seconds % 3600 / 60);
1021
+ const secs = Math.floor(seconds % 60);
1022
+ const pad = (num) => String(num).padStart(2, "0");
1023
+ if (hours > 0 || alwaysShowHours) {
1024
+ return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
1025
+ }
1026
+ return `${pad(minutes)}:${pad(secs)}`;
1027
+ },
1028
+ /**
1029
+ * Parse time string to seconds
1030
+ */
1031
+ parseTime(timeString) {
1032
+ const parts = timeString.split(":").map((p) => parseInt(p, 10));
1033
+ if (parts.length === 3) {
1034
+ return parts[0] * 3600 + parts[1] * 60 + parts[2];
1035
+ } else if (parts.length === 2) {
1036
+ return parts[0] * 60 + parts[1];
1037
+ } else if (parts.length === 1) {
1038
+ return parts[0];
1039
+ }
1040
+ return 0;
1041
+ },
1042
+ /**
1043
+ * Format seconds to readable duration
1044
+ */
1045
+ formatDuration(seconds) {
1046
+ if (!isFinite(seconds) || seconds < 0) {
1047
+ return i18n.t("time.seconds", { count: 0 });
1048
+ }
1049
+ const hours = Math.floor(seconds / 3600);
1050
+ const minutes = Math.floor(seconds % 3600 / 60);
1051
+ const secs = Math.floor(seconds % 60);
1052
+ const parts = [];
1053
+ if (hours > 0) {
1054
+ const key = hours === 1 ? "time.hour" : "time.hours";
1055
+ parts.push(i18n.t(key, { count: hours }));
1056
+ }
1057
+ if (minutes > 0) {
1058
+ const key = minutes === 1 ? "time.minute" : "time.minutes";
1059
+ parts.push(i18n.t(key, { count: minutes }));
1060
+ }
1061
+ if (secs > 0 || parts.length === 0) {
1062
+ const key = secs === 1 ? "time.second" : "time.seconds";
1063
+ parts.push(i18n.t(key, { count: secs }));
1064
+ }
1065
+ return parts.join(", ");
1066
+ },
1067
+ /**
1068
+ * Format percentage
1069
+ */
1070
+ formatPercentage(value, total) {
1071
+ if (total === 0) return 0;
1072
+ return Math.round(value / total * 100);
1073
+ }
1074
+ };
1075
+
1076
+ // src/icons/Icons.js
1077
+ var iconPaths = {
1078
+ play: `<path d="M8 5v14l11-7z"/>`,
1079
+ pause: `<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>`,
1080
+ stop: `<rect x="6" y="6" width="12" height="12"/>`,
1081
+ rewind: `<path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/>`,
1082
+ forward: `<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/>`,
1083
+ skipPrevious: `<path d="M6 6h2v12H6V6zm3 6l8.5 6V6L9 12z"/>`,
1084
+ skipNext: `<path d="M16 6h2v12h-2V6zM6 6l8.5 6L6 18V6z"/>`,
1085
+ restart: `<path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>`,
1086
+ volumeHigh: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>`,
1087
+ volumeMedium: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
1088
+ volumeLow: `<path d="M7 9v6h4l5 5V4l-5 5H7z"/>`,
1089
+ volumeMuted: `<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>`,
1090
+ fullscreen: `<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>`,
1091
+ fullscreenExit: `<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>`,
1092
+ settings: `<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94L14.4 2.81c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>`,
1093
+ captions: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/>`,
1094
+ captionsOff: `<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/><path d="M0 0h24v24H0z" fill="none"/>`,
1095
+ pip: `<path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/>`,
1096
+ speed: `<path d="M20.38 8.57l-1.23 1.85a8 8 0 0 1-.22 7.58H5.07A8 8 0 0 1 15.58 6.85l1.85-1.23A10 10 0 0 0 3.35 19a2 2 0 0 0 1.72 1h13.85a2 2 0 0 0 1.74-1 10 10 0 0 0-.27-10.44z"/><path d="M10.59 15.41a2 2 0 0 0 2.83 0l5.66-8.49-8.49 5.66a2 2 0 0 0 0 2.83z"/>`,
1097
+ close: `<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>`,
1098
+ check: `<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>`,
1099
+ arrowUp: `<path d="M7 14l5-5 5 5z"/>`,
1100
+ arrowDown: `<path d="M7 10l5 5 5-5z"/>`,
1101
+ arrowLeft: `<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>`,
1102
+ arrowRight: `<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>`,
1103
+ loading: `<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/>`,
1104
+ error: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>`,
1105
+ download: `<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>`,
1106
+ link: `<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>`,
1107
+ playlist: `<path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"/>`,
1108
+ language: `<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>`,
1109
+ hd: `<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-8 12H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/>`,
1110
+ transcript: `<path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>`,
1111
+ audioDescription: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
1112
+ audioDescriptionOn: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/><path d="M10.5 19c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/><circle cx="19" cy="16" r="3" fill="#3b82f6"/><path d="M18.5 17.5l1-1 1.5 1.5" stroke="white" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
1113
+ signLanguage: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
1114
+ signLanguageOn: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
1115
+ speaker: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
1116
+ music: `<path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7zm-1.5 16c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>`,
1117
+ moreVertical: `<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`,
1118
+ moreHorizontal: `<path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`
1119
+ };
1120
+ var svgWrapper = (paths) => `<svg viewBox="0 0 24 24" fill="currentColor">${paths}</svg>`;
1121
+ var Icons = Object.fromEntries(
1122
+ Object.entries(iconPaths).map(([key, value]) => [key, svgWrapper(value)])
1123
+ );
1124
+ function getIcon(name) {
1125
+ return Icons[name] || Icons.play;
1126
+ }
1127
+ function createIconElement(name, className = "") {
1128
+ const wrapper = document.createElement("span");
1129
+ wrapper.className = `vidply-icon ${className}`.trim();
1130
+ wrapper.innerHTML = getIcon(name);
1131
+ wrapper.setAttribute("aria-hidden", "true");
1132
+ return wrapper;
1133
+ }
1134
+ function createPlayOverlay() {
1135
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1136
+ svg.setAttribute("class", "vidply-play-overlay");
1137
+ svg.setAttribute("viewBox", "0 0 80 80");
1138
+ svg.setAttribute("width", "80");
1139
+ svg.setAttribute("height", "80");
1140
+ svg.setAttribute("aria-hidden", "true");
1141
+ svg.setAttribute("role", "presentation");
1142
+ svg.style.cursor = "pointer";
1143
+ const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
1144
+ const filterId = `vidply-play-shadow-${Math.random().toString(36).substr(2, 9)}`;
1145
+ const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
1146
+ filter.setAttribute("id", filterId);
1147
+ filter.setAttribute("x", "-50%");
1148
+ filter.setAttribute("y", "-50%");
1149
+ filter.setAttribute("width", "200%");
1150
+ filter.setAttribute("height", "200%");
1151
+ const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
1152
+ feGaussianBlur.setAttribute("in", "SourceAlpha");
1153
+ feGaussianBlur.setAttribute("stdDeviation", "3");
1154
+ const feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
1155
+ feOffset.setAttribute("dx", "0");
1156
+ feOffset.setAttribute("dy", "2");
1157
+ feOffset.setAttribute("result", "offsetblur");
1158
+ const feComponentTransfer = document.createElementNS("http://www.w3.org/2000/svg", "feComponentTransfer");
1159
+ const feFuncA = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA");
1160
+ feFuncA.setAttribute("type", "linear");
1161
+ feFuncA.setAttribute("slope", "0.3");
1162
+ feComponentTransfer.appendChild(feFuncA);
1163
+ const feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
1164
+ const feMergeNode1 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
1165
+ const feMergeNode2 = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
1166
+ feMergeNode2.setAttribute("in", "SourceGraphic");
1167
+ feMerge.appendChild(feMergeNode1);
1168
+ feMerge.appendChild(feMergeNode2);
1169
+ filter.appendChild(feGaussianBlur);
1170
+ filter.appendChild(feOffset);
1171
+ filter.appendChild(feComponentTransfer);
1172
+ filter.appendChild(feMerge);
1173
+ defs.appendChild(filter);
1174
+ svg.appendChild(defs);
1175
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1176
+ circle.setAttribute("cx", "40");
1177
+ circle.setAttribute("cy", "40");
1178
+ circle.setAttribute("r", "40");
1179
+ circle.setAttribute("fill", "rgba(255, 255, 255, 0.95)");
1180
+ circle.setAttribute("filter", `url(#${filterId})`);
1181
+ circle.setAttribute("class", "vidply-play-overlay-bg");
1182
+ svg.appendChild(circle);
1183
+ const playTriangle = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
1184
+ playTriangle.setAttribute("points", "32,28 32,52 54,40");
1185
+ playTriangle.setAttribute("fill", "#0a406e");
1186
+ playTriangle.setAttribute("class", "vidply-play-overlay-icon");
1187
+ svg.appendChild(playTriangle);
1188
+ return svg;
1189
+ }
1190
+
1138
1191
  // src/controls/ControlBar.js
1139
1192
  var ControlBar = class {
1140
1193
  constructor(player) {
@@ -1654,13 +1707,13 @@ var ControlBar = class {
1654
1707
  className: `${this.player.options.classPrefix}-time`,
1655
1708
  attributes: {
1656
1709
  "role": "group",
1657
- "aria-label": "Time display"
1710
+ "aria-label": i18n.t("time.display")
1658
1711
  }
1659
1712
  });
1660
1713
  this.controls.currentTimeDisplay = DOMUtils.createElement("span", {
1661
1714
  className: `${this.player.options.classPrefix}-current-time`,
1662
1715
  attributes: {
1663
- "aria-label": "0 seconds"
1716
+ "aria-label": i18n.t("time.seconds", { count: 0 })
1664
1717
  }
1665
1718
  });
1666
1719
  const currentTimeVisual = DOMUtils.createElement("span", {
@@ -1680,7 +1733,7 @@ var ControlBar = class {
1680
1733
  this.controls.durationDisplay = DOMUtils.createElement("span", {
1681
1734
  className: `${this.player.options.classPrefix}-duration`,
1682
1735
  attributes: {
1683
- "aria-label": "Duration: 0 seconds"
1736
+ "aria-label": i18n.t("time.durationPrefix") + i18n.t("time.seconds", { count: 0 })
1684
1737
  }
1685
1738
  });
1686
1739
  const durationVisual = DOMUtils.createElement("span", {
@@ -2524,7 +2577,7 @@ var ControlBar = class {
2524
2577
  if (this.controls.durationVisual) {
2525
2578
  const duration = this.player.state.duration;
2526
2579
  this.controls.durationVisual.textContent = TimeUtils.formatTime(duration);
2527
- this.controls.durationDisplay.setAttribute("aria-label", "Duration: " + TimeUtils.formatDuration(duration));
2580
+ this.controls.durationDisplay.setAttribute("aria-label", i18n.t("time.durationPrefix") + TimeUtils.formatDuration(duration));
2528
2581
  }
2529
2582
  }
2530
2583
  updateVolumeDisplay() {