diff --git a/packages/video-player/package.json b/packages/video-player/package.json index 5ba57e2..13ea69c 100644 --- a/packages/video-player/package.json +++ b/packages/video-player/package.json @@ -1,6 +1,6 @@ { "name": "@hublib-web/video-player", - "version": "0.1.2", + "version": "0.1.3", "description": "Cross-framework video player package for React and Angular", "license": "MIT", "type": "module", diff --git a/packages/video-player/src/core/format-time.ts b/packages/video-player/src/core/format-time.ts index 8b94323..1e2a5ea 100644 --- a/packages/video-player/src/core/format-time.ts +++ b/packages/video-player/src/core/format-time.ts @@ -1,10 +1,12 @@ export const formatTime = (seconds: number) => { + const safeSeconds = + Number.isFinite(seconds) && seconds > 0 ? seconds : 0; const pad = (num: number) => String(num).padStart(2, "0"); - const hrs = Math.floor(seconds / 3600); - const mins = Math.floor((seconds % 3600) / 60); - const secs = Math.floor(seconds % 60); + const hrs = Math.floor(safeSeconds / 3600); + const mins = Math.floor((safeSeconds % 3600) / 60); + const secs = Math.floor(safeSeconds % 60); - if (seconds < 3600) { + if (safeSeconds < 3600) { return `${pad(mins)}:${pad(secs)}`; } diff --git a/packages/video-player/src/core/player-runtime.ts b/packages/video-player/src/core/player-runtime.ts index ee31bfb..63828e4 100644 --- a/packages/video-player/src/core/player-runtime.ts +++ b/packages/video-player/src/core/player-runtime.ts @@ -718,6 +718,31 @@ export class VideoPlayerRuntime { }; } + private syncLiveUi( + player: VideoPlayerRuntimePlayer, + video: HTMLVideoElement, + isLive: boolean, + ) { + const wrapper = video.parentElement; + + if (isLive) { + wrapper?.classList.add("vjs-hls-live", "vjs-live"); + player.duration(Infinity); + if (player.liveTracker) { + player.liveTracker.isLive_ = true; + player.liveTracker.startTracking(); + player.liveTracker.trigger("durationchange"); + } + return; + } + + wrapper?.classList.remove("vjs-hls-live", "vjs-live"); + if (player.liveTracker) { + player.liveTracker.isLive_ = false; + player.liveTracker.trigger("durationchange"); + } + } + private async loadHlsSource() { const options = this.options; const player = this.playerRef; @@ -769,14 +794,8 @@ export class VideoPlayerRuntime { live: details?.live, }); - if (details?.live) { - video.parentElement?.classList.add("vjs-hls-live", "vjs-live"); - player.duration(Infinity); - if (player.liveTracker) { - player.liveTracker.isLive_ = true; - player.liveTracker.startTracking(); - player.liveTracker.trigger("durationchange"); - } + if (typeof details?.live === "boolean") { + this.syncLiveUi(player, video, details.live); } if (options.initialTime > 0) { @@ -784,6 +803,22 @@ export class VideoPlayerRuntime { } }); + hls.on(Hls.Events.LEVEL_LOADED, (_event, data) => { + const details = data?.details; + if (!details) { + return; + } + + this.emit("manifestloaded", { + engine: "hls", + duration: details.totalduration, + live: details.live, + }); + if (typeof details.live === "boolean") { + this.syncLiveUi(player, video, details.live); + } + }); + hls.on(Hls.Events.FRAG_CHANGED, () => { if (player.liveTracker) { player.liveTracker.atLiveEdge = isAtLiveEdge; @@ -1034,10 +1069,8 @@ export class VideoPlayerRuntime { const videoElement = this.playerRef .el() ?.querySelector("video") as HTMLVideoElement | null; - videoElement?.parentElement?.classList.remove("vjs-hls-live", "vjs-live"); - if (this.playerRef.liveTracker) { - this.playerRef.liveTracker.isLive_ = false; - this.playerRef.liveTracker.trigger("durationchange"); + if (videoElement) { + this.syncLiveUi(this.playerRef, videoElement, false); } } } diff --git a/packages/video-player/src/react/video-player/components/video-js/utils.ts b/packages/video-player/src/react/video-player/components/video-js/utils.ts index ad25ee5..acc0db2 100644 --- a/packages/video-player/src/react/video-player/components/video-js/utils.ts +++ b/packages/video-player/src/react/video-player/components/video-js/utils.ts @@ -1,9 +1,11 @@ export const formatTime = (seconds: number) => { + const safeSeconds = + Number.isFinite(seconds) && seconds > 0 ? seconds : 0; const pad = (num: number) => String(num).padStart(2, "0"); - const hrs = Math.floor(seconds / 3600); - const mins = Math.floor((seconds % 3600) / 60); - const secs = Math.floor(seconds % 60); - if (seconds < 3600) { + const hrs = Math.floor(safeSeconds / 3600); + const mins = Math.floor((safeSeconds % 3600) / 60); + const secs = Math.floor(safeSeconds % 60); + if (safeSeconds < 3600) { return `${pad(mins)}:${pad(secs)}`; } return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`;