vidply 1.0.32 → 1.0.33

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 (43) hide show
  1. package/README.md +7 -7
  2. package/dist/dev/{vidply.HLSRenderer-5MJZR4D2.js → vidply.HLSRenderer-LIFBU6UD.js} +48 -3
  3. package/dist/dev/vidply.HLSRenderer-LIFBU6UD.js.map +7 -0
  4. package/dist/dev/{vidply.HTML5Renderer-FXBZQL6Y.js → vidply.HTML5Renderer-YWMVYWFS.js} +2 -2
  5. package/dist/dev/{vidply.TranscriptManager-T677KF4N.js → vidply.TranscriptManager-R7NJRU7E.js} +2 -2
  6. package/dist/dev/{vidply.chunk-GS2JX5RQ.js → vidply.chunk-PMRKJBGH.js} +5 -2
  7. package/dist/dev/vidply.chunk-PMRKJBGH.js.map +7 -0
  8. package/dist/dev/{vidply.chunk-W2LSBD6Y.js → vidply.chunk-UVO24MXU.js} +33 -3
  9. package/dist/dev/vidply.chunk-UVO24MXU.js.map +7 -0
  10. package/dist/dev/{vidply.de-SNL6AJ4D.js → vidply.de-CEGBLV67.js} +4 -1
  11. package/dist/dev/vidply.de-CEGBLV67.js.map +7 -0
  12. package/dist/dev/vidply.esm.js +364 -63
  13. package/dist/dev/vidply.esm.js.map +2 -2
  14. package/dist/legacy/vidply.js +459 -63
  15. package/dist/legacy/vidply.js.map +3 -3
  16. package/dist/legacy/vidply.min.js +1 -1
  17. package/dist/legacy/vidply.min.meta.json +15 -15
  18. package/dist/prod/vidply.HLSRenderer-ESR6NAMI.min.js +6 -0
  19. package/dist/prod/{vidply.HTML5Renderer-KKW3OLHM.min.js → vidply.HTML5Renderer-6ROXQSQY.min.js} +1 -1
  20. package/dist/prod/{vidply.TranscriptManager-WFZSW6NR.min.js → vidply.TranscriptManager-B65LKXGG.min.js} +1 -1
  21. package/dist/prod/vidply.chunk-7HVHEUHH.min.js +6 -0
  22. package/dist/prod/vidply.chunk-IQKD4GUB.min.js +6 -0
  23. package/dist/prod/vidply.de-IHKC573T.min.js +6 -0
  24. package/dist/prod/vidply.esm.min.js +4 -4
  25. package/dist/vidply.esm.min.meta.json +33 -33
  26. package/package.json +1 -1
  27. package/src/controls/ControlBar.js +104 -70
  28. package/src/core/Player.js +217 -3
  29. package/src/features/PlaylistManager.js +175 -17
  30. package/src/i18n/languages/de.js +3 -0
  31. package/src/i18n/languages/en.js +3 -0
  32. package/src/renderers/HLSRenderer.js +60 -1
  33. package/src/renderers/HTML5Renderer.js +43 -5
  34. package/dist/dev/vidply.HLSRenderer-5MJZR4D2.js.map +0 -7
  35. package/dist/dev/vidply.chunk-GS2JX5RQ.js.map +0 -7
  36. package/dist/dev/vidply.chunk-W2LSBD6Y.js.map +0 -7
  37. package/dist/dev/vidply.de-SNL6AJ4D.js.map +0 -7
  38. package/dist/prod/vidply.HLSRenderer-VWNJD2CB.min.js +0 -6
  39. package/dist/prod/vidply.chunk-34RH2THY.min.js +0 -6
  40. package/dist/prod/vidply.chunk-LGTJRPUL.min.js +0 -6
  41. package/dist/prod/vidply.de-FR3XX54P.min.js +0 -6
  42. /package/dist/dev/{vidply.HTML5Renderer-FXBZQL6Y.js.map → vidply.HTML5Renderer-YWMVYWFS.js.map} +0 -0
  43. /package/dist/dev/{vidply.TranscriptManager-T677KF4N.js.map → vidply.TranscriptManager-R7NJRU7E.js.map} +0 -0
