unified-video-framework 1.4.146 → 1.4.148
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.
|
@@ -153,8 +153,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
153
153
|
this.video = document.createElement('video');
|
|
154
154
|
this.video.className = 'uvf-video';
|
|
155
155
|
this.video.controls = false; // We'll use custom controls
|
|
156
|
+
// For autoplay to work, video must be muted in most browsers
|
|
156
157
|
this.video.autoplay = this.config.autoPlay ?? false;
|
|
157
|
-
this.video.muted = this.config.muted ?? false;
|
|
158
|
+
this.video.muted = this.config.autoPlay ? true : (this.config.muted ?? false);
|
|
158
159
|
this.video.loop = this.config.loop ?? false;
|
|
159
160
|
this.video.playsInline = this.config.playsInline ?? true;
|
|
160
161
|
this.video.preload = this.config.preload ?? 'metadata';
|
|
@@ -193,6 +194,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
193
194
|
this.setupKeyboardShortcuts();
|
|
194
195
|
this.setupWatermark();
|
|
195
196
|
this.setupFullscreenListeners();
|
|
197
|
+
this.setupUserInteractionTracking();
|
|
196
198
|
|
|
197
199
|
// Initialize paywall controller if provided
|
|
198
200
|
try {
|
|
@@ -518,7 +520,15 @@ export class WebPlayer extends BasePlayer {
|
|
|
518
520
|
|
|
519
521
|
// Start playback if autoPlay is enabled
|
|
520
522
|
if (this.config.autoPlay) {
|
|
521
|
-
|
|
523
|
+
// Attempt autoplay, but handle gracefully if blocked
|
|
524
|
+
this.play().catch(error => {
|
|
525
|
+
if (this.isAutoplayRestrictionError(error)) {
|
|
526
|
+
this.debugWarn('HLS autoplay blocked, showing play overlay');
|
|
527
|
+
this.showPlayOverlay();
|
|
528
|
+
} else {
|
|
529
|
+
this.debugError('HLS autoplay failed:', error);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
522
532
|
}
|
|
523
533
|
});
|
|
524
534
|
|
|
@@ -681,6 +691,167 @@ export class WebPlayer extends BasePlayer {
|
|
|
681
691
|
);
|
|
682
692
|
}
|
|
683
693
|
|
|
694
|
+
private isAutoplayRestrictionError(err: any): boolean {
|
|
695
|
+
if (!err) return false;
|
|
696
|
+
|
|
697
|
+
const message = (err.message || '').toLowerCase();
|
|
698
|
+
const name = (err.name || '').toLowerCase();
|
|
699
|
+
|
|
700
|
+
// Common autoplay restriction error patterns
|
|
701
|
+
return (
|
|
702
|
+
name === 'notallowederror' ||
|
|
703
|
+
message.includes('user didn\'t interact') ||
|
|
704
|
+
message.includes('autoplay') ||
|
|
705
|
+
message.includes('gesture') ||
|
|
706
|
+
message.includes('user activation') ||
|
|
707
|
+
message.includes('play() failed') ||
|
|
708
|
+
message.includes('user interaction')
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
private showPlayOverlay(): void {
|
|
713
|
+
// Remove existing overlay
|
|
714
|
+
this.hidePlayOverlay();
|
|
715
|
+
|
|
716
|
+
const overlay = document.createElement('div');
|
|
717
|
+
overlay.id = 'uvf-play-overlay';
|
|
718
|
+
overlay.className = 'uvf-play-overlay';
|
|
719
|
+
|
|
720
|
+
const playButton = document.createElement('button');
|
|
721
|
+
playButton.className = 'uvf-play-button';
|
|
722
|
+
playButton.innerHTML = `
|
|
723
|
+
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
724
|
+
<path d="M8 5v14l11-7z"/>
|
|
725
|
+
</svg>
|
|
726
|
+
`;
|
|
727
|
+
|
|
728
|
+
const message = document.createElement('div');
|
|
729
|
+
message.className = 'uvf-play-message';
|
|
730
|
+
message.textContent = 'Click to play';
|
|
731
|
+
|
|
732
|
+
overlay.appendChild(playButton);
|
|
733
|
+
overlay.appendChild(message);
|
|
734
|
+
|
|
735
|
+
// Add click handler
|
|
736
|
+
playButton.addEventListener('click', async (e) => {
|
|
737
|
+
e.stopPropagation();
|
|
738
|
+
this.lastUserInteraction = Date.now();
|
|
739
|
+
|
|
740
|
+
try {
|
|
741
|
+
await this.play();
|
|
742
|
+
} catch (error) {
|
|
743
|
+
this.debugError('Failed to play after user interaction:', error);
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
// Also allow clicking anywhere on overlay
|
|
748
|
+
overlay.addEventListener('click', async (e) => {
|
|
749
|
+
if (e.target === overlay) {
|
|
750
|
+
e.stopPropagation();
|
|
751
|
+
this.lastUserInteraction = Date.now();
|
|
752
|
+
|
|
753
|
+
try {
|
|
754
|
+
await this.play();
|
|
755
|
+
} catch (error) {
|
|
756
|
+
this.debugError('Failed to play after user interaction:', error);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
// Add styles
|
|
762
|
+
const style = document.createElement('style');
|
|
763
|
+
style.textContent = `
|
|
764
|
+
.uvf-play-overlay {
|
|
765
|
+
position: absolute;
|
|
766
|
+
top: 0;
|
|
767
|
+
left: 0;
|
|
768
|
+
width: 100%;
|
|
769
|
+
height: 100%;
|
|
770
|
+
background: rgba(0, 0, 0, 0.7);
|
|
771
|
+
display: flex;
|
|
772
|
+
flex-direction: column;
|
|
773
|
+
justify-content: center;
|
|
774
|
+
align-items: center;
|
|
775
|
+
z-index: 1000;
|
|
776
|
+
cursor: pointer;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
.uvf-play-button {
|
|
780
|
+
width: 80px;
|
|
781
|
+
height: 80px;
|
|
782
|
+
border-radius: 50%;
|
|
783
|
+
background: rgba(255, 255, 255, 0.9);
|
|
784
|
+
border: none;
|
|
785
|
+
color: #000;
|
|
786
|
+
cursor: pointer;
|
|
787
|
+
display: flex;
|
|
788
|
+
align-items: center;
|
|
789
|
+
justify-content: center;
|
|
790
|
+
transition: all 0.3s ease;
|
|
791
|
+
margin-bottom: 16px;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.uvf-play-button:hover {
|
|
795
|
+
background: #fff;
|
|
796
|
+
transform: scale(1.1);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.uvf-play-button svg {
|
|
800
|
+
width: 32px;
|
|
801
|
+
height: 32px;
|
|
802
|
+
margin-left: 4px;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.uvf-play-message {
|
|
806
|
+
color: white;
|
|
807
|
+
font-size: 16px;
|
|
808
|
+
font-weight: 500;
|
|
809
|
+
text-align: center;
|
|
810
|
+
opacity: 0.9;
|
|
811
|
+
}
|
|
812
|
+
`;
|
|
813
|
+
|
|
814
|
+
// Add to page if not already added
|
|
815
|
+
if (!document.getElementById('uvf-play-overlay-styles')) {
|
|
816
|
+
style.id = 'uvf-play-overlay-styles';
|
|
817
|
+
document.head.appendChild(style);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Add to player
|
|
821
|
+
if (this.playerWrapper) {
|
|
822
|
+
this.playerWrapper.appendChild(overlay);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
private hidePlayOverlay(): void {
|
|
827
|
+
const overlay = document.getElementById('uvf-play-overlay');
|
|
828
|
+
if (overlay) {
|
|
829
|
+
overlay.remove();
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
private setupUserInteractionTracking(): void {
|
|
834
|
+
// Track various user interactions to enable autoplay
|
|
835
|
+
const interactionEvents = ['click', 'mousedown', 'keydown', 'touchstart'];
|
|
836
|
+
|
|
837
|
+
const updateLastInteraction = () => {
|
|
838
|
+
this.lastUserInteraction = Date.now();
|
|
839
|
+
this.debugLog('User interaction detected at:', this.lastUserInteraction);
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
// Listen on document for global interactions
|
|
843
|
+
interactionEvents.forEach(eventType => {
|
|
844
|
+
document.addEventListener(eventType, updateLastInteraction, { passive: true });
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
// Also listen on player wrapper for more specific interactions
|
|
848
|
+
if (this.playerWrapper) {
|
|
849
|
+
interactionEvents.forEach(eventType => {
|
|
850
|
+
this.playerWrapper!.addEventListener(eventType, updateLastInteraction, { passive: true });
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
684
855
|
async play(): Promise<void> {
|
|
685
856
|
if (!this.video) throw new Error('Video element not initialized');
|
|
686
857
|
|
|
@@ -710,6 +881,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
710
881
|
this.video.pause();
|
|
711
882
|
}
|
|
712
883
|
|
|
884
|
+
// Hide the play overlay if it exists
|
|
885
|
+
this.hidePlayOverlay();
|
|
713
886
|
await super.play();
|
|
714
887
|
} catch (err) {
|
|
715
888
|
this._playPromise = null;
|
|
@@ -717,6 +890,14 @@ export class WebPlayer extends BasePlayer {
|
|
|
717
890
|
// Benign: pause() raced play(); ignore the error.
|
|
718
891
|
return;
|
|
719
892
|
}
|
|
893
|
+
|
|
894
|
+
// Check if this is an autoplay restriction error
|
|
895
|
+
if (this.isAutoplayRestrictionError(err)) {
|
|
896
|
+
this.debugWarn('Autoplay blocked by browser policy - showing play overlay');
|
|
897
|
+
this.showPlayOverlay();
|
|
898
|
+
return; // Don't throw error for autoplay restrictions
|
|
899
|
+
}
|
|
900
|
+
|
|
720
901
|
this.handleError({
|
|
721
902
|
code: 'PLAY_ERROR',
|
|
722
903
|
message: `Failed to start playbook: ${err}`,
|
|
@@ -1932,8 +2113,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
1932
2113
|
|
|
1933
2114
|
/* Center Play Button */
|
|
1934
2115
|
.uvf-center-play-btn {
|
|
1935
|
-
width: clamp(
|
|
1936
|
-
height: clamp(
|
|
2116
|
+
width: clamp(40px, 8vw, 60px);
|
|
2117
|
+
height: clamp(40px, 8vw, 60px);
|
|
1937
2118
|
background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
|
|
1938
2119
|
border: 0;
|
|
1939
2120
|
border-radius: 50%;
|
|
@@ -1945,13 +2126,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
1945
2126
|
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
1946
2127
|
opacity: 1;
|
|
1947
2128
|
visibility: visible;
|
|
1948
|
-
box-shadow: 0
|
|
2129
|
+
box-shadow: 0 4px 16px var(--uvf-accent-1-20);
|
|
1949
2130
|
}
|
|
1950
2131
|
|
|
1951
2132
|
.uvf-center-play-btn:hover {
|
|
1952
|
-
transform: scale(1.
|
|
2133
|
+
transform: scale(1.08);
|
|
1953
2134
|
filter: saturate(1.08) brightness(1.05);
|
|
1954
|
-
box-shadow: 0
|
|
2135
|
+
box-shadow: 0 6px 20px var(--uvf-accent-1-20);
|
|
1955
2136
|
}
|
|
1956
2137
|
|
|
1957
2138
|
.uvf-center-play-btn.hidden {
|
|
@@ -1963,11 +2144,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
1963
2144
|
}
|
|
1964
2145
|
|
|
1965
2146
|
.uvf-center-play-btn svg {
|
|
1966
|
-
width: clamp(
|
|
1967
|
-
height: clamp(
|
|
2147
|
+
width: clamp(18px, 3vw, 24px);
|
|
2148
|
+
height: clamp(18px, 3vw, 24px);
|
|
1968
2149
|
fill: #fff;
|
|
1969
|
-
margin-left:
|
|
1970
|
-
filter: drop-shadow(0
|
|
2150
|
+
margin-left: 2px;
|
|
2151
|
+
filter: drop-shadow(0 1px 3px rgba(0,0,0,0.35));
|
|
1971
2152
|
}
|
|
1972
2153
|
|
|
1973
2154
|
/* Pulse animation for center play button when paused */
|
|
@@ -1977,15 +2158,15 @@ export class WebPlayer extends BasePlayer {
|
|
|
1977
2158
|
|
|
1978
2159
|
@keyframes uvf-centerPlayPulse {
|
|
1979
2160
|
0% {
|
|
1980
|
-
box-shadow: 0
|
|
2161
|
+
box-shadow: 0 4px 16px var(--uvf-accent-1-20);
|
|
1981
2162
|
filter: saturate(1) brightness(1);
|
|
1982
2163
|
}
|
|
1983
2164
|
50% {
|
|
1984
|
-
box-shadow: 0
|
|
2165
|
+
box-shadow: 0 6px 20px var(--uvf-accent-1-20), 0 0 20px rgba(255,0,0,0.1);
|
|
1985
2166
|
filter: saturate(1.05) brightness(1.02);
|
|
1986
2167
|
}
|
|
1987
2168
|
100% {
|
|
1988
|
-
box-shadow: 0
|
|
2169
|
+
box-shadow: 0 4px 16px var(--uvf-accent-1-20);
|
|
1989
2170
|
filter: saturate(1) brightness(1);
|
|
1990
2171
|
}
|
|
1991
2172
|
}
|
|
@@ -2031,10 +2212,22 @@ export class WebPlayer extends BasePlayer {
|
|
|
2031
2212
|
width: 100%;
|
|
2032
2213
|
position: relative;
|
|
2033
2214
|
cursor: pointer;
|
|
2034
|
-
padding:
|
|
2215
|
+
padding: 6px 0;
|
|
2035
2216
|
overflow: visible;
|
|
2036
2217
|
}
|
|
2037
2218
|
|
|
2219
|
+
/* Extended touch area for better mobile UX without affecting visual spacing */
|
|
2220
|
+
.uvf-progress-bar-wrapper::before {
|
|
2221
|
+
content: '';
|
|
2222
|
+
position: absolute;
|
|
2223
|
+
top: -8px;
|
|
2224
|
+
bottom: -8px;
|
|
2225
|
+
left: 0;
|
|
2226
|
+
right: 0;
|
|
2227
|
+
cursor: pointer;
|
|
2228
|
+
z-index: 10;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2038
2231
|
.uvf-progress-bar {
|
|
2039
2232
|
width: 100%;
|
|
2040
2233
|
height: 2px;
|
|
@@ -2146,7 +2339,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
2146
2339
|
/* Mobile responsive design with enhanced touch targets */
|
|
2147
2340
|
@media (max-width: 768px) {
|
|
2148
2341
|
.uvf-progress-bar-wrapper {
|
|
2149
|
-
padding:
|
|
2342
|
+
padding: 8px 0; /* Optimized touch area */
|
|
2150
2343
|
}
|
|
2151
2344
|
|
|
2152
2345
|
.uvf-progress-bar {
|
|
@@ -3214,13 +3407,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
3214
3407
|
}
|
|
3215
3408
|
|
|
3216
3409
|
.uvf-player-wrapper.uvf-fullscreen .uvf-center-play-btn {
|
|
3217
|
-
width:
|
|
3218
|
-
height:
|
|
3410
|
+
width: 64px;
|
|
3411
|
+
height: 64px;
|
|
3219
3412
|
}
|
|
3220
3413
|
|
|
3221
3414
|
.uvf-player-wrapper.uvf-fullscreen .uvf-center-play-btn svg {
|
|
3222
|
-
width:
|
|
3223
|
-
height:
|
|
3415
|
+
width: 28px;
|
|
3416
|
+
height: 28px;
|
|
3224
3417
|
}
|
|
3225
3418
|
|
|
3226
3419
|
/* Ensure overlays remain visible in fullscreen with consistent spacing */
|
|
@@ -3819,7 +4012,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
3819
4012
|
height: 44px !important;
|
|
3820
4013
|
min-width: 44px !important;
|
|
3821
4014
|
min-height: 44px !important;
|
|
3822
|
-
background: rgba(255,255,255,0.15) !important;
|
|
3823
4015
|
backdrop-filter: blur(8px) !important;
|
|
3824
4016
|
border-radius: 22px !important;
|
|
3825
4017
|
align-items: center !important;
|
|
@@ -4085,22 +4277,22 @@ export class WebPlayer extends BasePlayer {
|
|
|
4085
4277
|
|
|
4086
4278
|
/* Tablet center play button - consistent theming */
|
|
4087
4279
|
.uvf-center-play-btn {
|
|
4088
|
-
width: clamp(
|
|
4089
|
-
height: clamp(
|
|
4280
|
+
width: clamp(48px, 10vw, 64px);
|
|
4281
|
+
height: clamp(48px, 10vw, 64px);
|
|
4090
4282
|
background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
|
|
4091
4283
|
border: 0;
|
|
4092
|
-
box-shadow: 0
|
|
4284
|
+
box-shadow: 0 4px 16px var(--uvf-accent-1-20);
|
|
4093
4285
|
}
|
|
4094
4286
|
|
|
4095
4287
|
.uvf-center-play-btn:hover {
|
|
4096
|
-
transform: scale(1.
|
|
4288
|
+
transform: scale(1.08);
|
|
4097
4289
|
filter: saturate(1.08) brightness(1.05);
|
|
4098
|
-
box-shadow: 0
|
|
4290
|
+
box-shadow: 0 6px 20px var(--uvf-accent-1-20);
|
|
4099
4291
|
}
|
|
4100
4292
|
|
|
4101
4293
|
.uvf-center-play-btn svg {
|
|
4102
|
-
width: clamp(
|
|
4103
|
-
height: clamp(
|
|
4294
|
+
width: clamp(20px, 3.5vw, 26px);
|
|
4295
|
+
height: clamp(20px, 3.5vw, 26px);
|
|
4104
4296
|
}
|
|
4105
4297
|
|
|
4106
4298
|
/* Tablet volume control - keep desktop functionality */
|
|
@@ -4249,8 +4441,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
4249
4441
|
|
|
4250
4442
|
/* Enhanced center play button with smooth transitions */
|
|
4251
4443
|
.uvf-center-play-btn {
|
|
4252
|
-
width: clamp(
|
|
4253
|
-
height: clamp(
|
|
4444
|
+
width: clamp(56px, 10vw, 72px);
|
|
4445
|
+
height: clamp(56px, 10vw, 72px);
|
|
4254
4446
|
background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
|
|
4255
4447
|
border: 0;
|
|
4256
4448
|
border-radius: 50%;
|
|
@@ -4260,13 +4452,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
4260
4452
|
align-items: center;
|
|
4261
4453
|
justify-content: center;
|
|
4262
4454
|
cursor: pointer;
|
|
4263
|
-
box-shadow: 0
|
|
4455
|
+
box-shadow: 0 4px 16px var(--uvf-accent-1-20);
|
|
4264
4456
|
}
|
|
4265
4457
|
|
|
4266
4458
|
.uvf-center-play-btn:hover {
|
|
4267
|
-
transform: scale(1.
|
|
4459
|
+
transform: scale(1.08);
|
|
4268
4460
|
filter: saturate(1.08) brightness(1.05);
|
|
4269
|
-
box-shadow: 0
|
|
4461
|
+
box-shadow: 0 6px 20px var(--uvf-accent-1-20);
|
|
4270
4462
|
}
|
|
4271
4463
|
|
|
4272
4464
|
.uvf-center-play-btn:active {
|
|
@@ -4275,11 +4467,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
4275
4467
|
}
|
|
4276
4468
|
|
|
4277
4469
|
.uvf-center-play-btn svg {
|
|
4278
|
-
width: clamp(
|
|
4279
|
-
height: clamp(
|
|
4470
|
+
width: clamp(24px, 4vw, 30px);
|
|
4471
|
+
height: clamp(24px, 4vw, 30px);
|
|
4280
4472
|
fill: #fff;
|
|
4281
|
-
margin-left:
|
|
4282
|
-
filter: drop-shadow(0
|
|
4473
|
+
margin-left: 3px;
|
|
4474
|
+
filter: drop-shadow(0 1px 3px rgba(0,0,0,0.35));
|
|
4283
4475
|
}
|
|
4284
4476
|
|
|
4285
4477
|
/* Optional: Add subtle pulse animation on idle */
|
|
@@ -4360,23 +4552,23 @@ export class WebPlayer extends BasePlayer {
|
|
|
4360
4552
|
}
|
|
4361
4553
|
|
|
4362
4554
|
.uvf-center-play-btn {
|
|
4363
|
-
width: clamp(
|
|
4364
|
-
height: clamp(
|
|
4555
|
+
width: clamp(64px, 10vw, 76px);
|
|
4556
|
+
height: clamp(64px, 10vw, 76px);
|
|
4365
4557
|
background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
|
|
4366
4558
|
border: 0;
|
|
4367
|
-
box-shadow: 0
|
|
4559
|
+
box-shadow: 0 4px 16px var(--uvf-accent-1-20);
|
|
4368
4560
|
}
|
|
4369
4561
|
|
|
4370
4562
|
.uvf-center-play-btn:hover {
|
|
4371
|
-
transform: scale(1.
|
|
4563
|
+
transform: scale(1.08);
|
|
4372
4564
|
filter: saturate(1.08) brightness(1.05);
|
|
4373
|
-
box-shadow: 0
|
|
4565
|
+
box-shadow: 0 6px 20px var(--uvf-accent-1-20);
|
|
4374
4566
|
}
|
|
4375
4567
|
|
|
4376
4568
|
.uvf-center-play-btn svg {
|
|
4377
|
-
width: clamp(
|
|
4378
|
-
height: clamp(
|
|
4379
|
-
margin-left:
|
|
4569
|
+
width: clamp(28px, 4.5vw, 32px);
|
|
4570
|
+
height: clamp(28px, 4.5vw, 32px);
|
|
4571
|
+
margin-left: 3px;
|
|
4380
4572
|
}
|
|
4381
4573
|
|
|
4382
4574
|
.uvf-video-title {
|