From da1aa9f48cd7c59da9db500ba4edfca880d15b0d Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Tue, 10 Dec 2024 20:37:58 +0100 Subject: [PATCH] feat: go to next episode countdown --- components/Button.tsx | 3 +- components/video-player/controls/Controls.tsx | 44 +++++----- .../controls/NextEpisodeCountDownButton.tsx | 81 +++++++++++++++++++ 3 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 components/video-player/controls/NextEpisodeCountDownButton.tsx diff --git a/components/Button.tsx b/components/Button.tsx index 305312d4..1498a975 100644 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -3,7 +3,8 @@ import React, { PropsWithChildren, ReactNode, useMemo } from "react"; import { Text, TouchableOpacity, View } from "react-native"; import { Loader } from "./Loader"; -interface ButtonProps extends React.ComponentProps { +export interface ButtonProps + extends React.ComponentProps { onPress?: () => void; className?: string; textClassName?: string; diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index 4bad27f0..4304248d 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -8,8 +8,10 @@ import { TrackInfo, VlcPlayerViewRef, } from "@/modules/vlc-player/src/VlcPlayer.types"; +import { apiAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings"; +import { getItemById } from "@/utils/jellyfin/user-library/getItemById"; import { writeToLog } from "@/utils/log"; import { formatTimeString, @@ -23,8 +25,11 @@ import { BaseItemDto, MediaSourceInfo, } from "@jellyfin/sdk/lib/generated-client"; +import * as Haptics from "expo-haptics"; import { Image } from "expo-image"; import { useLocalSearchParams, useRouter } from "expo-router"; +import { useAtom } from "jotai"; +import { debounce } from "lodash"; import { useCallback, useEffect, useRef, useState } from "react"; import { Dimensions, Pressable, TouchableOpacity, View } from "react-native"; import { Slider } from "react-native-awesome-slider"; @@ -36,19 +41,15 @@ import { } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { VideoRef } from "react-native-video"; +import AudioSlider from "./AudioSlider"; +import BrightnessSlider from "./BrightnessSlider"; import { ControlProvider } from "./contexts/ControlContext"; import { VideoProvider } from "./contexts/VideoContext"; -import * as Haptics from "expo-haptics"; import DropdownViewDirect from "./dropdown/DropdownViewDirect"; import DropdownViewTranscoding from "./dropdown/DropdownViewTranscoding"; -import BrightnessSlider from "./BrightnessSlider"; -import SkipButton from "./SkipButton"; -import { debounce } from "lodash"; import { EpisodeList } from "./EpisodeList"; -import { getItemById } from "@/utils/jellyfin/user-library/getItemById"; -import { useAtom } from "jotai"; -import { apiAtom } from "@/providers/JellyfinProvider"; -import AudioSlider from "./AudioSlider"; +import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton"; +import SkipButton from "./SkipButton"; interface Props { item: BaseItemDto; @@ -120,7 +121,7 @@ export const Controls: React.FC = ({ } = useTrickplay(item, !offline && enableTrickplay); const [currentTime, setCurrentTime] = useState(0); - const [remainingTime, setRemainingTime] = useState(0); + const [remainingTime, setRemainingTime] = useState(Infinity); const min = useSharedValue(0); const max = useSharedValue(item.RunTimeTicks || 0); @@ -209,15 +210,10 @@ export const Controls: React.FC = ({ ? maxValue - currentProgress : ticksToSeconds(maxValue - currentProgress); + console.log("remaining: ", remaining); + setCurrentTime(current); setRemainingTime(remaining); - - // Currently doesm't work in VLC because of some corrupted timestamps, will need to find a workaround. - if (currentProgress === maxValue) { - setShowControls(true); - // Automatically play the next item if it exists - goToNextItem(); - } }, [goToNextItem, isVlc] ); @@ -229,7 +225,6 @@ export const Controls: React.FC = ({ isSeeking: isSeeking.value, }), (result) => { - // console.log("Progress changed", result); if (result.isSeeking === false) { runOnJS(updateTimes)(result.progress, result.max); } @@ -290,7 +285,6 @@ export const Controls: React.FC = ({ const handleSliderChange = useCallback( debounce((value: number) => { const progressInTicks = isVlc ? msToTicks(value) : value; - console.log("Progress in ticks", progressInTicks); calculateTrickplayUrl(progressInTicks); const progressInSeconds = Math.floor(ticksToSeconds(progressInTicks)); const hours = Math.floor(progressInSeconds / 3600); @@ -663,10 +657,8 @@ export const Controls: React.FC = ({ right: 0, left: 0, bottom: 0, - opacity: showControls ? 1 : 0, }, ]} - pointerEvents={showControls ? "box-none" : "none"} className={`flex flex-col p-4`} > = ({ style={{ flexDirection: "column", alignSelf: "flex-end", // Shrink height based on content + opacity: showControls ? 1 : 0, }} + pointerEvents={showControls ? "box-none" : "none"} > {item?.Name} {item?.Type === "Episode" && ( @@ -699,7 +693,6 @@ export const Controls: React.FC = ({ style={{ flexDirection: "column", alignSelf: "flex-end", - marginRight: insets.right, }} > = ({ onPress={skipCredit} buttonText="Skip Credits" /> + void; + onPress?: () => void; + show: boolean; +} + +const NextEpisodeCountDownButton: React.FC = ({ + onFinish, + onPress, + show, + ...props +}) => { + const progress = useSharedValue(0); + + useEffect(() => { + if (show) { + progress.value = 0; + progress.value = withTiming( + 1, + { + duration: 10000, // 10 seconds + easing: Easing.linear, + }, + (finished) => { + if (finished && onFinish) { + console.log("finish"); + runOnJS(onFinish)(); + } + } + ); + } + }, [show, onFinish]); + + const animatedStyle = useAnimatedStyle(() => { + return { + position: "absolute", + left: 0, + top: 0, + bottom: 0, + width: `${progress.value * 100}%`, + backgroundColor: Colors.primary, + }; + }); + + const handlePress = () => { + if (onPress) { + onPress(); + } + }; + + if (!show) { + return null; + } + + return ( + + + + Next Episode + + + ); +}; + +export default NextEpisodeCountDownButton;