unified-video-framework 1.4.409 → 1.4.410
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.
|
@@ -169,6 +169,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
169
169
|
|
|
170
170
|
// Live stream detection
|
|
171
171
|
private lastDuration: number = 0; // Track duration changes to detect live streams
|
|
172
|
+
private isDetectedAsLive: boolean = false; // Once detected as live, stays live
|
|
172
173
|
|
|
173
174
|
// Debug logging helper
|
|
174
175
|
private debugLog(message: string, ...args: any[]): void {
|
|
@@ -539,6 +540,21 @@ export class WebPlayer extends BasePlayer {
|
|
|
539
540
|
}
|
|
540
541
|
|
|
541
542
|
this.setBuffering(false);
|
|
543
|
+
|
|
544
|
+
// Apply startTime if configured and not yet applied
|
|
545
|
+
// Done here (at canplay) instead of loadedmetadata for faster loading
|
|
546
|
+
// because video has buffered enough data to play from this position
|
|
547
|
+
if (this.config.startTime !== undefined &&
|
|
548
|
+
this.config.startTime > 0 &&
|
|
549
|
+
!this.hasAppliedStartTime &&
|
|
550
|
+
this.video &&
|
|
551
|
+
this.video.duration > 0) {
|
|
552
|
+
const startTime = Math.min(this.config.startTime, this.video.duration);
|
|
553
|
+
this.debugLog(`⏩ Applying startTime at canplay: ${startTime}s (continue watching)`);
|
|
554
|
+
this.video.currentTime = startTime;
|
|
555
|
+
this.hasAppliedStartTime = true;
|
|
556
|
+
}
|
|
557
|
+
|
|
542
558
|
this.emit('onReady');
|
|
543
559
|
|
|
544
560
|
// Update time display when video is ready to play
|
|
@@ -579,16 +595,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
579
595
|
// Update time display immediately when metadata loads
|
|
580
596
|
this.updateTimeDisplay();
|
|
581
597
|
|
|
582
|
-
//
|
|
583
|
-
|
|
584
|
-
this.config.startTime > 0 &&
|
|
585
|
-
!this.hasAppliedStartTime &&
|
|
586
|
-
this.video.duration > 0) {
|
|
587
|
-
const startTime = Math.min(this.config.startTime, this.video.duration);
|
|
588
|
-
this.debugLog(`⏩ Seeking to startTime: ${startTime}s`);
|
|
589
|
-
this.seek(startTime);
|
|
590
|
-
this.hasAppliedStartTime = true;
|
|
591
|
-
}
|
|
598
|
+
// Note: startTime is now applied at 'canplay' event for faster loading
|
|
599
|
+
// (moved from here to avoid premature seeking before buffering)
|
|
592
600
|
|
|
593
601
|
this.emit('onLoadedMetadata', {
|
|
594
602
|
duration: this.video.duration || 0,
|
|
@@ -992,12 +1000,22 @@ export class WebPlayer extends BasePlayer {
|
|
|
992
1000
|
}
|
|
993
1001
|
|
|
994
1002
|
if (window.Hls.isSupported()) {
|
|
995
|
-
|
|
1003
|
+
// Prepare HLS config with startPosition for faster continue watching
|
|
1004
|
+
const hlsConfig: any = {
|
|
996
1005
|
debug: this.config.debug,
|
|
997
1006
|
enableWorker: true,
|
|
998
1007
|
lowLatencyMode: false,
|
|
999
1008
|
backBufferLength: 90
|
|
1000
|
-
}
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
// Set startPosition for HLS to begin loading from the correct fragment
|
|
1012
|
+
// This avoids downloading from beginning and seeking later
|
|
1013
|
+
if (this.config.startTime !== undefined && this.config.startTime > 0) {
|
|
1014
|
+
hlsConfig.startPosition = this.config.startTime;
|
|
1015
|
+
this.debugLog(`⏩ HLS startPosition set to: ${this.config.startTime}s (continue watching)`);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
this.hls = new window.Hls(hlsConfig);
|
|
1001
1019
|
|
|
1002
1020
|
this.hls.loadSource(url);
|
|
1003
1021
|
this.hls.attachMedia(this.video);
|
|
@@ -1077,6 +1095,15 @@ export class WebPlayer extends BasePlayer {
|
|
|
1077
1095
|
}
|
|
1078
1096
|
|
|
1079
1097
|
this.dash = window.dashjs.MediaPlayer().create();
|
|
1098
|
+
|
|
1099
|
+
// Set initial seek time for DASH if startTime is configured (continue watching)
|
|
1100
|
+
// This allows DASH to start downloading from the correct segment
|
|
1101
|
+
if (this.config.startTime !== undefined && this.config.startTime > 0) {
|
|
1102
|
+
this.debugLog(`⏩ DASH will seek to: ${this.config.startTime}s on ready (continue watching)`);
|
|
1103
|
+
// Note: DASH doesn't support startPosition in config like HLS
|
|
1104
|
+
// The seek will be applied at canplay event when stream is ready
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1080
1107
|
this.dash.initialize(this.video, url, this.config.autoPlay);
|
|
1081
1108
|
|
|
1082
1109
|
// Configure DASH settings
|
|
@@ -4598,31 +4625,35 @@ export class WebPlayer extends BasePlayer {
|
|
|
4598
4625
|
}
|
|
4599
4626
|
|
|
4600
4627
|
.uvf-live-dot {
|
|
4601
|
-
width:
|
|
4602
|
-
height:
|
|
4628
|
+
width: 10px;
|
|
4629
|
+
height: 10px;
|
|
4603
4630
|
background: #ff0000;
|
|
4604
4631
|
border-radius: 50%;
|
|
4605
4632
|
display: inline-block;
|
|
4606
|
-
animation: uvf-live-pulse
|
|
4607
|
-
box-shadow: 0 0
|
|
4633
|
+
animation: uvf-live-pulse 1.5s ease-in-out infinite;
|
|
4634
|
+
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.7);
|
|
4608
4635
|
}
|
|
4609
4636
|
|
|
4610
4637
|
.uvf-live-text {
|
|
4611
|
-
color: #
|
|
4638
|
+
color: #ff0000;
|
|
4612
4639
|
font-size: 14px;
|
|
4613
4640
|
font-weight: 700;
|
|
4614
|
-
letter-spacing:
|
|
4615
|
-
text-shadow: 0
|
|
4641
|
+
letter-spacing: 1px;
|
|
4642
|
+
text-shadow: 0 0 10px rgba(255, 0, 0, 0.5);
|
|
4616
4643
|
}
|
|
4617
4644
|
|
|
4618
4645
|
@keyframes uvf-live-pulse {
|
|
4619
|
-
0
|
|
4620
|
-
|
|
4646
|
+
0% {
|
|
4647
|
+
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.7);
|
|
4621
4648
|
transform: scale(1);
|
|
4622
4649
|
}
|
|
4623
4650
|
50% {
|
|
4624
|
-
|
|
4625
|
-
transform: scale(1.
|
|
4651
|
+
box-shadow: 0 0 0 8px rgba(255, 0, 0, 0);
|
|
4652
|
+
transform: scale(1.2);
|
|
4653
|
+
}
|
|
4654
|
+
100% {
|
|
4655
|
+
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0);
|
|
4656
|
+
transform: scale(1);
|
|
4626
4657
|
}
|
|
4627
4658
|
}
|
|
4628
4659
|
|
|
@@ -8554,23 +8585,29 @@ export class WebPlayer extends BasePlayer {
|
|
|
8554
8585
|
|
|
8555
8586
|
// Detect if duration is increasing (live stream with DVR)
|
|
8556
8587
|
const isDurationIncreasing = duration > this.lastDuration && this.lastDuration > 0 && duration > 60;
|
|
8588
|
+
|
|
8589
|
+
// Once we detect duration is increasing, remember it's a live stream
|
|
8590
|
+
if (isDurationIncreasing) {
|
|
8591
|
+
this.isDetectedAsLive = true;
|
|
8592
|
+
}
|
|
8593
|
+
|
|
8557
8594
|
this.lastDuration = duration;
|
|
8558
8595
|
|
|
8559
8596
|
// Check if this is a live stream
|
|
8560
8597
|
// 1. Manual override via isLive config
|
|
8561
8598
|
// 2. Duration = Infinity (pure live)
|
|
8562
|
-
// 3.
|
|
8599
|
+
// 3. Previously detected as live (duration increased at some point)
|
|
8563
8600
|
// 4. Near live edge: duration - currentTime < 15 seconds (live with DVR)
|
|
8564
8601
|
const isLiveStream = this.config.isLive === true ||
|
|
8565
8602
|
!isFinite(duration) ||
|
|
8566
|
-
|
|
8603
|
+
this.isDetectedAsLive ||
|
|
8567
8604
|
(duration > 0 && (duration - current) < 15);
|
|
8568
8605
|
|
|
8569
8606
|
if (isLiveStream) {
|
|
8570
8607
|
// Show LIVE indicator for live streams
|
|
8571
8608
|
timeDisplay.innerHTML = '<span class="uvf-live-dot"></span><span class="uvf-live-text">LIVE</span>';
|
|
8572
8609
|
timeDisplay.classList.add('uvf-is-live');
|
|
8573
|
-
this.debugLog('Time display: LIVE stream detected (isLive=' + this.config.isLive + ',
|
|
8610
|
+
this.debugLog('Time display: LIVE stream detected (isLive=' + this.config.isLive + ', wasDetected=' + this.isDetectedAsLive + ', duration=' + duration + ')');
|
|
8574
8611
|
} else {
|
|
8575
8612
|
// Show normal time display for VOD
|
|
8576
8613
|
const currentFormatted = this.formatTime(current);
|