vidply 1.0.0 → 1.0.2

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
@@ -21,6 +21,9 @@ A modern, feature-rich video player built with vanilla ES6 JavaScript. Combines
21
21
  ### Accessibility Features
22
22
  - **Full Keyboard Navigation** - WCAG 2.1 compliant
23
23
  - **Screen Reader Support** - Complete ARIA labels
24
+ - **Interactive Transcripts** - Click-to-seek transcript window
25
+ - **Sign Language Overlay** - Picture-in-picture sign language video
26
+ - **Audio Description** - Alternate audio track with descriptions
24
27
  - **Customizable Shortcuts** - User-definable hotkeys
25
28
  - **High Contrast Mode** - Windows HCM support
26
29
  - **Focus Indicators** - Clear visual focus states
@@ -29,9 +32,10 @@ A modern, feature-rich video player built with vanilla ES6 JavaScript. Combines
29
32
  ### Captions & Subtitles
30
33
  - **WebVTT Support** - Standard caption format
31
34
  - **Multiple Languages** - Multi-track support
32
- - **Customizable Display** - Font, size, color, opacity
33
- - **Caption Positioning** - Flexible placement
34
- - **Auto-generated Transcripts** - Interactive text
35
+ - **Caption Selector** - Easy track switching with CC button
36
+ - **Caption Styling** - Dedicated styling menu (font, size, color, opacity)
37
+ - **Chapter Navigation** - Jump to video chapters
38
+ - **Interactive Transcripts** - Full-text searchable transcript panel
35
39
 
36
40
  ### Playback Features
37
41
  - **Adjustable Speed** - 0.25x to 2x playback
