fix: refactor

This commit is contained in:
Fredrik Burmester
2024-08-16 19:06:06 +02:00
parent 83131cabe1
commit 349fc1f21e
4 changed files with 195 additions and 190 deletions

View File

@@ -28,6 +28,7 @@ import { chromecastProfile } from "@/utils/profiles/chromecast";
import ios12 from "@/utils/profiles/ios12";
import {
currentlyPlayingItemAtom,
playingAtom,
triggerPlayAtom,
} from "@/components/CurrentlyPlayingBar";
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
@@ -47,6 +48,10 @@ const page: React.FC = () => {
const castDevice = useCastDevice();
const [, setCurrentlyPlying] = useAtom(currentlyPlayingItemAtom);
const [, setPlaying] = useAtom(playingAtom);
const client = useRemoteMediaClient();
const chromecastReady = useMemo(() => !!castDevice?.deviceId, [castDevice]);
const [selectedAudioStream, setSelectedAudioStream] = useState<number>(-1);
const [selectedSubtitleStream, setSelectedSubtitleStream] =
@@ -68,22 +73,6 @@ const page: React.FC = () => {
staleTime: 60,
});
const backdropUrl = useMemo(
() =>
getBackdropUrl({
api,
item,
quality: 90,
width: 1000,
}),
[item],
);
const logoUrl = useMemo(
() => (item?.Type === "Movie" ? getLogoImageUrlById({ api, item }) : null),
[item],
);
const { data: sessionData } = useQuery({
queryKey: ["sessionData", item?.Id],
queryFn: async () => {
@@ -131,10 +120,6 @@ const page: React.FC = () => {
staleTime: 0,
});
const [, setCp] = useAtom(currentlyPlayingItemAtom);
const client = useRemoteMediaClient();
const [, setPlayTrigger] = useAtom(triggerPlayAtom);
const onPressPlay = useCallback(
async (type: "device" | "cast" = "device") => {
if (!playbackUrl || !item) return;
@@ -159,18 +144,33 @@ const page: React.FC = () => {
}
});
} else {
setCp({
setCurrentlyPlying({
item,
playbackUrl,
});
// Use this trigger to initiate playback in another component (CurrentlyPlayingBar)
setPlayTrigger((prev) => prev + 1);
setPlaying(true);
}
},
[playbackUrl, item],
);
const backdropUrl = useMemo(
() =>
getBackdropUrl({
api,
item,
quality: 90,
width: 1000,
}),
[item],
);
const logoUrl = useMemo(
() => (item?.Type === "Movie" ? getLogoImageUrlById({ api, item }) : null),
[item],
);
if (l1)
return (
<View className="justify-center items-center h-full">

View File

@@ -15,7 +15,6 @@ import { useJobProcessor } from "@/utils/atoms/queue";
import { JobQueueProvider } from "@/providers/JobQueueProvider";
import { useKeepAwake } from "expo-keep-awake";
import { useSettings } from "@/utils/atoms/settings";
import { StreamProvider } from "@/providers/StreamProvider";
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
@@ -79,101 +78,99 @@ function Layout() {
<JobQueueProvider>
<ActionSheetProvider>
<JellyfinProvider>
<StreamProvider>
<StatusBar style="light" backgroundColor="#000" />
<ThemeProvider value={DarkTheme}>
<Stack initialRouteName="/home">
<Stack.Screen
name="(auth)/(tabs)"
options={{
headerShown: false,
title: "",
}}
/>
<Stack.Screen
name="(auth)/settings"
options={{
headerShown: true,
title: "Settings",
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/downloads"
options={{
headerShown: true,
title: "Downloads",
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/items/[id]"
options={{
title: "",
headerShown: false,
}}
/>
<Stack.Screen
name="(auth)/collections/[collectionId]"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/artists/page"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/artists/[artistId]/page"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/albums/[albumId]"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/songs/[songId]"
options={{
title: "",
headerShown: false,
}}
/>
<Stack.Screen
name="(auth)/series/[id]"
options={{
title: "",
headerShown: false,
}}
/>
<Stack.Screen
name="login"
options={{ headerShown: false, title: "Login" }}
/>
<Stack.Screen name="+not-found" />
</Stack>
<CurrentlyPlayingBar />
</ThemeProvider>
</StreamProvider>
<StatusBar style="light" backgroundColor="#000" />
<ThemeProvider value={DarkTheme}>
<Stack initialRouteName="/home">
<Stack.Screen
name="(auth)/(tabs)"
options={{
headerShown: false,
title: "",
}}
/>
<Stack.Screen
name="(auth)/settings"
options={{
headerShown: true,
title: "Settings",
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/downloads"
options={{
headerShown: true,
title: "Downloads",
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/items/[id]"
options={{
title: "",
headerShown: false,
}}
/>
<Stack.Screen
name="(auth)/collections/[collectionId]"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/artists/page"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/artists/[artistId]/page"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/albums/[albumId]"
options={{
title: "",
headerShown: true,
headerStyle: { backgroundColor: "black" },
headerShadowVisible: false,
}}
/>
<Stack.Screen
name="(auth)/songs/[songId]"
options={{
title: "",
headerShown: false,
}}
/>
<Stack.Screen
name="(auth)/series/[id]"
options={{
title: "",
headerShown: false,
}}
/>
<Stack.Screen
name="login"
options={{ headerShown: false, title: "Login" }}
/>
<Stack.Screen name="+not-found" />
</Stack>
<CurrentlyPlayingBar />
</ThemeProvider>
</JellyfinProvider>
</ActionSheetProvider>
</JobQueueProvider>

View File

@@ -44,17 +44,21 @@ export const currentlyPlayingItemAtom = atom<{
export const triggerPlayAtom = atom(0);
export const CurrentlyPlayingBar: React.FC = () => {
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const [cp, setCp] = useAtom(currentlyPlayingItemAtom);
export const playingAtom = atom(false);
export const CurrentlyPlayingBar: React.FC = () => {
const queryClient = useQueryClient();
const segments = useSegments();
const [settings] = useSettings();
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const [playing, setPlaying] = useAtom(playingAtom);
const [currentlyPlaying, setCurrentlyPlaying] = useAtom(
currentlyPlayingItemAtom,
);
const videoRef = useRef<VideoRef | null>(null);
const [paused, setPaused] = useState(true);
const [progress, setProgress] = useState(0);
const [fullScreen, setFullScreen] = useState(false);
@@ -113,81 +117,83 @@ export const CurrentlyPlayingBar: React.FC = () => {
// }, [settings, fullScreen]);
const { data: item } = useQuery({
queryKey: ["item", cp?.item.Id],
queryKey: ["item", currentlyPlaying?.item.Id],
queryFn: async () =>
await getUserItemData({
api,
userId: user?.Id,
itemId: cp?.item.Id,
itemId: currentlyPlaying?.item.Id,
}),
enabled: !!cp?.item.Id && !!api,
enabled: !!currentlyPlaying?.item.Id && !!api,
staleTime: 60,
});
const { data: sessionData } = useQuery({
queryKey: ["sessionData", cp?.item.Id],
queryKey: ["sessionData", currentlyPlaying?.item.Id],
queryFn: async () => {
if (!cp?.item.Id) return null;
if (!currentlyPlaying?.item.Id) return null;
const playbackData = await getMediaInfoApi(api!).getPlaybackInfo({
itemId: cp?.item.Id,
itemId: currentlyPlaying?.item.Id,
userId: user?.Id,
});
return playbackData.data;
},
enabled: !!cp?.item.Id && !!api && !!user?.Id,
enabled: !!currentlyPlaying?.item.Id && !!api && !!user?.Id,
staleTime: 0,
});
const onProgress = useCallback(
({ currentTime }: OnProgressData) => {
if (!currentTime || !sessionData?.PlaySessionId || paused) return;
if (
!currentTime ||
!sessionData?.PlaySessionId ||
!playing ||
!api ||
!currentlyPlaying?.item.Id
)
return;
const newProgress = currentTime * 10000000;
setProgress(newProgress);
reportPlaybackProgress({
api,
itemId: cp?.item.Id,
itemId: currentlyPlaying?.item.Id,
positionTicks: newProgress,
sessionId: sessionData.PlaySessionId,
});
},
[sessionData?.PlaySessionId, item, api, paused]
[sessionData?.PlaySessionId, api, playing, currentlyPlaying?.item.Id],
);
const play = () => {
if (videoRef.current) {
videoRef.current.resume();
setPaused(false);
}
};
useEffect(() => {
if (playing) {
videoRef.current?.resume();
} else {
videoRef.current?.pause();
if (progress > 0 && sessionData?.PlaySessionId && api)
reportPlaybackStopped({
api,
itemId: item?.Id,
positionTicks: progress,
sessionId: sessionData?.PlaySessionId,
});
const pause = useCallback(() => {
videoRef.current?.pause();
setPaused(true);
if (progress > 0)
reportPlaybackStopped({
api,
itemId: item?.Id,
positionTicks: progress,
sessionId: sessionData?.PlaySessionId,
queryClient.invalidateQueries({
queryKey: ["nextUp", item?.SeriesId],
refetchType: "all",
});
queryClient.invalidateQueries({
queryKey: ["nextUp", item?.SeriesId],
refetchType: "all",
});
queryClient.invalidateQueries({
queryKey: ["episodes"],
refetchType: "all",
});
}, [api, item, progress, sessionData, queryClient]);
queryClient.invalidateQueries({
queryKey: ["episodes"],
refetchType: "all",
});
}
}, [playing, progress, item, sessionData]);
const startPosition = useMemo(
() =>
item?.UserData?.PlaybackPositionTicks
? Math.round(item.UserData.PlaybackPositionTicks / 10000)
: 0,
[item]
[item],
);
const backdropUrl = useMemo(
@@ -198,7 +204,7 @@ export const CurrentlyPlayingBar: React.FC = () => {
quality: 70,
width: 200,
}),
[item]
[item],
);
/**
@@ -207,24 +213,24 @@ export const CurrentlyPlayingBar: React.FC = () => {
*
* The trigger playback is triggered from the button component.
*/
useEffect(() => {
if (cp?.playbackUrl) {
play();
if (settings?.openFullScreenVideoPlayerByDefault) {
videoRef.current?.presentFullscreenPlayer();
}
}
}, [cp?.playbackUrl]);
// useEffect(() => {
// if (currentlyPlaying?.playbackUrl) {
// play();
// if (settings?.openFullScreenVideoPlayerByDefault) {
// videoRef.current?.presentFullscreenPlayer();
// }
// }
// }, [currentlyPlaying?.playbackUrl]);
const [triggerPlay] = useAtom(triggerPlayAtom);
useEffect(() => {
play();
if (settings?.openFullScreenVideoPlayerByDefault) {
videoRef.current?.presentFullscreenPlayer();
}
}, [triggerPlay]);
// const [triggerPlay] = useAtom(triggerPlayAtom);
// useEffect(() => {
// setPlaying(true)
// if (settings?.openFullScreenVideoPlayerByDefault) {
// videoRef.current?.presentFullscreenPlayer();
// }
// }, [triggerPlay]);
if (!cp || !api) return null;
if (!currentlyPlaying || !api) return null;
return (
<Animated.View
@@ -254,7 +260,7 @@ export const CurrentlyPlayingBar: React.FC = () => {
${item?.Type === "Audio" ? "aspect-square" : "aspect-video"}
`}
>
{cp.playbackUrl && (
{currentlyPlaying.playbackUrl && (
<Video
ref={videoRef}
allowsExternalPlayback
@@ -274,13 +280,13 @@ export const CurrentlyPlayingBar: React.FC = () => {
enable: true,
thread: true,
}}
paused={paused}
paused={!playing}
onProgress={(e) => onProgress(e)}
subtitleStyle={{
fontSize: 16,
}}
source={{
uri: cp.playbackUrl,
uri: currentlyPlaying.playbackUrl,
isNetwork: true,
startPosition,
headers: getAuthHeaders(api),
@@ -296,11 +302,11 @@ export const CurrentlyPlayingBar: React.FC = () => {
}}
onPlaybackStateChanged={(e) => {
if (e.isPlaying) {
setPaused(false);
setPlaying(true);
} else if (e.isSeeking) {
return;
} else {
pause();
setPlaying(false);
}
}}
progressUpdateInterval={1000}
@@ -308,7 +314,7 @@ export const CurrentlyPlayingBar: React.FC = () => {
console.log(e);
writeToLog(
"ERROR",
"Video playback error: " + JSON.stringify(e)
"Video playback error: " + JSON.stringify(e),
);
}}
renderLoader={
@@ -364,20 +370,20 @@ export const CurrentlyPlayingBar: React.FC = () => {
<View className="flex flex-row items-center space-x-2">
<TouchableOpacity
onPress={() => {
if (paused) play();
else pause();
if (playing) setPlaying(false);
else setPlaying(true);
}}
className="aspect-square rounded flex flex-col items-center justify-center p-2"
>
{paused ? (
<Ionicons name="play" size={24} color="white" />
) : (
{playing ? (
<Ionicons name="pause" size={24} color="white" />
) : (
<Ionicons name="play" size={24} color="white" />
)}
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
setCp(null);
setCurrentlyPlaying(null);
}}
className="aspect-square rounded flex flex-col items-center justify-center p-2"
>

View File

@@ -4,6 +4,8 @@ import { Feather, Ionicons } from "@expo/vector-icons";
import { runtimeTicksToMinutes } from "@/utils/time";
import { useActionSheet } from "@expo/react-native-action-sheet";
import { View } from "react-native";
import { useAtom } from "jotai";
import { playingAtom } from "./CurrentlyPlayingBar";
interface Props extends React.ComponentProps<typeof Button> {
item: BaseItemDto;