diff --git a/src/setup/locales/en/translation.json b/src/setup/locales/en/translation.json index f7eb8b5d..f0cf677c 100644 --- a/src/setup/locales/en/translation.json +++ b/src/setup/locales/en/translation.json @@ -57,6 +57,8 @@ "backToHome": "Back to home", "backToHomeShort": "Back", "seasonAndEpisode": "S{{season}} E{{episode}}", + "timeLeft": "{{timeLeft}} left", + "finishAt": "Finish at {{timeFinished}}", "buttons": { "episodes": "Episodes", "source": "Source", diff --git a/src/setup/locales/fr/translation.json b/src/setup/locales/fr/translation.json index e5c669ce..fe9d73eb 100644 --- a/src/setup/locales/fr/translation.json +++ b/src/setup/locales/fr/translation.json @@ -39,13 +39,16 @@ "backToHome": "Retour à la page d'accueil", "backToHomeShort": "Retour", "seasonAndEpisode": "S{{season}} E{{episode}}", + "timeLeft": "{{timeLeft}} restant", + "finishAt": "Terminer à {{timeFinished}}", "buttons": { "episodes": "Épisodes", "source": "Source", "captions": "Sous-titres", "download": "Télécharger", "settings": "Paramètres", - "pictureInPicture": "Image dans l'image" + "pictureInPicture": "Image dans l'image", + "playbackSpeed": "Vitesse" }, "popouts": { "sources": "Sources", diff --git a/src/video/components/actions/TimeAction.tsx b/src/video/components/actions/TimeAction.tsx index 493f6d26..bab9e101 100644 --- a/src/video/components/actions/TimeAction.tsx +++ b/src/video/components/actions/TimeAction.tsx @@ -1,6 +1,11 @@ import { useVideoPlayerDescriptor } from "@/video/state/hooks"; +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; @@ -37,19 +42,71 @@ export function TimeAction(props: Props) { const descriptor = useVideoPlayerDescriptor(); const videoTime = useProgress(descriptor); const mediaPlaying = useMediaPlaying(descriptor); + const { setTimeFormat } = useControls(descriptor); + const { timeFormat } = 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, + hasHours + ); + const timeFinished = new Date( + new Date().getTime() + + (videoTime.duration * 1000) / mediaPlaying.playbackSpeed + ).toLocaleTimeString("en-US", { + hour: "numeric", + 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 && !isMobile) { + formattedTime = `${t("videoPlayer.timeLeft", { + timeLeft, + })}${videoTime.time === videoTime.duration ? "" : formattedTimeFinished} `; + } else if (timeFormat === VideoPlayerTimeFormat.REMAINING && isMobile) { + formattedTime = `-${timeLeft}`; + } else { + formattedTime = ""; + } return ( -
- {time} {props.noDuration ? "" : `/ ${duration}`} -
-