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
- // Apply startTime if configured and not yet applied
583
- if (this.config.startTime !== undefined &&
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
- this.hls = new window.Hls({
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: 8px;
4602
- height: 8px;
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 2s ease-in-out infinite;
4607
- box-shadow: 0 0 8px rgba(255, 0, 0, 0.8);
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: #ffffff;
4638
+ color: #ff0000;
4612
4639
  font-size: 14px;
4613
4640
  font-weight: 700;
4614
- letter-spacing: 0.5px;
4615
- text-shadow: 0 1px 3px rgba(0,0,0,0.8);
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%, 100% {
4620
- opacity: 1;
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
- opacity: 0.7;
4625
- transform: scale(1.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. Duration is actively increasing (live with DVR)
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
- isDurationIncreasing ||
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 + ', increasing=' + isDurationIncreasing + ', duration=' + duration + ', current=' + current + ')');
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);