import type { CultureDto, UserConfiguration, UserDto, } from "@jellyfin/sdk/lib/generated-client/models"; import { getLocalizationApi, getUserApi } from "@jellyfin/sdk/lib/utils/api"; import { useQuery } from "@tanstack/react-query"; import { useAtomValue } from "jotai"; import { createContext, type ReactNode, useContext, useEffect } from "react"; import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient"; import { apiAtom } from "@/providers/JellyfinProvider"; import { type Settings, useSettings } from "@/utils/atoms/settings"; interface MediaContextType { settings: Settings | null; updateSettings: (update: Partial) => void; user: UserDto | undefined; cultures: CultureDto[]; } const MediaContext = createContext(undefined); export const useMedia = () => { const context = useContext(MediaContext); if (!context) { throw new Error("useMedia must be used within a MediaProvider"); } return context; }; export const MediaProvider = ({ children }: { children: ReactNode }) => { const { settings, updateSettings } = useSettings(); const api = useAtomValue(apiAtom); const queryClient = useNetworkAwareQueryClient(); const updateSetingsWrapper = (update: Partial) => { const updateUserConfiguration = async ( update: Partial, ) => { if (api && user) { try { await getUserApi(api).updateUserConfiguration({ userConfiguration: { ...user.Configuration, ...update, }, }); queryClient.invalidateQueries({ queryKey: ["authUser"] }); } catch (_error) {} } }; updateSettings(update); const updatePayload = { SubtitleMode: update?.subtitleMode ?? settings?.subtitleMode, PlayDefaultAudioTrack: update?.playDefaultAudioTrack ?? settings?.playDefaultAudioTrack, RememberAudioSelections: update?.rememberAudioSelections ?? settings?.rememberAudioSelections, RememberSubtitleSelections: update?.rememberSubtitleSelections ?? settings?.rememberSubtitleSelections, } as Partial; updatePayload.AudioLanguagePreference = update?.defaultAudioLanguage === null ? "" : update?.defaultAudioLanguage?.ThreeLetterISOLanguageName || settings?.defaultAudioLanguage?.ThreeLetterISOLanguageName || ""; updatePayload.SubtitleLanguagePreference = update?.defaultSubtitleLanguage === null ? "" : update?.defaultSubtitleLanguage?.ThreeLetterISOLanguageName || settings?.defaultSubtitleLanguage?.ThreeLetterISOLanguageName || ""; updateUserConfiguration(updatePayload); }; const { data: user } = useQuery({ queryKey: ["authUser"], queryFn: async () => { if (!api) return; const userApi = await getUserApi(api).getCurrentUser(); return userApi.data; }, enabled: !!api, staleTime: 0, }); const { data: cultures = [], isFetched: isCulturesFetched } = useQuery({ queryKey: ["cultures"], queryFn: async () => { if (!api) return []; const localizationApi = await getLocalizationApi(api).getCultures(); const cultures = localizationApi.data; return cultures; }, enabled: !!api, staleTime: 43200000, // 12 hours }); // Set default settings from user configuration.s useEffect(() => { if (!user || cultures.length === 0) return; const userSubtitlePreference = user?.Configuration?.SubtitleLanguagePreference; const userAudioPreference = user?.Configuration?.AudioLanguagePreference; const subtitlePreference = cultures.find( (x) => x.ThreeLetterISOLanguageName === userSubtitlePreference, ); const audioPreference = cultures.find( (x) => x.ThreeLetterISOLanguageName === userAudioPreference, ); updateSettings({ defaultSubtitleLanguage: subtitlePreference, defaultAudioLanguage: audioPreference, subtitleMode: user?.Configuration?.SubtitleMode, playDefaultAudioTrack: user?.Configuration?.PlayDefaultAudioTrack, rememberAudioSelections: user?.Configuration?.RememberAudioSelections, rememberSubtitleSelections: user?.Configuration?.RememberSubtitleSelections, }); }, [user, isCulturesFetched]); if (!api) return null; return ( {children} ); };