@@ -11,12 +11,12 @@
11
11
  "format": "esm"
12
12
  },
13
13
  "src/i18n/languages/en.js": {
14
- "bytes": 7390,
14
+ "bytes": 7637,
15
15
  "imports": [],
16
16
  "format": "esm"
17
17
  },
18
18
  "src/i18n/languages/de.js": {
19
- "bytes": 9108,
19
+ "bytes": 9427,
20
20
  "imports": [],
21
21
  "format": "esm"
22
22
  },
@@ -109,7 +109,7 @@
109
109
  "format": "esm"
110
110
  },
111
111
  "src/controls/ControlBar.js": {
112
- "bytes": 145861,
112
+ "bytes": 147152,
113
113
  "imports": [
114
114
  {
115
115
  "path": "src/utils/DOMUtils.js",
@@ -186,7 +186,7 @@
186
186
  "format": "esm"
187
187
  },
188
188
  "src/renderers/HTML5Renderer.js": {
189
- "bytes": 9645,
189
+ "bytes": 10921,
190
190
  "imports": [],
191
191
  "format": "esm"
192
192
  },
@@ -346,7 +346,7 @@
346
346
  "format": "esm"
347
347
  },
348
348
  "src/renderers/HLSRenderer.js": {
349
- "bytes": 10310,
349
+ "bytes": 12151,
350
350
  "imports": [
351
351
  {
352
352
  "path": "src/renderers/HTML5Renderer.js",
@@ -362,7 +362,7 @@
362
362
  "format": "esm"
363
363
  },
364
364
  "src/core/Player.js": {
365
- "bytes": 214379,
365
+ "bytes": 224782,
366
366
  "imports": [
367
367
  {
368
368
  "path": "src/utils/EventEmitter.js",
@@ -473,7 +473,7 @@
473
473
  "format": "esm"
474
474
  },
475
475
  "src/features/PlaylistManager.js": {
476
- "bytes": 49922,
476
+ "bytes": 57201,
477
477
  "imports": [
478
478
  {
479
479
  "path": "src/utils/DOMUtils.js",
@@ -529,10 +529,10 @@
529
529
  },
530
530
  "bytes": 4730
531
531
  },
532
- "dist/prod/vidply.HLSRenderer-VWNJD2CB.min.js": {
532
+ "dist/prod/vidply.HLSRenderer-ESR6NAMI.min.js": {
533
533
  "imports": [
534
534
  {
535
- "path": "dist/prod/vidply.HTML5Renderer-KKW3OLHM.min.js",
535
+ "path": "dist/prod/vidply.HTML5Renderer-6ROXQSQY.min.js",
536
536
  "kind": "dynamic-import"
537
537
  }
538
538
  ],
@@ -542,10 +542,10 @@
542
542
  "entryPoint": "src/renderers/HLSRenderer.js",
543
543
  "inputs": {
544
544
  "src/renderers/HLSRenderer.js": {
545
- "bytesInOutput": 5884
545
+ "bytesInOutput": 6514
546
546
  }
547
547
  },
548
- "bytes": 6028
548
+ "bytes": 6658
549
549
  },
550
550
  "dist/prod/vidply.SoundCloudRenderer-MOR2CUFH.min.js": {
551
551
  "imports": [],
@@ -564,15 +564,15 @@
564
564
  "dist/prod/vidply.esm.min.js": {
565
565
  "imports": [
566
566
  {
567
- "path": "dist/prod/vidply.chunk-34RH2THY.min.js",
567
+ "path": "dist/prod/vidply.chunk-7HVHEUHH.min.js",
568
568
  "kind": "import-statement"
569
569
  },
570
570
  {
571
- "path": "dist/prod/vidply.chunk-LGTJRPUL.min.js",
571
+ "path": "dist/prod/vidply.chunk-IQKD4GUB.min.js",
572
572
  "kind": "import-statement"
573
573
  },
574
574
  {
575
- "path": "dist/prod/vidply.TranscriptManager-WFZSW6NR.min.js",
575
+ "path": "dist/prod/vidply.TranscriptManager-B65LKXGG.min.js",
576
576
  "kind": "dynamic-import"
577
577
  },
578
578
  {
@@ -584,7 +584,7 @@
584
584
  "kind": "dynamic-import"
585
585
  },
586
586
  {
587
- "path": "dist/prod/vidply.HLSRenderer-VWNJD2CB.min.js",
587
+ "path": "dist/prod/vidply.HLSRenderer-ESR6NAMI.min.js",
588
588
  "kind": "dynamic-import"
589
589
  },
590
590
  {
@@ -609,7 +609,7 @@
609
609
  "bytesInOutput": 1009
610
610
  },
611
611
  "src/controls/ControlBar.js": {
612
- "bytesInOutput": 62339
612
+ "bytesInOutput": 63355
613
613
  },
614
614
  "src/controls/CaptionManager.js": {
615
615
  "bytesInOutput": 7279
@@ -624,18 +624,18 @@
624
624
  "bytesInOutput": 19117
625
625
  },
626
626
  "src/core/Player.js": {
627
- "bytesInOutput": 76547
627
+ "bytesInOutput": 80485
628
628
  },
629
629
  "src/features/PlaylistManager.js": {
630
- "bytesInOutput": 21345
630
+ "bytesInOutput": 24268
631
631
  },
632
632
  "src/index.js": {
633
633
  "bytesInOutput": 1869
634
634
  }
635
635
  },
636
- "bytes": 204201
636
+ "bytes": 212078
637
637
  },
638
- "dist/prod/vidply.de-FR3XX54P.min.js": {
638
+ "dist/prod/vidply.de-IHKC573T.min.js": {
639
639
  "imports": [],
640
640
  "exports": [
641
641
  "de"
@@ -643,10 +643,10 @@
643
643
  "entryPoint": "src/i18n/languages/de.js",
644
644
  "inputs": {
645
645
  "src/i18n/languages/de.js": {
646
- "bytesInOutput": 7226
646
+ "bytesInOutput": 7512
647
647
  }
648
648
  },
649
- "bytes": 7361
649
+ "bytes": 7647
650
650
  },
651
651
  "dist/prod/vidply.es-3IJCQLJ7.min.js": {
652
652
  "imports": [],
@@ -687,10 +687,10 @@
687
687
  },
688
688
  "bytes": 8204
689
689
  },
690
- "dist/prod/vidply.HTML5Renderer-KKW3OLHM.min.js": {
690
+ "dist/prod/vidply.HTML5Renderer-6ROXQSQY.min.js": {
691
691
  "imports": [
692
692
  {
693
- "path": "dist/prod/vidply.chunk-34RH2THY.min.js",
693
+ "path": "dist/prod/vidply.chunk-7HVHEUHH.min.js",
694
694
  "kind": "import-statement"
695
695
  }
696
696
  ],
@@ -701,22 +701,22 @@
701
701
  "inputs": {},
702
702
  "bytes": 192
703
703
  },
704
- "dist/prod/vidply.chunk-34RH2THY.min.js": {
704
+ "dist/prod/vidply.chunk-7HVHEUHH.min.js": {
705
705
  "imports": [],
706
706
  "exports": [
707
707
  "a"
708
708
  ],
709
709
  "inputs": {
710
710
  "src/renderers/HTML5Renderer.js": {
711
- "bytesInOutput": 4974
711
+ "bytesInOutput": 5387
712
712
  }
713
713
  },
714
- "bytes": 5108
714
+ "bytes": 5521
715
715
  },
716
- "dist/prod/vidply.TranscriptManager-WFZSW6NR.min.js": {
716
+ "dist/prod/vidply.TranscriptManager-B65LKXGG.min.js": {
717
717
  "imports": [
718
718
  {
719
- "path": "dist/prod/vidply.chunk-LGTJRPUL.min.js",
719
+ "path": "dist/prod/vidply.chunk-IQKD4GUB.min.js",
720
720
  "kind": "import-statement"
721
721
  }
722
722
  ],
@@ -731,10 +731,10 @@
731
731
  },
732
732
  "bytes": 43042
733
733
  },
734
- "dist/prod/vidply.chunk-LGTJRPUL.min.js": {
734
+ "dist/prod/vidply.chunk-IQKD4GUB.min.js": {
735
735
  "imports": [
736
736
  {
737
- "path": "dist/prod/vidply.de-FR3XX54P.min.js",
737
+ "path": "dist/prod/vidply.de-IHKC573T.min.js",
738
738
  "kind": "dynamic-import"
739
739
  },
740
740
  {
@@ -771,7 +771,7 @@
771
771
  "bytesInOutput": 2348
772
772
  },
773
773
  "src/i18n/languages/en.js": {
774
- "bytesInOutput": 6159
774
+ "bytesInOutput": 6385
775
775
  },
776
776
  "src/i18n/translations.js": {
777
777
  "bytesInOutput": 340
@@ -801,7 +801,7 @@
801
801
  "bytesInOutput": 776
802
802
  }
803
803
  },
804
- "bytes": 40251
804
+ "bytes": 40477
805
805
  },
806
806
  "dist/prod/vidply.YouTubeRenderer-MFC2GMAC.min.js": {
807
807
  "imports": [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidply",
3
- "version": "1.0.32",
3
+ "version": "1.0.33",
4
4
  "description": "Universal, accessible video & audio player with ES6 modules",
5
5
  "type": "module",
6
6
  "main": "dist/prod/vidply.esm.min.js",
@@ -27,6 +27,10 @@ export class ControlBar {
27
27
  init() {
28
28
  this.createElement();
29
29
  this.createControls();
30
+ // Ensure time UI reflects any prefilled state (e.g. initialDuration)
31
+ // even when media metadata is deferred and 'loadedmetadata' won't fire yet.
32
+ this.updateDuration();
33
+ this.updateProgress();
30
34
  this.attachEvents();
31
35
  this.setupAutoHide();
32
36
  this.setupOverflowDetection();
@@ -846,22 +850,44 @@ export class ControlBar {
846
850
 
847
851
  // Helper methods to check for available features
848
852
  hasChapterTracks() {
853
+ // 1) Prefer already-loaded TextTracks (fast + accurate)
849
854
  const textTracks = this.player.element.textTracks;
850
855
  for (let i = 0; i < textTracks.length; i++) {
851
- if (textTracks[i].kind === 'chapters') {
852
- return true;
856
+ if (textTracks[i].kind === 'chapters') return true;
853
857
  }
858
+
859
+ // 2) Fallback to DOM <track> elements (works before tracks are fully loaded)
860
+ const trackEls = Array.from(this.player.element.querySelectorAll('track[kind="chapters"]'));
861
+ if (trackEls.length > 0) return true;
862
+
863
+ // 3) Playlist metadata fallback (works even when we intentionally defer loading)
864
+ const current = this.player.playlistManager?.getCurrentTrack?.();
865
+ if (current?.tracks && Array.isArray(current.tracks)) {
866
+ return current.tracks.some(t => t?.kind === 'chapters');
854
867
  }
868
+
855
869
  return false;
856
870
  }
857
871
 
858
872
  hasCaptionTracks() {
873
+ // 1) Prefer already-loaded TextTracks
859
874
  const textTracks = this.player.element.textTracks;
860
875
  for (let i = 0; i < textTracks.length; i++) {
861
- if (textTracks[i].kind === 'captions' || textTracks[i].kind === 'subtitles') {
876
+ if (textTracks[i].kind === 'captions' || textTracks[i].kind === 'subtitles') return true;
877
+ }
878
+
879
+ // 2) Fallback to DOM <track> elements
880
+ const trackEls = Array.from(this.player.element.querySelectorAll('track'));
881
+ if (trackEls.some(el => (el.getAttribute('kind') === 'captions' || el.getAttribute('kind') === 'subtitles'))) {
862
882
  return true;
863
883
  }
884
+
885
+ // 3) Playlist metadata fallback
886
+ const current = this.player.playlistManager?.getCurrentTrack?.();
887
+ if (current?.tracks && Array.isArray(current.tracks)) {
888
+ return current.tracks.some(t => t?.kind === 'captions' || t?.kind === 'subtitles');
864
889
  }
890
+
865
891
  return false;
866
892
  }
867
893
 
@@ -963,6 +989,8 @@ export class ControlBar {
963
989
  this.currentPreviewTime = null;
964
990
  this.previewThumbnailTimeout = null;
965
991
  this.previewSupported = false;
992
+ this.previewVideoReady = false;
993
+ this.previewVideoInitialized = false;
966
994
 
967
995
  // Check if preview is supported (HTML5 video only)
968
996
  // Check if element is a video
@@ -972,80 +1000,75 @@ export class ControlBar {
972
1000
  return;
973
1001
  }
974
1002
 
975
- // Check if renderer supports preview (HTML5Renderer or HLSRenderer with native support)
976
- // We check if renderer has a media property that is a video element
977
- // Note: Don't rely on constructor.name as it's minified in production builds
1003
+ // IMPORTANT: do NOT create/load the preview video until the user has started playback at least once.
1004
+ // Otherwise we'd trigger heavy MP4 network traffic just by hovering the progress bar.
1005
+ }
1006
+
1007
+ /**
1008
+ * Lazily create the hidden preview video (only after playback started once)
1009
+ */
1010
+ ensurePreviewVideoInitialized() {
1011
+ if (this.previewVideoInitialized) return;
1012
+ if (!this.player?.state?.hasStartedPlayback) return;
1013
+
978
1014
  const renderer = this.player.renderer;
979
1015
  const hasVideoMedia = renderer && renderer.media && renderer.media.tagName === 'VIDEO';
980
-
981
- // Check if it's HTML5Renderer by checking:
982
- // 1. Has media property that is a video element
983
- // 2. Media is the same as player.element (HTML5Renderer sets this.media = player.element)
984
- // 3. Doesn't have hls property (HLSRenderer has hls property)
985
- // 4. Has seek method (all renderers have this, but combined with above checks it's reliable)
986
- const isHTML5Renderer = hasVideoMedia &&
1016
+ const isHTML5Renderer = hasVideoMedia &&
987
1017
  renderer.media === this.player.element &&
988
1018
  !renderer.hls &&
989
1019
  typeof renderer.seek === 'function';
990
-
1020
+
991
1021
  this.previewSupported = isHTML5Renderer && hasVideoMedia;
1022
+ if (!this.previewSupported) return;
992
1023
 
993
- if (this.previewSupported) {
994
- // Create a hidden video element for capturing frames
995
- this.previewVideo = document.createElement('video');
996
- this.previewVideo.muted = true;
997
- this.previewVideo.preload = 'auto'; // Need more than metadata to capture frames
998
- this.previewVideo.playsInline = true;
999
- this.previewVideo.style.position = 'absolute';
1000
- this.previewVideo.style.visibility = 'hidden';
1001
- this.previewVideo.style.width = '1px';
1002
- this.previewVideo.style.height = '1px';
1003
- this.previewVideo.style.top = '-9999px';
1004
-
1005
- // Copy source and attributes from main video
1006
- const mainVideo = renderer.media || this.player.element;
1007
- let videoSrc = null;
1008
-
1009
- if (mainVideo.src) {
1010
- videoSrc = mainVideo.src;
1011
- } else {
1012
- const source = mainVideo.querySelector('source');
1013
- if (source) {
1014
- videoSrc = source.src;
1015
- }
1016
- }
1017
-
1018
- if (!videoSrc) {
1019
- this.player.log('No video source found for preview', 'warn');
1020
- this.previewSupported = false;
1021
- return;
1022
- }
1023
-
1024
- // Copy crossOrigin if set (important for CORS)
1025
- if (mainVideo.crossOrigin) {
1026
- this.previewVideo.crossOrigin = mainVideo.crossOrigin;
1027
- }
1028
-
1029
- // Handle errors gracefully
1030
- this.previewVideo.addEventListener('error', (e) => {
1031
- this.player.log('Preview video failed to load:', e, 'warn');
1032
- this.previewSupported = false;
1033
- });
1034
-
1035
- // Wait for metadata to be loaded before using
1036
- this.previewVideo.addEventListener('loadedmetadata', () => {
1037
- this.previewVideoReady = true;
1038
- }, { once: true });
1039
-
1040
- // Append to player container (hidden) BEFORE setting src
1041
- if (this.player.container) {
1042
- this.player.container.appendChild(this.previewVideo);
1024
+ const mainVideo = renderer.media || this.player.element;
1025
+ let videoSrc = null;
1026
+ if (mainVideo.src) {
1027
+ videoSrc = mainVideo.src;
1028
+ } else {
1029
+ const source = mainVideo.querySelector('source');
1030
+ if (source) {
1031
+ videoSrc = source.src;
1043
1032
  }
1044
-
1045
- // Set source after appending to DOM
1046
- this.previewVideo.src = videoSrc;
1047
- this.previewVideoReady = false;
1048
1033
  }
1034
+
1035
+ if (!videoSrc) {
1036
+ this.player.log('No video source found for preview', 'warn');
1037
+ this.previewSupported = false;
1038
+ return;
1039
+ }
1040
+
1041
+ // Create a hidden video element for capturing frames
1042
+ this.previewVideo = document.createElement('video');
1043
+ this.previewVideo.muted = true;
1044
+ this.previewVideo.preload = 'auto'; // Need more than metadata to capture frames
1045
+ this.previewVideo.playsInline = true;
1046
+ this.previewVideo.style.position = 'absolute';
1047
+ this.previewVideo.style.visibility = 'hidden';
1048
+ this.previewVideo.style.width = '1px';
1049
+ this.previewVideo.style.height = '1px';
1050
+ this.previewVideo.style.top = '-9999px';
1051
+
1052
+ if (mainVideo.crossOrigin) {
1053
+ this.previewVideo.crossOrigin = mainVideo.crossOrigin;
1054
+ }
1055
+
1056
+ this.previewVideo.addEventListener('error', (e) => {
1057
+ this.player.log('Preview video failed to load:', e, 'warn');
1058
+ this.previewSupported = false;
1059
+ });
1060
+
1061
+ this.previewVideo.addEventListener('loadedmetadata', () => {
1062
+ this.previewVideoReady = true;
1063
+ }, { once: true });
1064
+
1065
+ if (this.player.container) {
1066
+ this.player.container.appendChild(this.previewVideo);
1067
+ }
1068
+
1069
+ this.previewVideo.src = videoSrc;
1070
+ this.previewVideoReady = false;
1071
+ this.previewVideoInitialized = true;
1049
1072
  }
1050
1073
 
1051
1074
  /**
@@ -1218,10 +1241,21 @@ export class ControlBar {
1218
1241
  // Update tooltip position
1219
1242
  this.controls.progressTooltip.style.left = `${left}px`;
1220
1243
  this.controls.progressTooltip.style.display = 'block';
1221
-
1222
- // Update preview thumbnail (only if supported)
1244
+
1245
+ // Only show preview thumbnails after the user has started playback at least once.
1246
+ // Before that, show just the timestamp (no empty preview box).
1247
+ if (!this.player?.state?.hasStartedPlayback) {
1248
+ if (this.controls.progressPreview) {
1249
+ this.controls.progressPreview.style.display = 'none';
1250
+ }
1251
+ return;
1252
+ }
1253
+
1254
+ this.ensurePreviewVideoInitialized();
1223
1255
  if (this.previewSupported) {
1224
1256
  this.updatePreviewThumbnail(time);
1257
+ } else if (this.controls.progressPreview) {
1258
+ this.controls.progressPreview.style.display = 'none';
1225
1259
  }
1226
1260
  }
1227
1261
  });