import { useDownload } from "@/providers/DownloadProvider"; import { apiAtom, getOrSetDeviceId, userAtom, } from "@/providers/JellyfinProvider"; import { ScreenOrientationEnum, Settings, useSettings, } from "@/utils/atoms/settings"; import { BACKGROUND_FETCH_TASK, registerBackgroundFetchAsync, unregisterBackgroundFetchAsync, } from "@/utils/background-tasks"; import { getStatistics } from "@/utils/optimize-server"; import { getItemsApi } from "@jellyfin/sdk/lib/utils/api"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import * as BackgroundFetch from "expo-background-fetch"; import * as ScreenOrientation from "expo-screen-orientation"; import * as TaskManager from "expo-task-manager"; import { useAtom } from "jotai"; import React, {useCallback, useEffect, useState} from "react"; import { Alert, Linking, Switch, TouchableOpacity, View, ViewProps, } from "react-native"; import { toast } from "sonner-native"; import * as DropdownMenu from "zeego/dropdown-menu"; import { Button } from "../Button"; import { Input } from "../common/Input"; import { Text } from "../common/Text"; import { Loader } from "../Loader"; import { MediaToggles } from "./MediaToggles"; import { Stepper } from "@/components/inputs/Stepper"; import { MediaProvider } from "./MediaContext"; import { SubtitleToggles } from "./SubtitleToggles"; import { AudioToggles } from "./AudioToggles"; import {JellyseerrApi, useJellyseerr} from "@/hooks/useJellyseerr"; import {ListItem} from "@/components/ListItem"; interface Props extends ViewProps {} export const SettingToggles: React.FC = ({ ...props }) => { const [settings, updateSettings] = useSettings(); const { setProcesses } = useDownload(); const { jellyseerrApi, jellyseerrUser, setJellyseerrUser , clearAllJellyseerData } = useJellyseerr(); const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const [marlinUrl, setMarlinUrl] = useState(""); const [jellyseerrPassword, setJellyseerrPassword] = useState(undefined); const [optimizedVersionsServerUrl, setOptimizedVersionsServerUrl] = useState(settings?.optimizedVersionsServerUrl || ""); const [jellyseerrServerUrl, setjellyseerrServerUrl] = useState(settings?.jellyseerrServerUrl || undefined); const queryClient = useQueryClient(); /******************** * Background task *******************/ const checkStatusAsync = async () => { await BackgroundFetch.getStatusAsync(); return await TaskManager.isTaskRegisteredAsync(BACKGROUND_FETCH_TASK); }; useEffect(() => { (async () => { const registered = await checkStatusAsync(); if (settings?.autoDownload === true && !registered) { registerBackgroundFetchAsync(); toast.success("Background downloads enabled"); } else if (settings?.autoDownload === false && registered) { unregisterBackgroundFetchAsync(); toast.info("Background downloads disabled"); } else if (settings?.autoDownload === true && registered) { // Don't to anything } else if (settings?.autoDownload === false && !registered) { // Don't to anything } else { updateSettings({ autoDownload: false }); } })(); }, [settings?.autoDownload]); /********************** *********************/ const { data: mediaListCollections, isLoading: isLoadingMediaListCollections, } = useQuery({ queryKey: ["sf_promoted", user?.Id, settings?.usePopularPlugin], queryFn: async () => { if (!api || !user?.Id) return []; const response = await getItemsApi(api).getItems({ userId: user.Id, tags: ["sf_promoted"], recursive: true, fields: ["Tags"], includeItemTypes: ["BoxSet"], }); return response.data.Items ?? []; }, enabled: !!api && !!user?.Id && settings?.usePopularPlugin === true, staleTime: 0, }); const promptForJellyseerrLogin = useCallback(() => Alert.prompt( 'Enter jellyfin password', `Enter password for jellyfin user ${user?.Name}`, (input) => setJellyseerrPassword(input), 'secure-text' ), [user, setJellyseerrPassword] ); const testJellyseerrServerUrl = useCallback(async () => { if (!jellyseerrServerUrl || jellyseerrApi) return; const jellyseerrTempApi = new JellyseerrApi(jellyseerrServerUrl); jellyseerrTempApi.test().then(result => { if (result.isValid) { if (result.requiresPass) promptForJellyseerrLogin() else updateSettings({jellyseerrServerUrl}) } else { setjellyseerrServerUrl(undefined); clearAllJellyseerData(); } }) }, [jellyseerrServerUrl]) useEffect(() => { if (jellyseerrServerUrl && user?.Name && jellyseerrPassword) { const jellyseerrTempApi = new JellyseerrApi(jellyseerrServerUrl); jellyseerrTempApi.login(user?.Name, jellyseerrPassword) .then(user => { setJellyseerrUser(user); updateSettings({jellyseerrServerUrl}) }) .finally(() => { setJellyseerrPassword(undefined); }) } }, [user, jellyseerrServerUrl, jellyseerrPassword]); if (!settings) return null; return ( {/* Look and feel Coming soon Options for changing the look and feel of the app. */} Other Auto rotate Important on android since the video player orientation is locked to the app orientation. updateSettings({ autoRotate: value })} /> Video orientation Set the full screen video player orientation. {ScreenOrientationEnum[settings.defaultVideoOrientation]} Orientation { updateSettings({ defaultVideoOrientation: ScreenOrientation.OrientationLock.DEFAULT, }); }} > { ScreenOrientationEnum[ ScreenOrientation.OrientationLock.DEFAULT ] } { updateSettings({ defaultVideoOrientation: ScreenOrientation.OrientationLock.PORTRAIT_UP, }); }} > { ScreenOrientationEnum[ ScreenOrientation.OrientationLock.PORTRAIT_UP ] } { updateSettings({ defaultVideoOrientation: ScreenOrientation.OrientationLock.LANDSCAPE_LEFT, }); }} > { ScreenOrientationEnum[ ScreenOrientation.OrientationLock.LANDSCAPE_LEFT ] } { updateSettings({ defaultVideoOrientation: ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT, }); }} > { ScreenOrientationEnum[ ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT ] } Safe area in controls Enable safe area in video player controls updateSettings({ safeAreaInControlsEnabled: value }) } /> Use popular lists plugin Made by: lostb1t { Linking.openURL( "https://github.com/lostb1t/jellyfin-plugin-media-lists" ); }} > More info updateSettings({ usePopularPlugin: value }) } /> {settings.usePopularPlugin && ( {mediaListCollections?.map((mlc) => ( {mlc.Name} { if (!settings.mediaListCollectionIds) { updateSettings({ mediaListCollectionIds: [mlc.Id!], }); return; } updateSettings({ mediaListCollectionIds: settings.mediaListCollectionIds.includes(mlc.Id!) ? settings.mediaListCollectionIds.filter( (id) => id !== mlc.Id ) : [...settings.mediaListCollectionIds, mlc.Id!], }); }} /> ))} {isLoadingMediaListCollections && ( )} {mediaListCollections?.length === 0 && ( No collections found. Add some in Jellyfin. )} )} Search engine Choose the search engine you want to use. {settings.searchEngine} Profiles { updateSettings({ searchEngine: "Jellyfin" }); queryClient.invalidateQueries({ queryKey: ["search"] }); }} > Jellyfin { updateSettings({ searchEngine: "Marlin" }); queryClient.invalidateQueries({ queryKey: ["search"] }); }} > Marlin {settings.searchEngine === "Marlin" && ( setMarlinUrl(text)} /> {settings.marlinServerUrl && ( Current: {settings.marlinServerUrl} )} )} Show Custom Menu Links Show custom menu links defined inside your Jellyfin web config.json file Linking.openURL( "https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links" ) } > More info updateSettings({ showCustomMenuLinks: value }) } /> Downloads Download method Choose the download method to use. Optimized requires the optimized server. {settings.downloadMethod === "remux" ? "Default" : "Optimized"} Methods { updateSettings({ downloadMethod: "remux" }); setProcesses([]); }} > Default { updateSettings({ downloadMethod: "optimized" }); setProcesses([]); queryClient.invalidateQueries({ queryKey: ["search"] }); }} > Optimized Remux max download This is the total media you want to be able to download at the same time. updateSettings({ remuxConcurrentLimit: value as Settings["remuxConcurrentLimit"], }) } /> Auto download This will automatically download the media file when it's finished optimizing on the server. updateSettings({ autoDownload: value })} /> Optimized versions server Set the URL for the optimized versions server for downloads. setOptimizedVersionsServerUrl(text)} /> Jellyseerr {jellyseerrUser && <> } Server URL Set the URL for your jellyseerr instance. This integration is in its early stages. Expect things to change. ); };