Files
streamyfin/components/video-player/controls/TimeDisplay.tsx
Lance Chant 0fea901133 fix(player): resume replay, TV surface recovery, and finite time guards
ExoPlayer:
   - Pass the resume position into setMediaSource() instead of prepare()-ing
     from 0 and then seekTo()-ing, which replayed the first few seconds before
     jumping to the resume point.
   - Drop the redundant initial seek that mirrors the resume position (fired by
     the JS direct-player layer once tracks are ready). ExoPlayer re-buffers on
     every seek — even a no-op to the current position — so it stuttered startup.

   MPV (Android TV):
   - Recover the video pipeline when returning from the screensaver / app
     background while paused. TV uses zero-copy hwdec=mediacodec, which binds
     MediaCodec directly to the display surface; when the screensaver invalidates
     that surface the decoder is left bound to dead buffers and mpv disables the
     video track. Register TV-only activity-lifecycle callbacks and reload at the
     cached position on resume to recreate the decoder against the live surface
     (Android counterpart to iOS's performDecoderReset()).

   Video time controls:
   - Initialize remainingTime to 0 instead of Infinity and guard non-finite
     values, so the first paint shows "0:00" (and "—" for the end time) rather
     than "Infinityh NaNm NaNs" and an Invalid Date before the first progress
     update

Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
2026-07-14 10:55:10 +02:00

50 lines
1.4 KiB
TypeScript

import type { FC } from "react";
import { useTranslation } from "react-i18next";
import { View } from "react-native";
import { Text } from "@/components/common/Text";
import { formatTimeString } from "@/utils/time";
interface TimeDisplayProps {
currentTime: number;
remainingTime: number;
}
/**
* Displays current time and remaining time.
* MPV player uses milliseconds for time values.
*/
export const TimeDisplay: FC<TimeDisplayProps> = ({
currentTime,
remainingTime,
}) => {
const { t } = useTranslation();
const getFinishTime = () => {
if (!Number.isFinite(remainingTime)) return "—";
const now = new Date();
// remainingTime is in ms
const finishTime = new Date(now.getTime() + remainingTime);
return finishTime.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
};
return (
<View className='flex flex-row items-center justify-between mt-2'>
<Text className='text-[12px] text-neutral-400'>
{formatTimeString(currentTime, "ms")}
</Text>
<View className='flex flex-col items-end'>
<Text className='text-[12px] text-neutral-400'>
-{formatTimeString(remainingTime, "ms")}
</Text>
<Text className='text-[10px] text-neutral-500 opacity-70'>
{t("player.ends_at", { time: getFinishTime() })}
</Text>
</View>
</View>
);
};