fix: refactor

This commit is contained in:
Fredrik Burmester
2024-08-16 19:56:40 +02:00
parent d15d02d61d
commit d39e3aeb80
10 changed files with 143 additions and 111 deletions

View File

@@ -210,8 +210,6 @@ export default function index() {
limit: 10, limit: 10,
}); });
console.log("Popular", response.data.Items?.length);
return response.data.Items || []; return response.data.Items || [];
}, },
enabled: !!api && !!user?.Id && !!mediaListCollection, enabled: !!api && !!user?.Id && !!mediaListCollection,

View File

@@ -60,8 +60,6 @@ const page: React.FC = () => {
const sortBy: ItemSortBy[] = []; const sortBy: ItemSortBy[] = [];
const includeItemTypes: BaseItemKind[] = []; const includeItemTypes: BaseItemKind[] = [];
console.log("Collection", { collection });
switch (collection?.CollectionType) { switch (collection?.CollectionType) {
case "movies": case "movies":
sortBy.push("SortName", "ProductionYear"); sortBy.push("SortName", "ProductionYear");

View File

@@ -2,6 +2,7 @@ import { AudioTrackSelector } from "@/components/AudioTrackSelector";
import { Bitrate, BitrateSelector } from "@/components/BitrateSelector"; import { Bitrate, BitrateSelector } from "@/components/BitrateSelector";
import { import {
currentlyPlayingItemAtom, currentlyPlayingItemAtom,
fullScreenAtom,
playingAtom, playingAtom,
} from "@/components/CurrentlyPlayingBar"; } from "@/components/CurrentlyPlayingBar";
import { DownloadItem } from "@/components/DownloadItem"; import { DownloadItem } from "@/components/DownloadItem";
@@ -37,6 +38,10 @@ import CastContext, {
useRemoteMediaClient, useRemoteMediaClient,
} from "react-native-google-cast"; } from "react-native-google-cast";
import { ParallaxScrollView } from "../../../components/ParallaxPage"; import { ParallaxScrollView } from "../../../components/ParallaxPage";
import { useSettings } from "@/utils/atoms/settings";
import ios from "@/utils/profiles/ios";
import native from "@/utils/profiles/native";
import old from "@/utils/profiles/old";
const page: React.FC = () => { const page: React.FC = () => {
const local = useLocalSearchParams(); const local = useLocalSearchParams();
@@ -45,10 +50,13 @@ const page: React.FC = () => {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const [settings] = useSettings();
const castDevice = useCastDevice(); const castDevice = useCastDevice();
const [, setCurrentlyPlying] = useAtom(currentlyPlayingItemAtom); const [, setCurrentlyPlying] = useAtom(currentlyPlayingItemAtom);
const [, setPlaying] = useAtom(playingAtom); const [, setPlaying] = useAtom(playingAtom);
const [, setFullscreen] = useAtom(fullScreenAtom);
const client = useRemoteMediaClient(); const client = useRemoteMediaClient();
const chromecastReady = useMemo(() => !!castDevice?.deviceId, [castDevice]); const chromecastReady = useMemo(() => !!castDevice?.deviceId, [castDevice]);
@@ -95,10 +103,21 @@ const page: React.FC = () => {
castDevice, castDevice,
selectedAudioStream, selectedAudioStream,
selectedSubtitleStream, selectedSubtitleStream,
settings,
], ],
queryFn: async () => { queryFn: async () => {
if (!api || !user?.Id || !sessionData) return null; if (!api || !user?.Id || !sessionData) return null;
let deviceProfile: any = ios;
if (castDevice?.deviceId) {
deviceProfile = chromecastProfile;
} else if (settings?.deviceProfile === "Native") {
deviceProfile = native;
} else if (settings?.deviceProfile === "Old") {
deviceProfile = old;
}
const url = await getStreamUrl({ const url = await getStreamUrl({
api, api,
userId: user.Id, userId: user.Id,
@@ -106,16 +125,17 @@ const page: React.FC = () => {
startTimeTicks: item?.UserData?.PlaybackPositionTicks || 0, startTimeTicks: item?.UserData?.PlaybackPositionTicks || 0,
maxStreamingBitrate: maxBitrate.value, maxStreamingBitrate: maxBitrate.value,
sessionData, sessionData,
deviceProfile: castDevice?.deviceId ? chromecastProfile : ios12, deviceProfile,
audioStreamIndex: selectedAudioStream, audioStreamIndex: selectedAudioStream,
subtitleStreamIndex: selectedSubtitleStream, subtitleStreamIndex: selectedSubtitleStream,
forceDirectPlay: settings?.forceDirectPlay,
}); });
console.log("Transcode URL: ", url); console.log("Transcode URL: ", url);
return url; return url;
}, },
enabled: !!sessionData, enabled: !!sessionData && !!api && !!user?.Id && !!item?.Id,
staleTime: 0, staleTime: 0,
}); });
@@ -147,11 +167,13 @@ const page: React.FC = () => {
item, item,
playbackUrl, playbackUrl,
}); });
setPlaying(true); setPlaying(true);
if (settings?.openFullScreenVideoPlayerByDefault === true) {
setFullscreen(true);
}
} }
}, },
[playbackUrl, item], [playbackUrl, item, settings],
); );
const backdropUrl = useMemo( const backdropUrl = useMemo(

View File

@@ -20,12 +20,6 @@ const page: React.FC = () => {
seasonIndex: string; seasonIndex: string;
}; };
useEffect(() => {
if (seriesId) {
console.log("seasonIndex", seasonIndex);
}
}, [seriesId]);
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);

