mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
wip
This commit is contained in:
@@ -4,6 +4,7 @@ import { LargeMovieCarousel } from "@/components/home/LargeMovieCarousel";
|
|||||||
import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
|
import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
|
||||||
import { Loader } from "@/components/Loader";
|
import { Loader } from "@/components/Loader";
|
||||||
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
||||||
|
import { TAB_HEIGHT } from "@/constants/Values";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
@@ -25,7 +26,6 @@ import {
|
|||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Platform,
|
Platform,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
SafeAreaView,
|
|
||||||
ScrollView,
|
ScrollView,
|
||||||
View,
|
View,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
@@ -139,18 +139,24 @@ export default function index() {
|
|||||||
|
|
||||||
const refetch = useCallback(async () => {
|
const refetch = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await queryClient.refetchQueries({ queryKey: ["userViews"] });
|
await queryClient.invalidateQueries();
|
||||||
await queryClient.refetchQueries({ queryKey: ["resumeItems"] });
|
// await queryClient.invalidateQueries({ queryKey: ["userViews"] });
|
||||||
await queryClient.refetchQueries({ queryKey: ["nextUp-all"] });
|
// await queryClient.invalidateQueries({ queryKey: ["resumeItems"] });
|
||||||
await queryClient.refetchQueries({ queryKey: ["recentlyAddedInMovies"] });
|
// await queryClient.invalidateQueries({ queryKey: ["continueWatching"] });
|
||||||
await queryClient.refetchQueries({ queryKey: ["recentlyAddedInTVShows"] });
|
// await queryClient.invalidateQueries({ queryKey: ["nextUp-all"] });
|
||||||
await queryClient.refetchQueries({ queryKey: ["suggestions"] });
|
// await queryClient.invalidateQueries({
|
||||||
await queryClient.refetchQueries({
|
// queryKey: ["recentlyAddedInMovies"],
|
||||||
queryKey: ["sf_promoted"],
|
// });
|
||||||
});
|
// await queryClient.invalidateQueries({
|
||||||
await queryClient.refetchQueries({
|
// queryKey: ["recentlyAddedInTVShows"],
|
||||||
queryKey: ["sf_carousel"],
|
// });
|
||||||
});
|
// await queryClient.invalidateQueries({ queryKey: ["suggestions"] });
|
||||||
|
// await queryClient.invalidateQueries({
|
||||||
|
// queryKey: ["sf_promoted"],
|
||||||
|
// });
|
||||||
|
// await queryClient.invalidateQueries({
|
||||||
|
// queryKey: ["sf_carousel"],
|
||||||
|
// });
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [queryClient, user?.Id]);
|
}, [queryClient, user?.Id]);
|
||||||
|
|
||||||
@@ -344,15 +350,33 @@ export default function index() {
|
|||||||
}
|
}
|
||||||
key={"home"}
|
key={"home"}
|
||||||
contentContainerStyle={{
|
contentContainerStyle={{
|
||||||
|
flexDirection: "column",
|
||||||
paddingLeft: insets.left,
|
paddingLeft: insets.left,
|
||||||
paddingRight: insets.right,
|
paddingRight: insets.right,
|
||||||
paddingBottom:
|
paddingTop: 8,
|
||||||
Platform.OS === "android" ? insets.bottom + 65 : insets.bottom,
|
rowGap: 8,
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
marginBottom: TAB_HEIGHT,
|
||||||
}}
|
}}
|
||||||
className="flex flex-col space-y-4"
|
|
||||||
>
|
>
|
||||||
<LargeMovieCarousel />
|
<LargeMovieCarousel />
|
||||||
|
|
||||||
|
<ScrollingCollectionList
|
||||||
|
key="continueWatching"
|
||||||
|
title={"Continue Watching"}
|
||||||
|
queryKey={["continueWatching", user?.Id]}
|
||||||
|
queryFn={async () =>
|
||||||
|
(
|
||||||
|
await getItemsApi(api).getResumeItems({
|
||||||
|
userId: user?.Id,
|
||||||
|
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
|
||||||
|
})
|
||||||
|
).data.Items || []
|
||||||
|
}
|
||||||
|
orientation={"horizontal"}
|
||||||
|
/>
|
||||||
|
|
||||||
<ScrollingCollectionList
|
<ScrollingCollectionList
|
||||||
key="nextUp"
|
key="nextUp"
|
||||||
title={"Next Up"}
|
title={"Next Up"}
|
||||||
@@ -370,21 +394,6 @@ export default function index() {
|
|||||||
orientation={"horizontal"}
|
orientation={"horizontal"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ScrollingCollectionList
|
|
||||||
key="continueWatching"
|
|
||||||
title={"Continue Watching"}
|
|
||||||
queryKey={["continueWatching", user?.Id]}
|
|
||||||
queryFn={async () =>
|
|
||||||
(
|
|
||||||
await getItemsApi(api).getResumeItems({
|
|
||||||
userId: user?.Id,
|
|
||||||
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
|
|
||||||
})
|
|
||||||
).data.Items || []
|
|
||||||
}
|
|
||||||
orientation={"horizontal"}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{sections.map((section, index) => {
|
{sections.map((section, index) => {
|
||||||
if (section.type === "ScrollingCollectionList") {
|
if (section.type === "ScrollingCollectionList") {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export default function settings() {
|
|||||||
<View className="flex flex-col rounded-xl overflow-hidden border-neutral-800 divide-y-2 divide-solid divide-neutral-800 ">
|
<View className="flex flex-col rounded-xl overflow-hidden border-neutral-800 divide-y-2 divide-solid divide-neutral-800 ">
|
||||||
<ListItem title="User" subTitle={user?.Name} />
|
<ListItem title="User" subTitle={user?.Name} />
|
||||||
<ListItem title="Server" subTitle={api?.basePath} />
|
<ListItem title="Server" subTitle={api?.basePath} />
|
||||||
|
<ListItem title="Token" subTitle={api?.accessToken} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Loader } from "@/components/Loader";
|
|||||||
import AlbumCover from "@/components/posters/AlbumCover";
|
import AlbumCover from "@/components/posters/AlbumCover";
|
||||||
import MoviePoster from "@/components/posters/MoviePoster";
|
import MoviePoster from "@/components/posters/MoviePoster";
|
||||||
import SeriesPoster from "@/components/posters/SeriesPoster";
|
import SeriesPoster from "@/components/posters/SeriesPoster";
|
||||||
|
import { TAB_HEIGHT } from "@/constants/Values";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||||
@@ -226,8 +227,11 @@ export default function search() {
|
|||||||
paddingLeft: insets.left,
|
paddingLeft: insets.left,
|
||||||
paddingRight: insets.right,
|
paddingRight: insets.right,
|
||||||
}}
|
}}
|
||||||
|
style={{
|
||||||
|
marginBottom: TAB_HEIGHT,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<View className="flex flex-col pt-4 pb-32">
|
<View className="flex flex-col pt-4">
|
||||||
{Platform.OS === "android" && (
|
{Platform.OS === "android" && (
|
||||||
<View className="mb-4 px-4">
|
<View className="mb-4 px-4">
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { TabBarIcon } from "@/components/navigation/TabBarIcon";
|
import { TabBarIcon } from "@/components/navigation/TabBarIcon";
|
||||||
import { Colors } from "@/constants/Colors";
|
import { Colors } from "@/constants/Colors";
|
||||||
import { useCheckRunningJobs } from "@/hooks/useCheckRunningJobs";
|
|
||||||
import { BlurView } from "expo-blur";
|
import { BlurView } from "expo-blur";
|
||||||
import * as NavigationBar from "expo-navigation-bar";
|
import * as NavigationBar from "expo-navigation-bar";
|
||||||
import { Tabs } from "expo-router";
|
import { Tabs } from "expo-router";
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ function Layout() {
|
|||||||
new QueryClient({
|
new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
staleTime: 60 * 1000,
|
staleTime: 0,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
refetchOnReconnect: true,
|
refetchOnReconnect: true,
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useAtom } from "jotai";
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { WatchedIndicator } from "./WatchedIndicator";
|
import { WatchedIndicator } from "./WatchedIndicator";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
type ContinueWatchingPosterProps = {
|
type ContinueWatchingPosterProps = {
|
||||||
item: BaseItemDto;
|
item: BaseItemDto;
|
||||||
@@ -14,7 +15,6 @@ type ContinueWatchingPosterProps = {
|
|||||||
|
|
||||||
const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
|
const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
|
||||||
item,
|
item,
|
||||||
width = 176,
|
|
||||||
useEpisodePoster = false,
|
useEpisodePoster = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
@@ -47,21 +47,11 @@ const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
|
|||||||
|
|
||||||
if (!url)
|
if (!url)
|
||||||
return (
|
return (
|
||||||
<View
|
<View className="aspect-video border border-neutral-800 w-44"></View>
|
||||||
className="aspect-video border border-neutral-800"
|
|
||||||
style={{
|
|
||||||
width,
|
|
||||||
}}
|
|
||||||
></View>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View className="relative w-44 aspect-video rounded-lg overflow-hidden border border-neutral-800">
|
||||||
style={{
|
|
||||||
width,
|
|
||||||
}}
|
|
||||||
className="relative aspect-video rounded-lg overflow-hidden border border-neutral-800"
|
|
||||||
>
|
|
||||||
<Image
|
<Image
|
||||||
key={item.Id}
|
key={item.Id}
|
||||||
id={item.Id}
|
id={item.Id}
|
||||||
|
|||||||
@@ -23,7 +23,11 @@ export const ListItem: React.FC<PropsWithChildren<Props>> = ({
|
|||||||
>
|
>
|
||||||
<View className="flex flex-col">
|
<View className="flex flex-col">
|
||||||
<Text className="font-bold ">{title}</Text>
|
<Text className="font-bold ">{title}</Text>
|
||||||
{subTitle && <Text className="text-xs">{subTitle}</Text>}
|
{subTitle && (
|
||||||
|
<Text className="text-xs" selectable>
|
||||||
|
{subTitle}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
{iconAfter}
|
{iconAfter}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -21,11 +21,17 @@ export const PlayedStatus: React.FC<Props> = ({ item, ...props }) => {
|
|||||||
|
|
||||||
const invalidateQueries = () => {
|
const invalidateQueries = () => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["item"],
|
queryKey: ["item", item.Id],
|
||||||
});
|
});
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["resumeItems"],
|
queryKey: ["resumeItems"],
|
||||||
});
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["continueWatching"],
|
||||||
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["nextUp-all"],
|
||||||
|
});
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["nextUp"],
|
queryKey: ["nextUp"],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ export const HorizontalScroll = forwardRef<
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{...props}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import {
|
|||||||
type QueryFunction,
|
type QueryFunction,
|
||||||
type QueryKey,
|
type QueryKey,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import { View, ViewProps } from "react-native";
|
import { ScrollView, View, ViewProps } from "react-native";
|
||||||
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
||||||
import { ItemCardText } from "../ItemCardText";
|
import { ItemCardText } from "../ItemCardText";
|
||||||
import { HorizontalScroll } from "../common/HorrizontalScroll";
|
import { HorizontalScroll } from "../common/HorrizontalScroll";
|
||||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||||
import SeriesPoster from "../posters/SeriesPoster";
|
import SeriesPoster from "../posters/SeriesPoster";
|
||||||
|
import { FlashList } from "@shopify/flash-list";
|
||||||
|
|
||||||
interface Props extends ViewProps {
|
interface Props extends ViewProps {
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
@@ -39,40 +40,56 @@ export const ScrollingCollectionList: React.FC<Props> = ({
|
|||||||
if (disabled || !title) return null;
|
if (disabled || !title) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View {...props}>
|
<View {...props} className="">
|
||||||
<Text className="px-4 text-lg font-bold mb-2 text-neutral-100">
|
<Text className="px-4 text-lg font-bold mb-2 text-neutral-100">
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<HorizontalScroll
|
{isLoading ? (
|
||||||
data={data}
|
<View
|
||||||
extraData={[orientation, isLoading]}
|
className={`
|
||||||
loading={isLoading}
|
flex flex-row gap-2 px-4
|
||||||
renderItem={(item, index) => (
|
`}
|
||||||
<TouchableItemRouter
|
>
|
||||||
item={item}
|
{[1, 2, 3].map((i) => (
|
||||||
key={index}
|
<View className="w-44 mb-2">
|
||||||
style={{
|
<View className="bg-neutral-800 h-24 w-full rounded-md mb-2"></View>
|
||||||
width: orientation === "horizontal" ? 176 : 112,
|
<View className="bg-neutral-800 h-4 w-full rounded-md mb-2"></View>
|
||||||
zIndex: 100,
|
<View className="bg-neutral-800 h-4 w-1/2 rounded-md"></View>
|
||||||
}}
|
</View>
|
||||||
>
|
))}
|
||||||
{item.Type === "Episode" && orientation === "horizontal" && (
|
</View>
|
||||||
<ContinueWatchingPoster item={item} />
|
) : (
|
||||||
)}
|
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
||||||
{item.Type === "Episode" && orientation === "vertical" && (
|
<View className="px-4 flex flex-row">
|
||||||
<SeriesPoster item={item} />
|
{data?.map((item, index) => (
|
||||||
)}
|
<TouchableItemRouter
|
||||||
{item.Type === "Movie" && orientation === "horizontal" && (
|
item={item}
|
||||||
<ContinueWatchingPoster item={item} />
|
key={index}
|
||||||
)}
|
className={`
|
||||||
{item.Type === "Movie" && orientation === "vertical" && (
|
mr-2
|
||||||
<MoviePoster item={item} />
|
|
||||||
)}
|
${orientation === "horizontal" ? "w-44" : "w-28"}
|
||||||
{item.Type === "Series" && <SeriesPoster item={item} />}
|
`}
|
||||||
<ItemCardText item={item} />
|
>
|
||||||
</TouchableItemRouter>
|
{item.Type === "Episode" && orientation === "horizontal" && (
|
||||||
)}
|
<ContinueWatchingPoster item={item} />
|
||||||
/>
|
)}
|
||||||
|
{item.Type === "Episode" && orientation === "vertical" && (
|
||||||
|
<SeriesPoster item={item} />
|
||||||
|
)}
|
||||||
|
{item.Type === "Movie" && orientation === "horizontal" && (
|
||||||
|
<ContinueWatchingPoster item={item} />
|
||||||
|
)}
|
||||||
|
{item.Type === "Movie" && orientation === "vertical" && (
|
||||||
|
<MoviePoster item={item} />
|
||||||
|
)}
|
||||||
|
{item.Type === "Series" && <SeriesPoster item={item} />}
|
||||||
|
<ItemCardText item={item} />
|
||||||
|
</TouchableItemRouter>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
|
|||||||
}, [item]);
|
}, [item]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="relative rounded-lg overflow-hidden border border-neutral-900">
|
<View className="relative rounded-lg overflow-hidden border border-neutral-900 w-28 aspect-[10/15]">
|
||||||
<Image
|
<Image
|
||||||
placeholder={{
|
placeholder={{
|
||||||
blurhash,
|
blurhash,
|
||||||
@@ -57,7 +57,6 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WatchedIndicator item={item} />
|
<WatchedIndicator item={item} />
|
||||||
{showProgress && progress > 0 && (
|
{showProgress && progress > 0 && (
|
||||||
<View className="h-1 bg-red-600 w-full"></View>
|
<View className="h-1 bg-red-600 w-full"></View>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const SeriesPoster: React.FC<MoviePosterProps> = ({ item }) => {
|
|||||||
}, [item]);
|
}, [item]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="relative rounded-lg overflow-hidden border border-neutral-900">
|
<View className="w-28 aspect-[10/15] relative rounded-lg overflow-hidden border border-neutral-900 ">
|
||||||
<Image
|
<Image
|
||||||
placeholder={{
|
placeholder={{
|
||||||
blurhash,
|
blurhash,
|
||||||
@@ -49,7 +49,7 @@ const SeriesPoster: React.FC<MoviePosterProps> = ({ item }) => {
|
|||||||
cachePolicy={"memory-disk"}
|
cachePolicy={"memory-disk"}
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
style={{
|
style={{
|
||||||
aspectRatio: "10/15",
|
height: "100%",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { useDownload } from "@/providers/DownloadProvider";
|
import { useDownload } from "@/providers/DownloadProvider";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import {
|
||||||
|
apiAtom,
|
||||||
|
getOrSetDeviceId,
|
||||||
|
userAtom,
|
||||||
|
} from "@/providers/JellyfinProvider";
|
||||||
import { ScreenOrientationEnum, useSettings } from "@/utils/atoms/settings";
|
import { ScreenOrientationEnum, useSettings } from "@/utils/atoms/settings";
|
||||||
import {
|
import {
|
||||||
BACKGROUND_FETCH_TASK,
|
BACKGROUND_FETCH_TASK,
|
||||||
@@ -28,6 +32,8 @@ import { Input } from "../common/Input";
|
|||||||
import { Text } from "../common/Text";
|
import { Text } from "../common/Text";
|
||||||
import { Loader } from "../Loader";
|
import { Loader } from "../Loader";
|
||||||
import { MediaToggles } from "./MediaToggles";
|
import { MediaToggles } from "./MediaToggles";
|
||||||
|
import axios from "axios";
|
||||||
|
import { getStatistics } from "@/utils/optimize-server";
|
||||||
|
|
||||||
interface Props extends ViewProps {}
|
interface Props extends ViewProps {}
|
||||||
|
|
||||||
@@ -44,6 +50,21 @@ export const SettingToggles: React.FC<Props> = ({ ...props }) => {
|
|||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const { data: optimizeServerStatistics } = useQuery({
|
||||||
|
queryKey: ["optimize-server", settings?.optimizedVersionsServerUrl],
|
||||||
|
queryFn: async () =>
|
||||||
|
getStatistics({
|
||||||
|
url: settings?.optimizedVersionsServerUrl,
|
||||||
|
authHeader: api?.accessToken,
|
||||||
|
deviceId: await getOrSetDeviceId(),
|
||||||
|
}),
|
||||||
|
refetchInterval: 1000,
|
||||||
|
staleTime: 0,
|
||||||
|
enabled:
|
||||||
|
!!settings?.optimizedVersionsServerUrl &&
|
||||||
|
settings.optimizedVersionsServerUrl.length > 0,
|
||||||
|
});
|
||||||
|
|
||||||
/********************
|
/********************
|
||||||
* Background task
|
* Background task
|
||||||
*******************/
|
*******************/
|
||||||
@@ -568,11 +589,24 @@ export const SettingToggles: React.FC<Props> = ({ ...props }) => {
|
|||||||
>
|
>
|
||||||
<View className="flex flex-col bg-neutral-900 px-4 py-4">
|
<View className="flex flex-col bg-neutral-900 px-4 py-4">
|
||||||
<View className="flex flex-col shrink mb-2">
|
<View className="flex flex-col shrink mb-2">
|
||||||
<Text className="font-semibold">Optimized versions server</Text>
|
<View className="flex flex-row justify-between items-center">
|
||||||
|
<Text className="font-semibold">
|
||||||
|
Optimized versions server
|
||||||
|
</Text>
|
||||||
|
<View
|
||||||
|
className={`
|
||||||
|
w-3 h-3 rounded-full
|
||||||
|
${
|
||||||
|
optimizeServerStatistics ? "bg-green-600" : "bg-red-600"
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
></View>
|
||||||
|
</View>
|
||||||
<Text className="text-xs opacity-50">
|
<Text className="text-xs opacity-50">
|
||||||
Set the URL for the optimized versions server for downloads.
|
Set the URL for the optimized versions server for downloads.
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
<View></View>
|
||||||
<View className="flex flex-col">
|
<View className="flex flex-col">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Optimized versions server URL..."
|
placeholder="Optimized versions server URL..."
|
||||||
@@ -587,7 +621,7 @@ export const SettingToggles: React.FC<Props> = ({ ...props }) => {
|
|||||||
color="purple"
|
color="purple"
|
||||||
className="h-12 mt-2"
|
className="h-12 mt-2"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
toast.success("Saved");
|
toast.info("Saved");
|
||||||
updateSettings({
|
updateSettings({
|
||||||
optimizedVersionsServerUrl:
|
optimizedVersionsServerUrl:
|
||||||
optimizedVersionsServerUrl.length === 0
|
optimizedVersionsServerUrl.length === 0
|
||||||
|
|||||||
3
constants/Values.ts
Normal file
3
constants/Values.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { Platform } from "react-native";
|
||||||
|
|
||||||
|
export const TAB_HEIGHT = Platform.OS === "android" ? 58 : 74;
|
||||||
@@ -3,9 +3,9 @@ import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
interface IJobInput {
|
interface IJobInput {
|
||||||
deviceId: string;
|
deviceId?: string | null;
|
||||||
authHeader: string;
|
authHeader?: string | null;
|
||||||
url: string;
|
url?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JobStatus {
|
export interface JobStatus {
|
||||||
@@ -88,6 +88,10 @@ export async function cancelJobById({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function cancelAllJobs({ authHeader, url, deviceId }: IJobInput) {
|
export async function cancelAllJobs({ authHeader, url, deviceId }: IJobInput) {
|
||||||
|
if (!deviceId) return false;
|
||||||
|
if (!authHeader) return false;
|
||||||
|
if (!url) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await getAllJobsByDeviceId({
|
await getAllJobsByDeviceId({
|
||||||
deviceId,
|
deviceId,
|
||||||
@@ -109,3 +113,41 @@ export async function cancelAllJobs({ authHeader, url, deviceId }: IJobInput) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches statistics for a specific device.
|
||||||
|
*
|
||||||
|
* @param {IJobInput} params - The parameters for the API request.
|
||||||
|
* @param {string} params.deviceId - The ID of the device to fetch statistics for.
|
||||||
|
* @param {string} params.authHeader - The authorization header for the API request.
|
||||||
|
* @param {string} params.url - The base URL for the API endpoint.
|
||||||
|
*
|
||||||
|
* @returns {Promise<any | null>} A promise that resolves to the statistics data or null if the request fails.
|
||||||
|
*
|
||||||
|
* @throws {Error} Throws an error if any required parameter is missing.
|
||||||
|
*/
|
||||||
|
export async function getStatistics({
|
||||||
|
authHeader,
|
||||||
|
url,
|
||||||
|
deviceId,
|
||||||
|
}: IJobInput): Promise<any | null> {
|
||||||
|
if (!deviceId || !authHeader || !url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const statusResponse = await axios.get(`${url}statistics`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: authHeader,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
deviceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return statusResponse.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch statistics:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user