diff --git a/augmentations/mmkv.ts b/augmentations/mmkv.ts index 80fbeede..5667502f 100644 --- a/augmentations/mmkv.ts +++ b/augmentations/mmkv.ts @@ -13,5 +13,10 @@ MMKV.prototype.get = function (key: string): T | undefined { } MMKV.prototype.setAny = function (key: string, value: any | undefined): void { - this.set(key, JSON.stringify(value)); + if (value === undefined) { + this.delete(key) + } + else { + this.set(key, JSON.stringify(value)); + } } \ No newline at end of file diff --git a/providers/JellyfinProvider.tsx b/providers/JellyfinProvider.tsx index f4ccce75..2b602323 100644 --- a/providers/JellyfinProvider.tsx +++ b/providers/JellyfinProvider.tsx @@ -1,3 +1,4 @@ +import "@/augmentations"; import { useInterval } from "@/hooks/useInterval"; import { storage } from "@/utils/mmkv"; import { Api, Jellyfin } from "@jellyfin/sdk"; @@ -19,7 +20,8 @@ import React, { import { Platform } from "react-native"; import uuid from "react-native-uuid"; import { getDeviceName } from "react-native-device-info"; -import { toast } from "sonner-native"; +import { useSettings } from "@/utils/atoms/settings"; +import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr"; interface Server { address: string; @@ -70,6 +72,8 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({ const [user, setUser] = useAtom(userAtom); const [isPolling, setIsPolling] = useState(false); const [secret, setSecret] = useState(null); + const [settings, updateSettings, pluginSettings, setPluginSettings, refreshStreamyfinPluginSettings] = useSettings(); + const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr(); useQuery({ queryKey: ["user", api], @@ -226,6 +230,16 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({ storage.set("user", JSON.stringify(auth.data.User)); setApi(jellyfin.createApi(api?.basePath, auth.data?.AccessToken)); storage.set("token", auth.data?.AccessToken); + + const recentPluginSettings = await refreshStreamyfinPluginSettings(); + if (recentPluginSettings?.jellyseerrServerUrl?.value) { + const jellyseerrApi = new JellyseerrApi(recentPluginSettings.jellyseerrServerUrl.value); + await jellyseerrApi.test().then((result) => { + if (result.isValid && result.requiresPass) { + jellyseerrApi.login(username, password).then(setJellyseerrUser); + } + }) + } } } catch (error) { if (axios.isAxiosError(error)) { @@ -262,6 +276,8 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({ mutationFn: async () => { storage.delete("token"); setUser(null); + setPluginSettings(undefined); + await clearAllJellyseerData(); }, onError: (error) => { console.error("Logout failed:", error); diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts index b473198d..eac6826e 100644 --- a/utils/atoms/settings.ts +++ b/utils/atoms/settings.ts @@ -1,12 +1,19 @@ import { atom, useAtom } from "jotai"; -import { useEffect } from "react"; +import { useCallback, useEffect } from "react"; import * as ScreenOrientation from "expo-screen-orientation"; import { storage } from "../mmkv"; import { Platform } from "react-native"; import { CultureDto, + PluginStatus, SubtitlePlaybackMode, } from "@jellyfin/sdk/lib/generated-client"; +import {apiAtom} from "@/providers/JellyfinProvider"; +import {getPluginsApi} from "@jellyfin/sdk/lib/utils/api"; +import {writeErrorLog} from "@/utils/log"; +import {AUTHORIZATION_HEADER} from "@jellyfin/sdk"; + +const STREAMYFIN_PLUGIN_SETTINGS = "STREAMYFIN_PLUGIN_SETTINGS" export type DownloadQuality = "original" | "high" | "low"; @@ -92,6 +99,13 @@ export type Settings = { hiddenLibraries?: string[]; }; +export interface Lockable { + lockable: boolean; + value: T +} + +export type PluginLockableSettings = { [K in keyof Settings]: Lockable }; + const loadSettings = (): Settings => { const defaultValues: Settings = { autoRotate: true, @@ -150,9 +164,12 @@ const saveSettings = (settings: Settings) => { }; export const settingsAtom = atom(null); +export const pluginSettingsAtom = atom(storage.get(STREAMYFIN_PLUGIN_SETTINGS)); export const useSettings = () => { + const [api] = useAtom(apiAtom); const [settings, setSettings] = useAtom(settingsAtom); + const [pluginSettings, _setPluginSettings] = useAtom(pluginSettingsAtom); useEffect(() => { if (settings === null) { @@ -161,6 +178,45 @@ export const useSettings = () => { } }, [settings, setSettings]); + const setPluginSettings = useCallback((settings: PluginLockableSettings | undefined) => { + storage.setAny(STREAMYFIN_PLUGIN_SETTINGS, settings) + _setPluginSettings(settings) + }, + [_setPluginSettings] + ) + + const refreshStreamyfinPluginSettings = useCallback( + async () => { + if (!api) + return + + const plugins = await getPluginsApi(api).getPlugins().then(({data}) => data); + + if (plugins && plugins.length > 0) { + const streamyfinPlugin = plugins.find(plugin => plugin.Name === "Streamyfin"); + + if (streamyfinPlugin?.Status != PluginStatus.Active) { + writeErrorLog( + "Streamyfin plugin is currently not active.\n" + + `Current status is: ${streamyfinPlugin?.Status}` + ); + setPluginSettings(undefined); + return; + } + + const settings = await api.axiosInstance + .get(`${api.basePath}/Streamyfin/config`, { headers: { [AUTHORIZATION_HEADER]: api.authorizationHeader } }) + .then(response => { + return response.data['settings'] as PluginLockableSettings + }) + + setPluginSettings(settings); + return settings; + } + }, + [api] + ) + const updateSettings = (update: Partial) => { if (settings) { const newSettings = { ...settings, ...update }; @@ -170,5 +226,5 @@ export const useSettings = () => { } }; - return [settings, updateSettings] as const; + return [settings, updateSettings, pluginSettings, setPluginSettings, refreshStreamyfinPluginSettings] as const; };