View File

@@ -23,7 +23,7 @@ import CastContext, {
import { chromecastProfile } from "@/utils/profiles/chromecast"; import { chromecastProfile } from "@/utils/profiles/chromecast";
import { import {
currentlyPlayingItemAtom, currentlyPlayingItemAtom,
triggerPlayAtom, playingAtom,
} from "@/components/CurrentlyPlayingBar"; } from "@/components/CurrentlyPlayingBar";
import { AudioTrackSelector } from "@/components/AudioTrackSelector"; import { AudioTrackSelector } from "@/components/AudioTrackSelector";
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector"; import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
@@ -40,6 +40,8 @@ const page: React.FC = () => {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const [, setPlaying] = useAtom(playingAtom);
const castDevice = useCastDevice(); const castDevice = useCastDevice();
const navigation = useNavigation(); const navigation = useNavigation();
@@ -139,7 +141,6 @@ const page: React.FC = () => {
const [, setCp] = useAtom(currentlyPlayingItemAtom); const [, setCp] = useAtom(currentlyPlayingItemAtom);
const client = useRemoteMediaClient(); const client = useRemoteMediaClient();
const [, setPlayTrigger] = useAtom(triggerPlayAtom);
const onPressPlay = useCallback( const onPressPlay = useCallback(
async (type: "device" | "cast" = "device") => { async (type: "device" | "cast" = "device") => {
@@ -169,9 +170,7 @@ const page: React.FC = () => {
item, item,
playbackUrl, playbackUrl,
}); });
setPlaying(true);
// Use this trigger to initiate playback in another component (CurrentlyPlayingBar)
setPlayTrigger((prev) => prev + 1);
} }
}, },
[playbackUrl, item], [playbackUrl, item],

View File

