mirror of
https://github.com/streamyfin/streamyfin.git
synced 2025-08-20 18:37:18 +02:00
feat: [StreamyfinPlugin] Jellyseerr, Search Engine, & Download settings
- Added DisabledSetting.tsx component - Added DownloadMethod enum - cleanup
This commit is contained in:
@@ -4,7 +4,7 @@ import { MovieCard } from "@/components/downloads/MovieCard";
|
||||
import { SeriesCard } from "@/components/downloads/SeriesCard";
|
||||
import { DownloadedItem, useDownload } from "@/providers/DownloadProvider";
|
||||
import { queueAtom } from "@/utils/atoms/queue";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import {DownloadMethod, useSettings} from "@/utils/atoms/settings";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useNavigation, useRouter } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
@@ -96,7 +96,7 @@ export default function page() {
|
||||
>
|
||||
<View className="py-4">
|
||||
<View className="mb-4 flex flex-col space-y-4 px-4">
|
||||
{settings?.downloadMethod === "remux" && (
|
||||
{settings?.downloadMethod === DownloadMethod.Remux && (
|
||||
<View className="bg-neutral-900 p-4 rounded-2xl">
|
||||
<Text className="text-lg font-bold">Queue</Text>
|
||||
<Text className="text-xs opacity-70 text-red-600">
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useJellyfin } from "@/providers/JellyfinProvider";
|
||||
import { clearLogs } from "@/utils/log";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useNavigation, useRouter } from "expo-router";
|
||||
import { useEffect } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { ScrollView, TouchableOpacity, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
|
||||
@@ -1,78 +1,16 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { JellyseerrSettings } from "@/components/settings/Jellyseerr";
|
||||
import { OptimizedServerForm } from "@/components/settings/OptimizedServerForm";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getOrSetDeviceId } from "@/utils/device";
|
||||
import { getStatistics } from "@/utils/optimize-server";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useNavigation } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ActivityIndicator, TouchableOpacity, View } from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
|
||||
export default function page() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const [api] = useAtom(apiAtom);
|
||||
const [settings, updateSettings] = useSettings();
|
||||
|
||||
const [optimizedVersionsServerUrl, setOptimizedVersionsServerUrl] =
|
||||
useState<string>(settings?.optimizedVersionsServerUrl || "");
|
||||
|
||||
const saveMutation = useMutation({
|
||||
mutationFn: async (newVal: string) => {
|
||||
if (newVal.length === 0 || !newVal.startsWith("http")) {
|
||||
toast.error("Invalid URL");
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedUrl = newVal.endsWith("/") ? newVal : newVal + "/";
|
||||
|
||||
updateSettings({
|
||||
optimizedVersionsServerUrl: updatedUrl,
|
||||
});
|
||||
|
||||
return await getStatistics({
|
||||
url: settings?.optimizedVersionsServerUrl,
|
||||
authHeader: api?.accessToken,
|
||||
deviceId: getOrSetDeviceId(),
|
||||
});
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
if (data) {
|
||||
toast.success("Connected");
|
||||
} else {
|
||||
toast.error("Could not connect");
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
toast.error("Could not connect");
|
||||
},
|
||||
});
|
||||
|
||||
const onSave = (newVal: string) => {
|
||||
saveMutation.mutate(newVal);
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// navigation.setOptions({
|
||||
// title: "Optimized Server",
|
||||
// headerRight: () =>
|
||||
// saveMutation.isPending ? (
|
||||
// <ActivityIndicator size={"small"} color={"white"} />
|
||||
// ) : (
|
||||
// <TouchableOpacity onPress={() => onSave(optimizedVersionsServerUrl)}>
|
||||
// <Text className="text-blue-500">Save</Text>
|
||||
// </TouchableOpacity>
|
||||
// ),
|
||||
// });
|
||||
// }, [navigation, optimizedVersionsServerUrl, saveMutation.isPending]);
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
|
||||
return (
|
||||
<View className="p-4">
|
||||
<DisabledSetting
|
||||
disabled={pluginSettings?.jellyseerrServerUrl?.locked === true}
|
||||
className="p-4"
|
||||
>
|
||||
<JellyseerrSettings />
|
||||
</View>
|
||||
</DisabledSetting>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { ListGroup } from "@/components/list/ListGroup";
|
||||
import { ListItem } from "@/components/list/ListItem";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useNavigation } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import React, {useEffect, useMemo, useState} from "react";
|
||||
import {
|
||||
Linking,
|
||||
Switch,
|
||||
@@ -15,11 +13,12 @@ import {
|
||||
View,
|
||||
} from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
|
||||
export default function page() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [value, setValue] = useState<string>(settings?.marlinServerUrl || "");
|
||||
@@ -35,69 +34,81 @@ export default function page() {
|
||||
Linking.openURL("https://github.com/fredrikburmester/marlin-search");
|
||||
};
|
||||
|
||||
const disabled = useMemo(() => {
|
||||
return pluginSettings?.searchEngine?.locked === true && pluginSettings?.marlinServerUrl?.locked === true
|
||||
}, [pluginSettings]);
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerRight: () => (
|
||||
<TouchableOpacity onPress={() => onSave(value)}>
|
||||
<Text className="text-blue-500">Save</Text>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
});
|
||||
if (!pluginSettings?.marlinServerUrl?.locked) {
|
||||
navigation.setOptions({
|
||||
headerRight: () => (
|
||||
<TouchableOpacity onPress={() => onSave(value)}>
|
||||
<Text className="text-blue-500">Save</Text>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
});
|
||||
}
|
||||
}, [navigation, value]);
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
return (
|
||||
<View className="px-4">
|
||||
<DisabledSetting
|
||||
disabled={disabled}
|
||||
className="px-4"
|
||||
>
|
||||
<ListGroup>
|
||||
<ListItem
|
||||
title={"Enable Marlin Search"}
|
||||
onPress={() => {
|
||||
updateSettings({ searchEngine: "Jellyfin" });
|
||||
queryClient.invalidateQueries({ queryKey: ["search"] });
|
||||
}}
|
||||
<DisabledSetting
|
||||
disabled={pluginSettings?.searchEngine?.locked === true}
|
||||
showText={!pluginSettings?.marlinServerUrl?.locked}
|
||||
>
|
||||
<Switch
|
||||
value={settings.searchEngine === "Marlin"}
|
||||
onValueChange={(value) => {
|
||||
updateSettings({ searchEngine: value ? "Marlin" : "Jellyfin" });
|
||||
<ListItem
|
||||
title={"Enable Marlin Search"}
|
||||
onPress={() => {
|
||||
updateSettings({ searchEngine: "Jellyfin" });
|
||||
queryClient.invalidateQueries({ queryKey: ["search"] });
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
>
|
||||
<Switch
|
||||
value={settings.searchEngine === "Marlin"}
|
||||
onValueChange={(value) => {
|
||||
updateSettings({ searchEngine: value ? "Marlin" : "Jellyfin" });
|
||||
queryClient.invalidateQueries({ queryKey: ["search"] });
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
</DisabledSetting>
|
||||
</ListGroup>
|
||||
|
||||
<View
|
||||
className={`mt-2 ${
|
||||
settings.searchEngine === "Marlin" ? "" : "opacity-50"
|
||||
}`}
|
||||
<DisabledSetting
|
||||
disabled={pluginSettings?.marlinServerUrl?.locked === true}
|
||||
showText={!pluginSettings?.searchEngine?.locked}
|
||||
className="mt-2 flex flex-col rounded-xl overflow-hidden pl-4 bg-neutral-900 px-4"
|
||||
>
|
||||
<View className="flex flex-col rounded-xl overflow-hidden pl-4 bg-neutral-900 px-4">
|
||||
<View
|
||||
className={`flex flex-row items-center bg-neutral-900 h-11 pr-4`}
|
||||
>
|
||||
<Text className="mr-4">URL</Text>
|
||||
<TextInput
|
||||
editable={settings.searchEngine === "Marlin"}
|
||||
className="text-white"
|
||||
placeholder="http(s)://domain.org:port"
|
||||
value={value}
|
||||
keyboardType="url"
|
||||
returnKeyType="done"
|
||||
autoCapitalize="none"
|
||||
textContentType="URL"
|
||||
onChangeText={(text) => setValue(text)}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
className={`flex flex-row items-center bg-neutral-900 h-11 pr-4`}
|
||||
>
|
||||
<Text className="mr-4">URL</Text>
|
||||
<TextInput
|
||||
editable={settings.searchEngine === "Marlin"}
|
||||
className="text-white"
|
||||
placeholder="http(s)://domain.org:port"
|
||||
value={value}
|
||||
keyboardType="url"
|
||||
returnKeyType="done"
|
||||
autoCapitalize="none"
|
||||
textContentType="URL"
|
||||
onChangeText={(text) => setValue(text)}
|
||||
/>
|
||||
</View>
|
||||
<Text className="px-4 text-xs text-neutral-500 mt-1">
|
||||
Enter the URL for the Marlin server. The URL should include http or
|
||||
https and optionally the port.{" "}
|
||||
<Text className="text-blue-500" onPress={handleOpenLink}>
|
||||
Read more about Marlin.
|
||||
</Text>
|
||||
</DisabledSetting>
|
||||
<Text className="px-4 text-xs text-neutral-500 mt-1">
|
||||
Enter the URL for the Marlin server. The URL should include http or
|
||||
https and optionally the port.{" "}
|
||||
<Text className="text-blue-500" onPress={handleOpenLink}>
|
||||
Read more about Marlin.
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Text>
|
||||
</DisabledSetting>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@ import { useAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ActivityIndicator, TouchableOpacity, View } from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
|
||||
export default function page() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const [api] = useAtom(apiAtom);
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
|
||||
const [optimizedVersionsServerUrl, setOptimizedVersionsServerUrl] =
|
||||
useState<string>(settings?.optimizedVersionsServerUrl || "");
|
||||
@@ -56,25 +57,30 @@ export default function page() {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: "Optimized Server",
|
||||
headerRight: () =>
|
||||
saveMutation.isPending ? (
|
||||
<ActivityIndicator size={"small"} color={"white"} />
|
||||
) : (
|
||||
<TouchableOpacity onPress={() => onSave(optimizedVersionsServerUrl)}>
|
||||
<Text className="text-blue-500">Save</Text>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
});
|
||||
if (!pluginSettings?.optimizedVersionsServerUrl?.locked) {
|
||||
navigation.setOptions({
|
||||
title: "Optimized Server",
|
||||
headerRight: () =>
|
||||
saveMutation.isPending ? (
|
||||
<ActivityIndicator size={"small"} color={"white"} />
|
||||
) : (
|
||||
<TouchableOpacity onPress={() => onSave(optimizedVersionsServerUrl)}>
|
||||
<Text className="text-blue-500">Save</Text>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
});
|
||||
}
|
||||
}, [navigation, optimizedVersionsServerUrl, saveMutation.isPending]);
|
||||
|
||||
return (
|
||||
<View className="p-4">
|
||||
<DisabledSetting
|
||||
disabled={pluginSettings?.optimizedVersionsServerUrl?.locked === true}
|
||||
className="p-4"
|
||||
>
|
||||
<OptimizedServerForm
|
||||
value={optimizedVersionsServerUrl}
|
||||
onChangeValue={setOptimizedVersionsServerUrl}
|
||||
/>
|
||||
</View>
|
||||
</DisabledSetting>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useRemuxHlsToMp4 } from "@/hooks/useRemuxHlsToMp4";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { queueActions, queueAtom } from "@/utils/atoms/queue";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import {DownloadMethod, useSettings} from "@/utils/atoms/settings";
|
||||
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
|
||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||
import { saveDownloadItemInfoToDiskTmp } from "@/utils/optimize-server";
|
||||
@@ -74,7 +74,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
||||
[user]
|
||||
);
|
||||
const usingOptimizedServer = useMemo(
|
||||
() => settings?.downloadMethod === "optimized",
|
||||
() => settings?.downloadMethod === DownloadMethod.Optimized,
|
||||
[settings]
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import {DownloadMethod, useSettings} from "@/utils/atoms/settings";
|
||||
import { JobStatus } from "@/utils/optimize-server";
|
||||
import { formatTimeString } from "@/utils/time";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
@@ -9,7 +8,6 @@ import { checkForExistingDownloads } from "@kesha-antonov/react-native-backgroun
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useRouter } from "expo-router";
|
||||
import { FFmpegKit } from "ffmpeg-kit-react-native";
|
||||
import { useAtom } from "jotai";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
TouchableOpacity,
|
||||
@@ -62,7 +60,7 @@ const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
|
||||
mutationFn: async (id: string) => {
|
||||
if (!process) throw new Error("No active download");
|
||||
|
||||
if (settings?.downloadMethod === "optimized") {
|
||||
if (settings?.downloadMethod === DownloadMethod.Optimized) {
|
||||
try {
|
||||
const tasks = await checkForExistingDownloads();
|
||||
for (const task of tasks) {
|
||||
|
||||
26
components/settings/DisabledSetting.tsx
Normal file
26
components/settings/DisabledSetting.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import {View, ViewProps} from "react-native";
|
||||
import {Text} from "@/components/common/Text";
|
||||
|
||||
const DisabledSetting: React.FC<{disabled: boolean, showText?: boolean, text?: string} & ViewProps> = ({
|
||||
disabled = false,
|
||||
showText = true,
|
||||
text,
|
||||
children,
|
||||
...props
|
||||
}) => (
|
||||
<View
|
||||
pointerEvents={disabled ? "none" : "auto"}
|
||||
style={{
|
||||
opacity: disabled ? 0.5 : 1,
|
||||
}}
|
||||
>
|
||||
<View {...props}>
|
||||
{disabled && showText &&
|
||||
<Text className="text-center text-red-700 my-4">{text ?? "Currently disabled by admin."}</Text>
|
||||
}
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
export default DisabledSetting;
|
||||
@@ -1,33 +1,47 @@
|
||||
import { Stepper } from "@/components/inputs/Stepper";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { Settings, useSettings } from "@/utils/atoms/settings";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useRouter } from "expo-router";
|
||||
import React from "react";
|
||||
import { Switch, TouchableOpacity, View } from "react-native";
|
||||
import {Stepper} from "@/components/inputs/Stepper";
|
||||
import {useDownload} from "@/providers/DownloadProvider";
|
||||
import {DownloadMethod, Settings, useSettings} from "@/utils/atoms/settings";
|
||||
import {Ionicons} from "@expo/vector-icons";
|
||||
import {useQueryClient} from "@tanstack/react-query";
|
||||
import {useRouter} from "expo-router";
|
||||
import React, {useMemo} from "react";
|
||||
import {Switch, TouchableOpacity} from "react-native";
|
||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import {Text} from "../common/Text";
|
||||
import {ListGroup} from "../list/ListGroup";
|
||||
import {ListItem} from "../list/ListItem";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
|
||||
export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
const { setProcesses } = useDownload();
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const disabled = useMemo(() => (
|
||||
pluginSettings?.downloadMethod?.locked === true &&
|
||||
pluginSettings?.remuxConcurrentLimit?.locked === true &&
|
||||
pluginSettings?.autoDownload.locked === true
|
||||
), [pluginSettings])
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
return (
|
||||
<View {...props} className="mb-4">
|
||||
<DisabledSetting
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
className="mb-4"
|
||||
>
|
||||
<ListGroup title="Downloads">
|
||||
<ListItem title="Download method">
|
||||
<ListItem
|
||||
title="Download method"
|
||||
disabled={pluginSettings?.downloadMethod?.locked}
|
||||
>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
{settings.downloadMethod === "remux"
|
||||
{settings.downloadMethod === DownloadMethod.Remux
|
||||
? "Default"
|
||||
: "Optimized"}
|
||||
</Text>
|
||||
@@ -51,7 +65,7 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
<DropdownMenu.Item
|
||||
key="1"
|
||||
onSelect={() => {
|
||||
updateSettings({ downloadMethod: "remux" });
|
||||
updateSettings({ downloadMethod: DownloadMethod.Remux });
|
||||
setProcesses([]);
|
||||
}}
|
||||
>
|
||||
@@ -60,7 +74,7 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
<DropdownMenu.Item
|
||||
key="2"
|
||||
onSelect={() => {
|
||||
updateSettings({ downloadMethod: "optimized" });
|
||||
updateSettings({ downloadMethod: DownloadMethod.Optimized });
|
||||
setProcesses([]);
|
||||
queryClient.invalidateQueries({ queryKey: ["search"] });
|
||||
}}
|
||||
@@ -73,7 +87,7 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
|
||||
<ListItem
|
||||
title="Remux max download"
|
||||
disabled={settings.downloadMethod !== "remux"}
|
||||
disabled={pluginSettings?.remuxConcurrentLimit?.locked || settings.downloadMethod !== DownloadMethod.Remux}
|
||||
>
|
||||
<Stepper
|
||||
value={settings.remuxConcurrentLimit}
|
||||
@@ -90,22 +104,22 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
|
||||
<ListItem
|
||||
title="Auto download"
|
||||
disabled={settings.downloadMethod !== "optimized"}
|
||||
disabled={pluginSettings?.autoDownload?.locked || settings.downloadMethod !== DownloadMethod.Optimized}
|
||||
>
|
||||
<Switch
|
||||
disabled={settings.downloadMethod !== "optimized"}
|
||||
disabled={pluginSettings?.autoDownload?.locked || settings.downloadMethod !== DownloadMethod.Optimized}
|
||||
value={settings.autoDownload}
|
||||
onValueChange={(value) => updateSettings({ autoDownload: value })}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
disabled={settings.downloadMethod !== "optimized"}
|
||||
disabled={pluginSettings?.optimizedVersionsServerUrl?.locked || settings.downloadMethod !== DownloadMethod.Optimized}
|
||||
onPress={() => router.push("/settings/optimized-server/page")}
|
||||
showArrow
|
||||
title="Optimized Versions Server"
|
||||
></ListItem>
|
||||
</ListGroup>
|
||||
</View>
|
||||
</DisabledSetting>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export const JellyseerrSettings = () => {
|
||||
} = useJellyseerr();
|
||||
|
||||
const [user] = useAtom(userAtom);
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
|
||||
const [promptForJellyseerrPass, setPromptForJellyseerrPass] =
|
||||
useState<boolean>(false);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import {DownloadMethod, useSettings} from "@/utils/atoms/settings";
|
||||
import { getOrSetDeviceId } from "@/utils/device";
|
||||
import { useLog, writeToLog } from "@/utils/log";
|
||||
import {
|
||||
@@ -106,7 +106,7 @@ function useDownloadProvider() {
|
||||
const url = settings?.optimizedVersionsServerUrl;
|
||||
|
||||
if (
|
||||
settings?.downloadMethod !== "optimized" ||
|
||||
settings?.downloadMethod !== DownloadMethod.Optimized ||
|
||||
!url ||
|
||||
!deviceId ||
|
||||
!authHeader
|
||||
@@ -166,7 +166,7 @@ function useDownloadProvider() {
|
||||
},
|
||||
staleTime: 0,
|
||||
refetchInterval: 2000,
|
||||
enabled: settings?.downloadMethod === "optimized",
|
||||
enabled: settings?.downloadMethod === DownloadMethod.Optimized,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { atom, useAtom } from "jotai";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import {useCallback, useEffect, useMemo} from "react";
|
||||
import * as ScreenOrientation from "expo-screen-orientation";
|
||||
import { storage } from "../mmkv";
|
||||
import { Platform } from "react-native";
|
||||
@@ -12,7 +12,7 @@ import {apiAtom} from "@/providers/JellyfinProvider";
|
||||
import {getPluginsApi} from "@jellyfin/sdk/lib/utils/api";
|
||||
import {writeErrorLog} from "@/utils/log";
|
||||
|
||||
const STREAMYFIN_PLUGIN_ID = "1e9e5d38-6e67-4615-8719-e98a5c34f004"
|
||||
const STREAMYFIN_PLUGIN_ID = "1e9e5d386e6746158719e98a5c34f004"
|
||||
const STREAMYFIN_PLUGIN_SETTINGS = "STREAMYFIN_PLUGIN_SETTINGS"
|
||||
|
||||
export type DownloadQuality = "original" | "high" | "low";
|
||||
@@ -66,6 +66,11 @@ export type DefaultLanguageOption = {
|
||||
label: string;
|
||||
};
|
||||
|
||||
export enum DownloadMethod {
|
||||
Remux = "remux",
|
||||
Optimized = "optimized"
|
||||
}
|
||||
|
||||
export type Settings = {
|
||||
autoRotate?: boolean;
|
||||
forceLandscapeInVideoPlayer?: boolean;
|
||||
@@ -88,7 +93,7 @@ export type Settings = {
|
||||
forwardSkipTime: number;
|
||||
rewindSkipTime: number;
|
||||
optimizedVersionsServerUrl?: string | null;
|
||||
downloadMethod: "optimized" | "remux";
|
||||
downloadMethod: DownloadMethod;
|
||||
autoDownload: boolean;
|
||||
showCustomMenuLinks: boolean;
|
||||
disableHapticFeedback: boolean;
|
||||
@@ -100,7 +105,7 @@ export type Settings = {
|
||||
};
|
||||
|
||||
export interface Lockable<T> {
|
||||
lockable: boolean;
|
||||
locked: boolean;
|
||||
value: T
|
||||
}
|
||||
|
||||
@@ -138,7 +143,7 @@ const loadSettings = (): Settings => {
|
||||
forwardSkipTime: 30,
|
||||
rewindSkipTime: 10,
|
||||
optimizedVersionsServerUrl: null,
|
||||
downloadMethod: "remux",
|
||||
downloadMethod: DownloadMethod.Remux,
|
||||
autoDownload: false,
|
||||
showCustomMenuLinks: false,
|
||||
disableHapticFeedback: false,
|
||||
@@ -171,15 +176,15 @@ export const pluginSettingsAtom = atom(storage.get<PluginLockableSettings>(STREA
|
||||
|
||||
export const useSettings = () => {
|
||||
const [api] = useAtom(apiAtom);
|
||||
const [settings, setSettings] = useAtom(settingsAtom);
|
||||
const [_settings, setSettings] = useAtom(settingsAtom);
|
||||
const [pluginSettings, _setPluginSettings] = useAtom(pluginSettingsAtom);
|
||||
|
||||
useEffect(() => {
|
||||
if (settings === null) {
|
||||
if (_settings === null) {
|
||||
const loadedSettings = loadSettings();
|
||||
setSettings(loadedSettings);
|
||||
}
|
||||
}, [settings, setSettings]);
|
||||
}, [_settings, setSettings]);
|
||||
|
||||
const setPluginSettings = useCallback((settings: PluginLockableSettings | undefined) => {
|
||||
storage.setAny(STREAMYFIN_PLUGIN_SETTINGS, settings)
|
||||
@@ -217,6 +222,22 @@ export const useSettings = () => {
|
||||
[api]
|
||||
)
|
||||
|
||||
// We do not want to save over users pre-existing settings in case admin ever removes/unlocks a setting.
|
||||
const settings: Settings = useMemo(() => {
|
||||
const overrideSettings = Object.entries(pluginSettings || {})
|
||||
.reduce((acc, [key, value]) => {
|
||||
if (value) {
|
||||
acc = Object.assign(acc, {[key]: value.value})
|
||||
}
|
||||
return acc
|
||||
}, {} as Settings)
|
||||
|
||||
return {
|
||||
..._settings,
|
||||
...overrideSettings
|
||||
}
|
||||
}, [_settings, setSettings, pluginSettings, _setPluginSettings, setPluginSettings])
|
||||
|
||||
const updateSettings = (update: Partial<Settings>) => {
|
||||
if (settings) {
|
||||
const newSettings = { ...settings, ...update };
|
||||
|
||||
Reference in New Issue
Block a user