vidply 1.0.30 → 1.0.32
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 +708 -708
- package/dist/dev/{vidply.HLSRenderer-UMPUDSYL.js → vidply.HLSRenderer-5MJZR4D2.js} +14 -8
- package/dist/dev/{vidply.HLSRenderer-ENLZE4QS.js.map → vidply.HLSRenderer-5MJZR4D2.js.map} +2 -2
- package/dist/dev/vidply.esm.js +137 -17
- package/dist/dev/vidply.esm.js.map +2 -2
- package/dist/legacy/vidply.js +151 -23
- package/dist/legacy/vidply.js.map +2 -2
- package/dist/legacy/vidply.min.js +1 -1
- package/dist/legacy/vidply.min.meta.json +10 -10
- package/dist/prod/{vidply.HLSRenderer-3CG7BZKA.min.js → vidply.HLSRenderer-VWNJD2CB.min.js} +1 -1
- package/dist/prod/vidply.esm.min.js +9 -9
- package/dist/vidply.esm.min.meta.json +13 -13
- package/package.json +1 -1
- package/src/controls/ControlBar.js +183 -22
- package/src/core/Player.js +4873 -4868
- package/src/features/PlaylistManager.js +1511 -1511
- package/src/renderers/HLSRenderer.js +17 -7
- package/src/utils/VideoFrameCapture.js +11 -2
- package/dist/dev/vidply.HLSRenderer-ENLZE4QS.js +0 -266
- package/dist/dev/vidply.HLSRenderer-UMPUDSYL.js.map +0 -7
- package/dist/dev/vidply.HTML5Renderer-6SBDI6S2.js +0 -12
- package/dist/dev/vidply.HTML5Renderer-6SBDI6S2.js.map +0 -7
- package/dist/dev/vidply.chunk-BCOFCT6U.js +0 -246
- package/dist/dev/vidply.chunk-BCOFCT6U.js.map +0 -7
- package/dist/prod/vidply.HLSRenderer-CBXZ4RF2.min.js +0 -6
- package/dist/prod/vidply.HTML5Renderer-MY7XDV7R.min.js +0 -6
- package/dist/prod/vidply.chunk-OXXPY2XB.min.js +0 -6
|
@@ -104,12 +104,12 @@
|
|
|
104
104
|
"format": "esm"
|
|
105
105
|
},
|
|
106
106
|
"src/utils/VideoFrameCapture.js": {
|
|
107
|
-
"bytes":
|
|
107
|
+
"bytes": 4640,
|
|
108
108
|
"imports": [],
|
|
109
109
|
"format": "esm"
|
|
110
110
|
},
|
|
111
111
|
"src/controls/ControlBar.js": {
|
|
112
|
-
"bytes":
|
|
112
|
+
"bytes": 145861,
|
|
113
113
|
"imports": [
|
|
114
114
|
{
|
|
115
115
|
"path": "src/utils/DOMUtils.js",
|
|
@@ -346,7 +346,7 @@
|
|
|
346
346
|
"format": "esm"
|
|
347
347
|
},
|
|
348
348
|
"src/renderers/HLSRenderer.js": {
|
|
349
|
-
"bytes":
|
|
349
|
+
"bytes": 10310,
|
|
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":
|
|
365
|
+
"bytes": 214379,
|
|
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":
|
|
476
|
+
"bytes": 49922,
|
|
477
477
|
"imports": [
|
|
478
478
|
{
|
|
479
479
|
"path": "src/utils/DOMUtils.js",
|
|
@@ -529,7 +529,7 @@
|
|
|
529
529
|
},
|
|
530
530
|
"bytes": 4730
|
|
531
531
|
},
|
|
532
|
-
"dist/prod/vidply.HLSRenderer-
|
|
532
|
+
"dist/prod/vidply.HLSRenderer-VWNJD2CB.min.js": {
|
|
533
533
|
"imports": [
|
|
534
534
|
{
|
|
535
535
|
"path": "dist/prod/vidply.HTML5Renderer-KKW3OLHM.min.js",
|
|
@@ -542,10 +542,10 @@
|
|
|
542
542
|
"entryPoint": "src/renderers/HLSRenderer.js",
|
|
543
543
|
"inputs": {
|
|
544
544
|
"src/renderers/HLSRenderer.js": {
|
|
545
|
-
"bytesInOutput":
|
|
545
|
+
"bytesInOutput": 5884
|
|
546
546
|
}
|
|
547
547
|
},
|
|
548
|
-
"bytes":
|
|
548
|
+
"bytes": 6028
|
|
549
549
|
},
|
|
550
550
|
"dist/prod/vidply.SoundCloudRenderer-MOR2CUFH.min.js": {
|
|
551
551
|
"imports": [],
|
|
@@ -584,7 +584,7 @@
|
|
|
584
584
|
"kind": "dynamic-import"
|
|
585
585
|
},
|
|
586
586
|
{
|
|
587
|
-
"path": "dist/prod/vidply.HLSRenderer-
|
|
587
|
+
"path": "dist/prod/vidply.HLSRenderer-VWNJD2CB.min.js",
|
|
588
588
|
"kind": "dynamic-import"
|
|
589
589
|
},
|
|
590
590
|
{
|
|
@@ -606,10 +606,10 @@
|
|
|
606
606
|
"bytesInOutput": 256
|
|
607
607
|
},
|
|
608
608
|
"src/utils/VideoFrameCapture.js": {
|
|
609
|
-
"bytesInOutput":
|
|
609
|
+
"bytesInOutput": 1009
|
|
610
610
|
},
|
|
611
611
|
"src/controls/ControlBar.js": {
|
|
612
|
-
"bytesInOutput":
|
|
612
|
+
"bytesInOutput": 62339
|
|
613
613
|
},
|
|
614
614
|
"src/controls/CaptionManager.js": {
|
|
615
615
|
"bytesInOutput": 7279
|
|
@@ -624,7 +624,7 @@
|
|
|
624
624
|
"bytesInOutput": 19117
|
|
625
625
|
},
|
|
626
626
|
"src/core/Player.js": {
|
|
627
|
-
"bytesInOutput":
|
|
627
|
+
"bytesInOutput": 76547
|
|
628
628
|
},
|
|
629
629
|
"src/features/PlaylistManager.js": {
|
|
630
630
|
"bytesInOutput": 21345
|
|
@@ -633,7 +633,7 @@
|
|
|
633
633
|
"bytesInOutput": 1869
|
|
634
634
|
}
|
|
635
635
|
},
|
|
636
|
-
"bytes":
|
|
636
|
+
"bytes": 204201
|
|
637
637
|
},
|
|
638
638
|
"dist/prod/vidply.de-FR3XX54P.min.js": {
|
|
639
639
|
"imports": [],
|
package/package.json
CHANGED
|
@@ -715,7 +715,22 @@ export class ControlBar {
|
|
|
715
715
|
}
|
|
716
716
|
|
|
717
717
|
// 4. Playback speed button
|
|
718
|
-
|
|
718
|
+
// IMPORTANT: Don't rely on renderer.constructor.name here.
|
|
719
|
+
// In production builds, class names are minified (e.g. "class s"), which would break the check.
|
|
720
|
+
// Instead, detect HLS by the current source URL.
|
|
721
|
+
const src = this.player.currentSource
|
|
722
|
+
|| this.player.element?.getAttribute?.('src')
|
|
723
|
+
|| this.player.element?.currentSrc
|
|
724
|
+
|| this.player.element?.src
|
|
725
|
+
|| this.player.element?.querySelector?.('source')?.getAttribute?.('src')
|
|
726
|
+
|| this.player.element?.querySelector?.('source')?.src
|
|
727
|
+
|| '';
|
|
728
|
+
const isHlsSource = typeof src === 'string' && src.includes('.m3u8');
|
|
729
|
+
const isVideoElement = this.player.element?.tagName?.toLowerCase() === 'video';
|
|
730
|
+
const hideSpeedForThisPlayer =
|
|
731
|
+
(!!this.player.options.hideSpeedForHls && isHlsSource)
|
|
732
|
+
|| (!!this.player.options.hideSpeedForHlsVideo && isHlsSource && isVideoElement);
|
|
733
|
+
if (this.player.options.speedButton && !hideSpeedForThisPlayer) {
|
|
719
734
|
const btn = this.createSpeedButton();
|
|
720
735
|
btn.dataset.overflowPriority = '1';
|
|
721
736
|
btn.dataset.overflowPriorityMobile = '3';
|
|
@@ -959,12 +974,19 @@ export class ControlBar {
|
|
|
959
974
|
|
|
960
975
|
// Check if renderer supports preview (HTML5Renderer or HLSRenderer with native support)
|
|
961
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
|
|
962
978
|
const renderer = this.player.renderer;
|
|
963
979
|
const hasVideoMedia = renderer && renderer.media && renderer.media.tagName === 'VIDEO';
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
)
|
|
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 &&
|
|
987
|
+
renderer.media === this.player.element &&
|
|
988
|
+
!renderer.hls &&
|
|
989
|
+
typeof renderer.seek === 'function';
|
|
968
990
|
|
|
969
991
|
this.previewSupported = isHTML5Renderer && hasVideoMedia;
|
|
970
992
|
|
|
@@ -972,34 +994,57 @@ export class ControlBar {
|
|
|
972
994
|
// Create a hidden video element for capturing frames
|
|
973
995
|
this.previewVideo = document.createElement('video');
|
|
974
996
|
this.previewVideo.muted = true;
|
|
975
|
-
this.previewVideo.preload = '
|
|
997
|
+
this.previewVideo.preload = 'auto'; // Need more than metadata to capture frames
|
|
998
|
+
this.previewVideo.playsInline = true;
|
|
976
999
|
this.previewVideo.style.position = 'absolute';
|
|
977
1000
|
this.previewVideo.style.visibility = 'hidden';
|
|
978
1001
|
this.previewVideo.style.width = '1px';
|
|
979
1002
|
this.previewVideo.style.height = '1px';
|
|
980
1003
|
this.previewVideo.style.top = '-9999px';
|
|
981
1004
|
|
|
982
|
-
// Copy source from main video
|
|
1005
|
+
// Copy source and attributes from main video
|
|
983
1006
|
const mainVideo = renderer.media || this.player.element;
|
|
1007
|
+
let videoSrc = null;
|
|
1008
|
+
|
|
984
1009
|
if (mainVideo.src) {
|
|
985
|
-
|
|
1010
|
+
videoSrc = mainVideo.src;
|
|
986
1011
|
} else {
|
|
987
1012
|
const source = mainVideo.querySelector('source');
|
|
988
1013
|
if (source) {
|
|
989
|
-
|
|
1014
|
+
videoSrc = source.src;
|
|
990
1015
|
}
|
|
991
1016
|
}
|
|
992
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
|
+
|
|
993
1029
|
// Handle errors gracefully
|
|
994
|
-
this.previewVideo.addEventListener('error', () => {
|
|
995
|
-
this.player.log('Preview video failed to load', 'warn');
|
|
1030
|
+
this.previewVideo.addEventListener('error', (e) => {
|
|
1031
|
+
this.player.log('Preview video failed to load:', e, 'warn');
|
|
996
1032
|
this.previewSupported = false;
|
|
997
1033
|
});
|
|
998
1034
|
|
|
999
|
-
//
|
|
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
|
|
1000
1041
|
if (this.player.container) {
|
|
1001
1042
|
this.player.container.appendChild(this.previewVideo);
|
|
1002
1043
|
}
|
|
1044
|
+
|
|
1045
|
+
// Set source after appending to DOM
|
|
1046
|
+
this.previewVideo.src = videoSrc;
|
|
1047
|
+
this.previewVideoReady = false;
|
|
1003
1048
|
}
|
|
1004
1049
|
}
|
|
1005
1050
|
|
|
@@ -1013,6 +1058,55 @@ export class ControlBar {
|
|
|
1013
1058
|
return null;
|
|
1014
1059
|
}
|
|
1015
1060
|
|
|
1061
|
+
// Wait for preview video to be ready if not yet loaded
|
|
1062
|
+
if (!this.previewVideoReady) {
|
|
1063
|
+
if (this.previewVideo.readyState < 2) {
|
|
1064
|
+
// Wait for at least HAVE_CURRENT_DATA (2) to ensure we can capture frames
|
|
1065
|
+
await new Promise((resolve, reject) => {
|
|
1066
|
+
const timeout = setTimeout(() => {
|
|
1067
|
+
reject(new Error('Preview video data load timeout'));
|
|
1068
|
+
}, 10000);
|
|
1069
|
+
|
|
1070
|
+
const cleanup = () => {
|
|
1071
|
+
clearTimeout(timeout);
|
|
1072
|
+
this.previewVideo.removeEventListener('loadeddata', checkReady);
|
|
1073
|
+
this.previewVideo.removeEventListener('canplay', checkReady);
|
|
1074
|
+
this.previewVideo.removeEventListener('error', onError);
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
const checkReady = () => {
|
|
1078
|
+
if (this.previewVideo.readyState >= 2) {
|
|
1079
|
+
cleanup();
|
|
1080
|
+
this.previewVideoReady = true;
|
|
1081
|
+
resolve();
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
const onError = () => {
|
|
1086
|
+
cleanup();
|
|
1087
|
+
reject(new Error('Preview video failed to load'));
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
// Try loadeddata first (faster), fallback to canplay
|
|
1091
|
+
if (this.previewVideo.readyState >= 1) {
|
|
1092
|
+
this.previewVideo.addEventListener('loadeddata', checkReady);
|
|
1093
|
+
}
|
|
1094
|
+
this.previewVideo.addEventListener('canplay', checkReady);
|
|
1095
|
+
this.previewVideo.addEventListener('error', onError);
|
|
1096
|
+
|
|
1097
|
+
// If already ready, resolve immediately
|
|
1098
|
+
if (this.previewVideo.readyState >= 2) {
|
|
1099
|
+
checkReady();
|
|
1100
|
+
}
|
|
1101
|
+
}).catch(() => {
|
|
1102
|
+
this.previewSupported = false;
|
|
1103
|
+
return null;
|
|
1104
|
+
});
|
|
1105
|
+
} else {
|
|
1106
|
+
this.previewVideoReady = true;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1016
1110
|
// Check cache first
|
|
1017
1111
|
const cacheKey = Math.floor(time);
|
|
1018
1112
|
if (this.previewThumbnailCache.has(cacheKey)) {
|
|
@@ -1029,8 +1123,9 @@ export class ControlBar {
|
|
|
1029
1123
|
});
|
|
1030
1124
|
|
|
1031
1125
|
if (dataURL) {
|
|
1032
|
-
// Cache the thumbnail (limit cache size)
|
|
1033
|
-
if (this.previewThumbnailCache.size
|
|
1126
|
+
// Cache the thumbnail (limit cache size to 20 entries using LRU-like behavior)
|
|
1127
|
+
if (this.previewThumbnailCache.size >= 20) {
|
|
1128
|
+
// Delete oldest entry (first key in insertion order)
|
|
1034
1129
|
const firstKey = this.previewThumbnailCache.keys().next().value;
|
|
1035
1130
|
this.previewThumbnailCache.delete(firstKey);
|
|
1036
1131
|
}
|
|
@@ -1045,7 +1140,7 @@ export class ControlBar {
|
|
|
1045
1140
|
* @param {number} time - Time in seconds
|
|
1046
1141
|
*/
|
|
1047
1142
|
async updatePreviewThumbnail(time) {
|
|
1048
|
-
if (!this.previewSupported) {
|
|
1143
|
+
if (!this.previewSupported || !this.controls.progressPreview) {
|
|
1049
1144
|
return;
|
|
1050
1145
|
}
|
|
1051
1146
|
|
|
@@ -1056,12 +1151,27 @@ export class ControlBar {
|
|
|
1056
1151
|
|
|
1057
1152
|
// Debounce thumbnail generation to avoid excessive seeking
|
|
1058
1153
|
this.previewThumbnailTimeout = setTimeout(async () => {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
this.controls.progressPreview
|
|
1062
|
-
|
|
1154
|
+
try {
|
|
1155
|
+
const thumbnail = await this.generatePreviewThumbnail(time);
|
|
1156
|
+
if (thumbnail && this.controls.progressPreview) {
|
|
1157
|
+
// Set background image and make visible
|
|
1158
|
+
this.controls.progressPreview.style.backgroundImage = `url("${thumbnail}")`;
|
|
1159
|
+
this.controls.progressPreview.style.display = 'block';
|
|
1160
|
+
this.controls.progressPreview.style.backgroundRepeat = 'no-repeat';
|
|
1161
|
+
this.controls.progressPreview.style.backgroundPosition = 'center';
|
|
1162
|
+
} else {
|
|
1163
|
+
// Hide if thumbnail generation failed
|
|
1164
|
+
if (this.controls.progressPreview) {
|
|
1165
|
+
this.controls.progressPreview.style.display = 'none';
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
this.currentPreviewTime = time;
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
this.player.log('Preview thumbnail update failed:', error, 'warn');
|
|
1171
|
+
if (this.controls.progressPreview) {
|
|
1172
|
+
this.controls.progressPreview.style.display = 'none';
|
|
1173
|
+
}
|
|
1063
1174
|
}
|
|
1064
|
-
this.currentPreviewTime = time;
|
|
1065
1175
|
}, 100);
|
|
1066
1176
|
}
|
|
1067
1177
|
|
|
@@ -1109,8 +1219,10 @@ export class ControlBar {
|
|
|
1109
1219
|
this.controls.progressTooltip.style.left = `${left}px`;
|
|
1110
1220
|
this.controls.progressTooltip.style.display = 'block';
|
|
1111
1221
|
|
|
1112
|
-
// Update preview thumbnail
|
|
1113
|
-
this.
|
|
1222
|
+
// Update preview thumbnail (only if supported)
|
|
1223
|
+
if (this.previewSupported) {
|
|
1224
|
+
this.updatePreviewThumbnail(time);
|
|
1225
|
+
}
|
|
1114
1226
|
}
|
|
1115
1227
|
});
|
|
1116
1228
|
|
|
@@ -2815,6 +2927,12 @@ export class ControlBar {
|
|
|
2815
2927
|
this.updateDuration();
|
|
2816
2928
|
this.ensureQualityButton();
|
|
2817
2929
|
this.updateQualityIndicator();
|
|
2930
|
+
// Update preview video source when metadata loads (for playlists)
|
|
2931
|
+
this.updatePreviewVideoSource();
|
|
2932
|
+
});
|
|
2933
|
+
this.player.on('sourcechange', () => {
|
|
2934
|
+
// Update preview video source when source changes (for playlists)
|
|
2935
|
+
this.updatePreviewVideoSource();
|
|
2818
2936
|
});
|
|
2819
2937
|
this.player.on('volumechange', () => this.updateVolumeDisplay());
|
|
2820
2938
|
this.player.on('progress', () => this.updateBuffered());
|
|
@@ -3476,6 +3594,49 @@ export class ControlBar {
|
|
|
3476
3594
|
this.element.style.display = 'none';
|
|
3477
3595
|
}
|
|
3478
3596
|
|
|
3597
|
+
/**
|
|
3598
|
+
* Update preview video source when player source changes (for playlists)
|
|
3599
|
+
* Also re-initializes if preview wasn't set up initially
|
|
3600
|
+
*/
|
|
3601
|
+
updatePreviewVideoSource() {
|
|
3602
|
+
const renderer = this.player.renderer;
|
|
3603
|
+
if (!renderer || !renderer.media || renderer.media.tagName !== 'VIDEO') {
|
|
3604
|
+
return;
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
// If preview wasn't initialized yet, try to initialize it now
|
|
3608
|
+
if (!this.previewSupported && !this.previewVideo) {
|
|
3609
|
+
this.initPreviewThumbnail();
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
if (!this.previewSupported || !this.previewVideo) {
|
|
3613
|
+
return;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
const mainVideo = renderer.media;
|
|
3617
|
+
const newSrc = mainVideo.src || mainVideo.querySelector('source')?.src;
|
|
3618
|
+
|
|
3619
|
+
if (newSrc && this.previewVideo.src !== newSrc) {
|
|
3620
|
+
// Clear cache when source changes
|
|
3621
|
+
this.previewThumbnailCache.clear();
|
|
3622
|
+
this.previewVideoReady = false;
|
|
3623
|
+
this.previewVideo.src = newSrc;
|
|
3624
|
+
|
|
3625
|
+
// Copy crossOrigin if set
|
|
3626
|
+
if (mainVideo.crossOrigin) {
|
|
3627
|
+
this.previewVideo.crossOrigin = mainVideo.crossOrigin;
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
// Wait for new source to load
|
|
3631
|
+
this.previewVideo.addEventListener('loadedmetadata', () => {
|
|
3632
|
+
this.previewVideoReady = true;
|
|
3633
|
+
}, { once: true });
|
|
3634
|
+
} else if (newSrc && !this.previewVideoReady && this.previewVideo.readyState >= 1) {
|
|
3635
|
+
// If source is the same but video is ready, mark as ready
|
|
3636
|
+
this.previewVideoReady = true;
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3479
3640
|
/**
|
|
3480
3641
|
* Cleanup preview thumbnail resources
|
|
3481
3642
|
*/
|