@@ -16,6 +16,7 @@ import { atom, useAtom } from "jotai";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { import {
ActivityIndicator, ActivityIndicator,
Alert,
Platform, Platform,
TouchableOpacity, TouchableOpacity,
View, View,
@@ -33,14 +34,12 @@ export const currentlyPlayingItemAtom = atom<{
playbackUrl: string; playbackUrl: string;
} | null>(null); } | null>(null);
export const triggerPlayAtom = atom(0);
export const playingAtom = atom(false); export const playingAtom = atom(false);
export const fullScreenAtom = atom(false);
export const CurrentlyPlayingBar: React.FC = () => { export const CurrentlyPlayingBar: React.FC = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const segments = useSegments(); const segments = useSegments();
const [settings] = useSettings();
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
@@ -48,10 +47,10 @@ export const CurrentlyPlayingBar: React.FC = () => {
const [currentlyPlaying, setCurrentlyPlaying] = useAtom( const [currentlyPlaying, setCurrentlyPlaying] = useAtom(
currentlyPlayingItemAtom, currentlyPlayingItemAtom,
); );
const [fullScreen, setFullScreen] = useAtom(fullScreenAtom);
const videoRef = useRef<VideoRef | null>(null); const videoRef = useRef<VideoRef | null>(null);
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
const [fullScreen, setFullScreen] = useState(false);
const aBottom = useSharedValue(0); const aBottom = useSharedValue(0);
const aPadding = useSharedValue(0); const aPadding = useSharedValue(0);
@@ -148,11 +147,13 @@ export const CurrentlyPlayingBar: React.FC = () => {
); );
useEffect(() => { useEffect(() => {
if (!item || !api) return;
if (playing) { if (playing) {
videoRef.current?.resume(); videoRef.current?.resume();
} else { } else {
videoRef.current?.pause(); videoRef.current?.pause();
if (progress > 0 && sessionData?.PlaySessionId && api) if (progress > 0 && sessionData?.PlaySessionId)
reportPlaybackStopped({ reportPlaybackStopped({
api, api,
itemId: item?.Id, itemId: item?.Id,
@@ -171,6 +172,15 @@ export const CurrentlyPlayingBar: React.FC = () => {
} }
}, [playing, progress, item, sessionData]); }, [playing, progress, item, sessionData]);
useEffect(() => {
console.log("Full screen changed", fullScreen);
if (fullScreen === true) {
videoRef.current?.presentFullscreenPlayer();
} else {
videoRef.current?.dismissFullscreenPlayer();
}
}, [fullScreen]);
const startPosition = useMemo( const startPosition = useMemo(
() => () =>
item?.UserData?.PlaybackPositionTicks item?.UserData?.PlaybackPositionTicks
@@ -276,6 +286,9 @@ export const CurrentlyPlayingBar: React.FC = () => {
"ERROR", "ERROR",
"Video playback error: " + JSON.stringify(e), "Video playback error: " + JSON.stringify(e),
); );
Alert.alert("Error", "Cannot play this video file.");
setPlaying(false);
setCurrentlyPlaying(null);
}} }}
renderLoader={ renderLoader={
item?.Type !== "Audio" && ( item?.Type !== "Audio" && (

View File

@@ -42,7 +42,7 @@ export const PlayButton: React.FC<Props> = ({
onPress("device"); onPress("device");
break; break;
case cancelButtonIndex: case cancelButtonIndex:
console.log("calcel"); break;
} }
}, },
); );

View File

@@ -8,7 +8,12 @@ import { useAtom } from "jotai";
import { Text } from "../common/Text"; import { Text } from "../common/Text";
import { useFiles } from "@/hooks/useFiles"; import { useFiles } from "@/hooks/useFiles";
import { currentlyPlayingItemAtom } from "../CurrentlyPlayingBar"; import {
currentlyPlayingItemAtom,
fullScreenAtom,
playingAtom,
} from "../CurrentlyPlayingBar";
import { useSettings } from "@/utils/atoms/settings";
interface EpisodeCardProps { interface EpisodeCardProps {
item: BaseItemDto; item: BaseItemDto;
@@ -22,16 +27,22 @@ interface EpisodeCardProps {
export const EpisodeCard: React.FC<EpisodeCardProps> = ({ item }) => { export const EpisodeCard: React.FC<EpisodeCardProps> = ({ item }) => {
const { deleteFile } = useFiles(); const { deleteFile } = useFiles();
const [, setCurrentlyPlaying] = useAtom(currentlyPlayingItemAtom); const [, setCurrentlyPlaying] = useAtom(currentlyPlayingItemAtom);
const [, setPlaying] = useAtom(playingAtom);
const [, setFullscreen] = useAtom(fullScreenAtom);
const [settings] = useSettings();
/** /**
* Handles opening the file for playback. * Handles opening the file for playback.
*/ */
const handleOpenFile = useCallback(() => { const handleOpenFile = useCallback(async () => {
setCurrentlyPlaying({ setCurrentlyPlaying({
item, item,
playbackUrl: `${FileSystem.documentDirectory}/${item.Id}.mp4`, playbackUrl: `${FileSystem.documentDirectory}/${item.Id}.mp4`,
}); });
}, [item, setCurrentlyPlaying]); setPlaying(true);
if (settings?.openFullScreenVideoPlayerByDefault === true)
setFullscreen(true);
}, [item, setCurrentlyPlaying, settings]);
/** /**
* Handles deleting the file with haptic feedback. * Handles deleting the file with haptic feedback.

View File

@@ -1,97 +1,99 @@
import React, { useCallback } from "react";
import { TouchableOpacity, View } from "react-native"; import { TouchableOpacity, View } from "react-native";
import { Text } from "../common/Text";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { runtimeTicksToMinutes } from "@/utils/time";
import * as ContextMenu from "zeego/context-menu"; import * as ContextMenu from "zeego/context-menu";
import { useFiles } from "@/hooks/useFiles";
import * as FileSystem from "expo-file-system"; import * as FileSystem from "expo-file-system";
import { useCallback } from "react";
import * as Haptics from "expo-haptics"; import * as Haptics from "expo-haptics";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { currentlyPlayingItemAtom } from "../CurrentlyPlayingBar";
import { useQuery } from "@tanstack/react-query";
export const MovieCard: React.FC<{ item: BaseItemDto }> = ({ item }) => { import { Text } from "../common/Text";
import { useFiles } from "@/hooks/useFiles";
import { runtimeTicksToMinutes } from "@/utils/time";
import {
currentlyPlayingItemAtom,
fullScreenAtom,
playingAtom,
} from "../CurrentlyPlayingBar";
import { useSettings } from "@/utils/atoms/settings";
interface MovieCardProps {
item: BaseItemDto;
}
/**
* MovieCard component displays a movie with context menu options.
* @param {MovieCardProps} props - The component props.
* @returns {React.ReactElement} The rendered MovieCard component.
*/
export const MovieCard: React.FC<MovieCardProps> = ({ item }) => {
const { deleteFile } = useFiles(); const { deleteFile } = useFiles();
const [_, setCp] = useAtom(currentlyPlayingItemAtom); const [, setCurrentlyPlaying] = useAtom(currentlyPlayingItemAtom);
const [, setPlaying] = useAtom(playingAtom);
const [, setFullscreen] = useAtom(fullScreenAtom);
const [settings] = useSettings();
// const fetchFileSize = async () => { /**
// const filePath = `${FileSystem.documentDirectory}/${item.Id}.mp4`; * Handles opening the file for playback.
// const info = await FileSystem.getInfoAsync(filePath); */
// return info.exists ? info.size : null; const handleOpenFile = useCallback(() => {
// }; console.log("Open movie file", item.Name);
setCurrentlyPlaying({
// const { data: fileSize } = useQuery({
// queryKey: ["fileSize", item?.Id],
// queryFn: fetchFileSize,
// });
const openFile = useCallback(() => {
setCp({
item, item,
playbackUrl: `${FileSystem.documentDirectory}/${item.Id}.mp4`, playbackUrl: `${FileSystem.documentDirectory}/${item.Id}.mp4`,
}); });
}, [item]); setPlaying(true);
if (settings?.openFullScreenVideoPlayerByDefault === true) {
setFullscreen(true);
}
}, [item, setCurrentlyPlaying, setPlaying, settings]);
const options = [ /**
* Handles deleting the file with haptic feedback.
*/
const handleDeleteFile = useCallback(() => {
if (item.Id) {
deleteFile(item.Id);
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
}
}, [deleteFile, item.Id]);
const contextMenuOptions = [
{ {
label: "Delete", label: "Delete",
onSelect: (id: string) => { onSelect: handleDeleteFile,
deleteFile(id);
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
},
destructive: true, destructive: true,
}, },
]; ];
return ( return (
<> <ContextMenu.Root>
<ContextMenu.Root> <ContextMenu.Trigger>
<ContextMenu.Trigger> <TouchableOpacity
<TouchableOpacity onPress={handleOpenFile}
onPress={openFile} className="bg-neutral-900 border border-neutral-800 rounded-2xl p-4"
className="bg-neutral-900 border border-neutral-800 rounded-2xl p-4"
>
<Text className=" font-bold">{item.Name}</Text>
<View className="flex flex-col">
<Text className=" text-xs opacity-50">{item.ProductionYear}</Text>
<Text className=" text-xs opacity-50">
{runtimeTicksToMinutes(item.RunTimeTicks)}
</Text>
{/* <Text className=" text-xs opacity-50">
Size:{" "}
{fileSize
? `${(fileSize / 1000000).toFixed(0)} MB`
: "Calculating..."}{" "}
</Text>*/}
</View>
</TouchableOpacity>
</ContextMenu.Trigger>
<ContextMenu.Content
alignOffset={0}
avoidCollisions={false}
collisionPadding={0}
loop={false}
> >
{options.map((i) => ( <Text className="font-bold">{item.Name}</Text>
<ContextMenu.Item <View className="flex flex-col">
onSelect={() => { <Text className="text-xs opacity-50">{item.ProductionYear}</Text>
i.onSelect(item.Id!); <Text className="text-xs opacity-50">
}} {runtimeTicksToMinutes(item.RunTimeTicks)}
key={i.label} </Text>
destructive={i.destructive} </View>
> </TouchableOpacity>
<ContextMenu.ItemTitle </ContextMenu.Trigger>
style={{ <ContextMenu.Content>
color: "red", {contextMenuOptions.map((option) => (
}} <ContextMenu.Item
> key={option.label}
{i.label} onSelect={option.onSelect}
</ContextMenu.ItemTitle> destructive={option.destructive}
</ContextMenu.Item> >
))} <ContextMenu.ItemTitle style={{ color: "red" }}>
</ContextMenu.Content> {option.label}
</ContextMenu.Root> </ContextMenu.ItemTitle>
</> </ContextMenu.Item>
))}
</ContextMenu.Content>
</ContextMenu.Root>
); );
}; };

View File

@@ -19,10 +19,7 @@ import CastContext, {
useCastDevice, useCastDevice,
useRemoteMediaClient, useRemoteMediaClient,
} from "react-native-google-cast"; } from "react-native-google-cast";
import { import { currentlyPlayingItemAtom, playingAtom } from "../CurrentlyPlayingBar";
currentlyPlayingItemAtom,
triggerPlayAtom,
} from "../CurrentlyPlayingBar";
import { useActionSheet } from "@expo/react-native-action-sheet"; import { useActionSheet } from "@expo/react-native-action-sheet";
import ios from "@/utils/profiles/ios"; import ios from "@/utils/profiles/ios";
@@ -46,7 +43,7 @@ export const SongsListItem: React.FC<Props> = ({
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const castDevice = useCastDevice(); const castDevice = useCastDevice();
const [, setCp] = useAtom(currentlyPlayingItemAtom); const [, setCp] = useAtom(currentlyPlayingItemAtom);
const [, setPlayTrigger] = useAtom(triggerPlayAtom); const [, setPlaying] = useAtom(playingAtom);
const client = useRemoteMediaClient(); const client = useRemoteMediaClient();
const { showActionSheetWithOptions } = useActionSheet(); const { showActionSheetWithOptions } = useActionSheet();
@@ -74,7 +71,7 @@ export const SongsListItem: React.FC<Props> = ({
play("device"); play("device");
break; break;
case cancelButtonIndex: case cancelButtonIndex:
console.log("calcel"); break;
} }
}, },
); );
@@ -125,9 +122,7 @@ export const SongsListItem: React.FC<Props> = ({
item, item,
playbackUrl: url, playbackUrl: url,
}); });
setPlaying(true);
// Use this trigger to initiate playback in another component (CurrentlyPlayingBar)
setPlayTrigger((prev) => prev + 1);
} }
}; };