@@ -198,8 +202,14 @@ const player = new Player('#video', {
198
202
  playPauseButton: true,
199
203
  progressBar: true,
200
204
  volumeControl: true,
205
+ chaptersButton: true,
206
+ qualityButton: true,
207
+ captionStyleButton: true,
201
208
  speedButton: true,
202
209
  captionsButton: true,
210
+ transcriptButton: true,
211
+ audioDescriptionButton: true,
212
+ signLanguageButton: true,
203
213
  fullscreenButton: true,
204
214
  pipButton: true,
205
215
 
@@ -212,6 +222,23 @@ const player = new Player('#video', {
212
222
  captionsBackgroundColor: '#000000',
213
223
  captionsOpacity: 0.8,
214
224
 
225
+ // Audio Description
226
+ audioDescription: true,
227
+ audioDescriptionSrc: null, // URL to audio-described version
228
+ audioDescriptionButton: true,
229
+
230
+ // Sign Language
231
+ signLanguage: true,
232
+ signLanguageSrc: null, // URL to sign language video
233
+ signLanguageButton: true,
234
+ signLanguagePosition: 'bottom-right', // 'bottom-right', 'bottom-left', 'top-right', 'top-left'
235
+
236
+ // Transcripts
237
+ transcript: false,
238
+ transcriptButton: true,
239
+ transcriptPosition: 'external',
240
+ transcriptContainer: null,
241
+
215
242
  // Keyboard
216
243
  keyboard: true,
217
244
  keyboardShortcuts: {
@@ -254,9 +281,10 @@ const player = new Player('#video', {
254
281
  | <kbd>↑</kbd> / <kbd>↓</kbd> | Volume Up/Down |
255
282
  | <kbd>←</kbd> / <kbd>→</kbd> | Seek -10s / +10s |
256
283
  | <kbd>J</kbd> / <kbd>L</kbd> | Seek -30s / +30s |
257
- | <kbd>C</kbd> | Toggle Captions |
284
+ | <kbd>C</kbd> | Toggle Captions (or open menu if multiple) |
258
285
  | <kbd><</kbd> / <kbd>></kbd> | Decrease/Increase Speed |
259
286
  | <kbd>S</kbd> | Open Settings |
287
+ | <kbd>R</kbd> | Restart Video |
260
288
 
261
289
  ## API Reference
262
290
 
@@ -309,6 +337,30 @@ player.captionManager.switchTrack(0) // Switch to first track
309
337
  player.captionManager.getAvailableTracks() // Get all tracks
310
338
  ```
311
339
 
340
+ ### Transcript
341
+
342
+ ```javascript
343
+ player.transcriptManager.showTranscript() // Show transcript window
344
+ player.transcriptManager.hideTranscript() // Hide transcript window
345
+ player.transcriptManager.toggleTranscript() // Toggle transcript
346
+ ```
347
+
348
+ ### Audio Description
349
+
350
+ ```javascript
351
+ player.enableAudioDescription() // Switch to described version
352
+ player.disableAudioDescription() // Switch back to original
353
+ player.toggleAudioDescription() // Toggle audio description
354
+ ```
355
+
356
+ ### Sign Language
357
+
358
+ ```javascript
359
+ player.enableSignLanguage() // Show sign language overlay
360
+ player.disableSignLanguage() // Hide sign language overlay
361
+ player.toggleSignLanguage() // Toggle sign language
362
+ ```
363
+
312
364
  ### Settings
313
365
 
314
366
  ```javascript
@@ -446,7 +498,9 @@ Contributions are welcome! Please feel free to submit a Pull Request.
446
498
  - Getting Started: See [GETTING_STARTED.md](docs/GETTING_STARTED.md)
447
499
  - Usage Examples: See [USAGE.md](docs/USAGE.md)
448
500
  - Playlist Guide: See [PLAYLIST.md](docs/PLAYLIST.md)
501
+ - Transcript Guide: See [TRANSCRIPT.md](docs/TRANSCRIPT.md)
449
502
  - Build Guide: See [BUILD.md](docs/BUILD.md)
503
+ - Changelog: See [CHANGELOG.md](docs/CHANGELOG.md)
450
504
  - Issues: Report on GitHub
451
505
  - Discussions: GitHub Discussions
452
506
 
package/dist/vidply.css CHANGED
@@ -151,6 +151,10 @@
151
151
  height: auto;
152
152
  }
153
153
 
154
+ .vidply-player.vidply-audio .vidply-menu {
155
+ max-height: 150px;
156
+ }
157
+
154
158
  /* Video/Audio Element */
155
159
  .vidply-player video,
156
160
  .vidply-player audio {
@@ -191,7 +195,7 @@
191
195
  overflow: hidden;
192
196
  position: relative;
193
197
  width: 100%;
194
- z-index: 10; /* Above track-info and playlist siblings */
198
+ z-index: 1; /* Base video layer */
195
199
  }
196
200
 
197
201
  /* Mobile: Simplify video wrapper */
@@ -222,7 +226,7 @@
222
226
  top: 50%;
223
227
  transform: translate(-50%, -50%);
224
228
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
225
- z-index: 15;
229
+ z-index: 2;
226
230
  }
227
231
 
228
232
  .vidply-play-overlay:hover {
@@ -287,7 +291,7 @@
287
291
  position: relative;
288
292
  right: 0;
289
293
  width: 100%;
290
- z-index: 100;
294
+ z-index: 10;
291
295
  }
292
296
 
293
297
  /* Allow menus to position within controls */
@@ -524,7 +528,7 @@
524
528
  position: fixed;
525
529
  right: 0;
526
530
  top: 0;
527
- z-index: 999;
531
+ z-index: 19;
528
532
  }
529
533
 
530
534
  .vidply-menu-backdrop.visible {
@@ -568,8 +572,8 @@
568
572
  /* iOS momentum scrolling */
569
573
  transform: translateX(50%);
570
574
 
571
- /* Ensure touch events work on iOS */
572
- z-index: 1000;
575
+ /* Ensure menus appear above all player content including overlays */
576
+ z-index: 20;
573
577
  }
574
578
 
575
579
  /* Menu positioned below button */
@@ -836,7 +840,12 @@
836
840
  text-align: center;
837
841
  transform: translateX(-50%);
838
842
  transition: bottom 0.3s ease;
839
- z-index: 5;
843
+ z-index: 4;
844
+ }
845
+
846
+ .vidply-audio .vidply-captions {
847
+ bottom: auto;
848
+ top: -42px;
840
849
  }
841
850
 
842
851
  .vidply-captions:empty {
@@ -870,7 +879,7 @@
870
879
  position: absolute;
871
880
  right: 0;
872
881
  top: 0;
873
- z-index: 2000;
882
+ z-index: 20;
874
883
  }
875
884
 
876
885
  @keyframes vidply-fade-in {
@@ -1182,7 +1191,7 @@
1182
1191
  top: auto;
1183
1192
  transform: translateX(-50%);
1184
1193
  width: auto;
1185
- z-index: 200;
1194
+ z-index: 15;
1186
1195
  }
1187
1196
 
1188
1197
  .vidply-menu::after {
@@ -1380,7 +1389,7 @@
1380
1389
  position: sticky;
1381
1390
  text-transform: uppercase;
1382
1391
  top: 0;
1383
- z-index: 10;
1392
+ z-index: 2;
1384
1393
  }
1385
1394
 
1386
1395
  .vidply-playlist-list {
@@ -1546,7 +1555,7 @@
1546
1555
  position: absolute;
1547
1556
  top: 0;
1548
1557
  width: 400px;
1549
- z-index: 100;
1558
+ z-index: 5;
1550
1559
  }
1551
1560
 
1552
1561
  /* Minimum height for audio player transcript */
@@ -1668,6 +1677,69 @@
1668
1677
  background: var(--vidply-border-hover);
1669
1678
  }
1670
1679
 
1680
+ /* Sign Language Video */
1681
+ .vidply-sign-language-video {
1682
+ background: var(--vidply-black);
1683
+ border: 2px solid var(--vidply-white-30);
1684
+ border-radius: 4px;
1685
+ box-shadow: 0 4px 12px var(--vidply-black-60);
1686
+ height: auto !important;
1687
+ max-width: 400px;
1688
+ min-width: 150px;
1689
+ position: absolute;
1690
+ transition: opacity 0.3s ease;
1691
+ width: 35% !important;
1692
+ z-index: 3;
1693
+ }
1694
+
1695
+ .vidply-fullscreen .vidply-sign-language-video {
1696
+ max-width: 600px;
1697
+ }
1698
+
1699
+ /* Sign Language Video Positions */
1700
+ .vidply-sign-position-bottom-right {
1701
+ bottom: 16px; /* Above controls */
1702
+ right: 16px;
1703
+ }
1704
+
1705
+ .vidply-controls-visible + .vidply-sign-position-bottom-right,
1706
+ .vidply-controls-visible + .vidply-captions + .vidply-sign-position-bottom-right {
1707
+ bottom: 95px; /* Above controls */
1708
+ }
1709
+
1710
+ .vidply-sign-position-bottom-left {
1711
+ bottom: 16px; /* Above controls */
1712
+ left: 16px;
1713
+ }
1714
+
1715
+ .vidply-controls-visible + .vidply-sign-position-bottom-left,
1716
+ .vidply-controls-visible + .vidply-captions + .vidply-sign-position-bottom-left {
1717
+ bottom: 95px; /* Above controls */
1718
+ }
1719
+
1720
+ .vidply-sign-position-top-right {
1721
+ right: 16px;
1722
+ top: 16px;
1723
+ }
1724
+
1725
+ .vidply-sign-position-top-left {
1726
+ left: 16px;
1727
+ top: 16px;
1728
+ }
1729
+
1730
+ /* Responsive Sign Language Video */
1731
+ @media (width <= 640px) {
1732
+ .vidply-sign-language-video {
1733
+ min-width: 120px;
1734
+ width: 35%;
1735
+ }
1736
+
1737
+ .vidply-sign-position-bottom-right,
1738
+ .vidply-sign-position-bottom-left {
1739
+ bottom: 126px !important; /* Adjust for smaller controls */
1740
+ }
1741
+ }
1742
+
1671
1743
  /* Responsive Adjustments */
1672
1744
  @media (width <= 640px) {
1673
1745
  .vidply-playlist-thumbnail {
@@ -1697,7 +1769,7 @@
1697
1769
  order: 3; /* After controls (which are order: 2) */
1698
1770
  position: relative;
1699
1771
  width: 100%;
1700
- z-index: 50;
1772
+ z-index: 5;
1701
1773
  }
1702
1774
 
1703
1775
  .vidply-transcript-header {
@@ -502,8 +502,8 @@ var iconPaths = {
502
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
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
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: `<path d="M12 2C10.34 2 9 3.34 9 5v4c0 .34.07.66.18.96L7.5 8.29C7.19 8.1 6.85 8 6.5 8 5.12 8 4 9.12 4 10.5v3c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-3c0-.28.22-.5.5-.5s.5.22.5.5V14l2 2v-1c0-.55.45-1 1-1s1 .45 1 1v2c0 .55.45 1 1 1s1-.45 1-1V9c0-.55.45-1 1-1s1 .45 1 1v8c0 2.21-1.79 4-4 4s-4-1.79-4-4v-2.83l-2.93-2.93A3.93 3.93 0 0 1 4 8c0-1.66 1.34-3 3-3 .83 0 1.58.34 2.12.88L11 7.76V5c0-.55.45-1 1-1s1 .45 1 1v4c0 .55.45 1 1 1s1-.45 1-1V5c0-1.66-1.34-3-3-3z"/>`,
506
- signLanguageOn: `<path d="M12 2C10.34 2 9 3.34 9 5v4c0 .34.07.66.18.96L7.5 8.29C7.19 8.1 6.85 8 6.5 8 5.12 8 4 9.12 4 10.5v3c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-3c0-.28.22-.5.5-.5s.5.22.5.5V14l2 2v-1c0-.55.45-1 1-1s1 .45 1 1v2c0 .55.45 1 1 1s1-.45 1-1V9c0-.55.45-1 1-1s1 .45 1 1v8c0 2.21-1.79 4-4 4s-4-1.79-4-4v-2.83l-2.93-2.93A3.93 3.93 0 0 1 4 8c0-1.66 1.34-3 3-3 .83 0 1.58.34 2.12.88L11 7.76V5c0-.55.45-1 1-1s1 .45 1 1v4c0 .55.45 1 1 1s1-.45 1-1V5c0-1.66-1.34-3-3-3z"/><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
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
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
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"/>`,
@@ -5004,7 +5004,7 @@ var Player = class extends EventEmitter {
5004
5004
  this.signLanguageVideo = document.createElement("video");
5005
5005
  this.signLanguageVideo.className = "vidply-sign-language-video";
5006
5006
  this.signLanguageVideo.src = this.signLanguageSrc;
5007
- this.signLanguageVideo.setAttribute("aria-label", this.i18n.t("signLanguageVideo"));
5007
+ this.signLanguageVideo.setAttribute("aria-label", i18n.t("player.signLanguage"));
5008
5008
  const position = this.options.signLanguagePosition || "bottom-right";
5009
5009
  this.signLanguageVideo.classList.add(`vidply-sign-position-${position}`);
5010
5010
  this.signLanguageVideo.muted = true;
@@ -5012,7 +5012,7 @@ var Player = class extends EventEmitter {
5012
5012
  if (!this.state.paused) {
5013
5013
  this.signLanguageVideo.play();
5014
5014
  }
5015
- this.container.appendChild(this.signLanguageVideo);
5015
+ this.videoWrapper.appendChild(this.signLanguageVideo);
5016
5016
  this.signLanguageHandlers = {
5017
5017
  play: () => {
5018
5018
  if (this.signLanguageVideo) {
@@ -5541,9 +5541,56 @@ function initializePlayers() {
5541
5541
  const elements = document.querySelectorAll("[data-vidply]");
5542
5542
  elements.forEach((element) => {
5543
5543
  const options = element.dataset.vidplyOptions ? JSON.parse(element.dataset.vidplyOptions) : {};
5544
- new Player(element, options);
5544
+ const dataOptions = parseDataAttributes(element.dataset);
5545
+ const mergedOptions = { ...dataOptions, ...options };
5546
+ new Player(element, mergedOptions);
5545
5547
  });
5546
5548
  }
5549
+ function parseDataAttributes(dataset) {
5550
+ const options = {};
5551
+ const attributeMap = {
5552
+ // Sign Language
5553
+ "signLanguageSrc": "signLanguageSrc",
5554
+ "signLanguageButton": "signLanguageButton",
5555
+ "signLanguagePosition": "signLanguagePosition",
5556
+ // Audio Description
5557
+ "audioDescriptionSrc": "audioDescriptionSrc",
5558
+ "audioDescriptionButton": "audioDescriptionButton",
5559
+ // Other common options
5560
+ "autoplay": "autoplay",
5561
+ "loop": "loop",
5562
+ "muted": "muted",
5563
+ "controls": "controls",
5564
+ "poster": "poster",
5565
+ "width": "width",
5566
+ "height": "height",
5567
+ "language": "language",
5568
+ "captions": "captions",
5569
+ "captionsDefault": "captionsDefault",
5570
+ "transcript": "transcript",
5571
+ "transcriptButton": "transcriptButton",
5572
+ "keyboard": "keyboard",
5573
+ "responsive": "responsive",
5574
+ "pipButton": "pipButton",
5575
+ "fullscreenButton": "fullscreenButton"
5576
+ };
5577
+ Object.keys(attributeMap).forEach((dataKey) => {
5578
+ const optionKey = attributeMap[dataKey];
5579
+ const value = dataset[dataKey];
5580
+ if (value !== void 0) {
5581
+ if (value === "true") {
5582
+ options[optionKey] = true;
5583
+ } else if (value === "false") {
5584
+ options[optionKey] = false;
5585
+ } else if (!isNaN(value) && value !== "") {
5586
+ options[optionKey] = Number(value);
5587
+ } else {
5588
+ options[optionKey] = value;
5589
+ }
5590
+ }
5591
+ });
5592
+ return options;
5593
+ }
5547
5594
  if (document.readyState === "loading") {
5548
5595
  document.addEventListener("DOMContentLoaded", initializePlayers);
5549
5596
  } else {