diff --git a/src/video/components/actions/TimeAction.tsx b/src/video/components/actions/TimeAction.tsx index d355ba2d..d71d345c 100644 --- a/src/video/components/actions/TimeAction.tsx +++ b/src/video/components/actions/TimeAction.tsx @@ -3,6 +3,9 @@ import { useTranslation } from "react-i18next"; import { useMediaPlaying } from "@/video/state/logic/mediaplaying"; import { useProgress } from "@/video/state/logic/progress"; import { useInterface } from "@/video/state/logic/interface"; +import { VideoPlayerTimeFormat } from "@/video/state/types"; +import { useIsMobile } from "@/hooks/useIsMobile"; +import { useControls } from "@/video/state/logic/controls"; function durationExceedsHour(secs: number): boolean { return secs > 60 * 60; @@ -40,19 +43,20 @@ export function TimeAction(props: Props) { const videoTime = useProgress(descriptor); const mediaPlaying = useMediaPlaying(descriptor); const { timeFormat, setTimeFormat } = useInterface(descriptor); + const { isMobile } = useIsMobile(); const { t } = useTranslation(); const hasHours = durationExceedsHour(videoTime.duration); - const time = formatSeconds( + + const currentTime = formatSeconds( mediaPlaying.isDragSeeking ? videoTime.draggingTime : videoTime.time, hasHours ); const duration = formatSeconds(videoTime.duration, hasHours); - const timeLeft = formatSeconds( - (videoTime.duration - videoTime.time) / mediaPlaying.playbackSpeed + (videoTime.duration - videoTime.time) / mediaPlaying.playbackSpeed, + hasHours ); - const timeFinished = new Date( new Date().getTime() + (videoTime.duration * 1000) / mediaPlaying.playbackSpeed @@ -61,15 +65,38 @@ export function TimeAction(props: Props) { minute: "numeric", hour12: true, }); + const formattedTimeFinished = ` - ${t("videoPlayer.finishAt", { + timeFinished, + })}`; + + let formattedTime: string; + + if (timeFormat === VideoPlayerTimeFormat.REGULAR) { + formattedTime = `${currentTime} ${props.noDuration ? "" : `/ ${duration}`}`; + } else if (timeFormat === VideoPlayerTimeFormat.REMAINING) { + formattedTime = `${t("videoPlayer.timeLeft", { + timeLeft, + })}${ + videoTime.time === videoTime.duration || isMobile + ? "" + : formattedTimeFinished + } `; + } else { + formattedTime = ""; + } return ( diff --git a/src/video/state/logic/controls.ts b/src/video/state/logic/controls.ts index 535bd146..ccd83add 100644 --- a/src/video/state/logic/controls.ts +++ b/src/video/state/logic/controls.ts @@ -1,7 +1,7 @@ import { updateInterface } from "@/video/state/logic/interface"; import { updateMeta } from "@/video/state/logic/meta"; import { updateProgress } from "@/video/state/logic/progress"; -import { VideoPlayerMeta } from "@/video/state/types"; +import { VideoPlayerMeta, VideoPlayerTimeFormat } from "@/video/state/types"; import { getPlayerState } from "../cache"; import { VideoPlayerStateController } from "../providers/providerTypes"; @@ -15,7 +15,7 @@ export type ControlMethods = { setDraggingTime(num: number): void; togglePictureInPicture(): void; setPlaybackSpeed(num: number): void; - setTimeFormat(num: 0 | 1): void; + setTimeFormat(num: VideoPlayerTimeFormat): void; }; export function useControls( diff --git a/src/video/state/logic/interface.ts b/src/video/state/logic/interface.ts index 8e761e4e..5a5ea719 100644 --- a/src/video/state/logic/interface.ts +++ b/src/video/state/logic/interface.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { getPlayerState } from "../cache"; import { listenEvent, sendEvent, unlistenEvent } from "../events"; -import { VideoPlayerState } from "../types"; +import { VideoPlayerState, VideoPlayerTimeFormat } from "../types"; export type VideoInterfaceEvent = { popout: string | null; @@ -9,8 +9,8 @@ export type VideoInterfaceEvent = { isFocused: boolean; isFullscreen: boolean; popoutBounds: null | DOMRect; - timeFormat: 0 | 1 | 2; - setTimeFormat(timeFormat: 0 | 1 | 2): void; + timeFormat: VideoPlayerTimeFormat; + setTimeFormat(timeFormat: VideoPlayerTimeFormat): void; }; function getInterfaceFromState(state: VideoPlayerState): VideoInterfaceEvent { @@ -21,7 +21,7 @@ function getInterfaceFromState(state: VideoPlayerState): VideoInterfaceEvent { isFullscreen: state.interface.isFullscreen, popoutBounds: state.interface.popoutBounds, timeFormat: state.interface.timeFormat, - setTimeFormat(timeFormat: 0 | 1 | 2) { + setTimeFormat(timeFormat: VideoPlayerTimeFormat) { state.stateProvider?.setTimeFormat(timeFormat); }, }; diff --git a/src/video/state/providers/providerTypes.ts b/src/video/state/providers/providerTypes.ts index 6b3d7391..2cab148b 100644 --- a/src/video/state/providers/providerTypes.ts +++ b/src/video/state/providers/providerTypes.ts @@ -1,4 +1,5 @@ import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams"; +import { VideoPlayerTimeFormat } from "@/video/state/types"; type VideoPlayerSource = { source: string; @@ -23,7 +24,7 @@ export type VideoPlayerStateController = { getId(): string; togglePictureInPicture(): void; setPlaybackSpeed(num: number): void; - setTimeFormat(format: 0 | 1 | 2): void; + setTimeFormat(timeFormat: VideoPlayerTimeFormat): void; }; export type VideoPlayerStateProvider = VideoPlayerStateController & { diff --git a/src/video/state/types.ts b/src/video/state/types.ts index 3a482332..e5e403da 100644 --- a/src/video/state/types.ts +++ b/src/video/state/types.ts @@ -22,6 +22,11 @@ export type VideoPlayerMeta = { }[]; }; +export enum VideoPlayerTimeFormat { + REGULAR = 0, + REMAINING = 1, +} + export type VideoPlayerState = { // state related to the user interface interface: { @@ -30,7 +35,7 @@ export type VideoPlayerState = { isFocused: boolean; // is the video player the users focus? (shortcuts only works when its focused) leftControlHovering: boolean; // is the cursor hovered over the left side of player controls popoutBounds: null | DOMRect; // bounding box of current popout - timeFormat: 0 | 1 | 2; // Time format of the video player + timeFormat: VideoPlayerTimeFormat; // Time format of the video player }; // state related to the playing state of the media