Merge branch 'develop' into feat/i18n

This commit is contained in:
Simon Caron
2025-01-12 19:49:58 -05:00
87 changed files with 1703 additions and 2433 deletions

View File

@@ -48,5 +48,20 @@ export const useJellyseerrCanRequest = (
return userHasPermission && !canNotRequest;
}, [item, jellyseerrUser]);
return canRequest;
const hasAdvancedRequestPermission = useMemo(() => {
if (!jellyseerrUser) return false;
return hasPermission(
[
Permission.REQUEST_ADVANCED,
Permission.MANAGE_REQUESTS
],
jellyseerrUser.permissions,
{type: 'or'}
)
},
[jellyseerrUser]
);
return [canRequest, hasAdvancedRequestPermission];
};

View File

@@ -1,13 +1,19 @@
import { atom, useAtom } from "jotai";
import { useEffect } from "react";
import { getLocales } from "expo-localization";
import {useCallback, useEffect, useMemo} 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";
const STREAMYFIN_PLUGIN_ID = "1e9e5d386e6746158719e98a5c34f004"
const STREAMYFIN_PLUGIN_SETTINGS = "STREAMYFIN_PLUGIN_SETTINGS"
export type DownloadQuality = "original" | "high" | "low";
@@ -60,6 +66,11 @@ export type DefaultLanguageOption = {
label: string;
};
export enum DownloadMethod {
Remux = "remux",
Optimized = "optimized"
}
export type Settings = {
autoRotate?: boolean;
forceLandscapeInVideoPlayer?: boolean;
@@ -83,7 +94,7 @@ export type Settings = {
forwardSkipTime: number;
rewindSkipTime: number;
optimizedVersionsServerUrl?: string | null;
downloadMethod: "optimized" | "remux";
downloadMethod: DownloadMethod;
autoDownload: boolean;
showCustomMenuLinks: boolean;
disableHapticFeedback: boolean;
@@ -94,6 +105,16 @@ export type Settings = {
hiddenLibraries?: string[];
};
export interface Lockable<T> {
locked: boolean;
value: T
}
export type PluginLockableSettings = { [K in keyof Settings]: Lockable<Settings[K]> };
export type StreamyfinPluginConfig = {
settings: PluginLockableSettings
}
const loadSettings = (): Settings => {
const defaultValues: Settings = {
autoRotate: true,
@@ -124,7 +145,7 @@ const loadSettings = (): Settings => {
forwardSkipTime: 30,
rewindSkipTime: 10,
optimizedVersionsServerUrl: null,
downloadMethod: "remux",
downloadMethod: DownloadMethod.Remux,
autoDownload: false,
showCustomMenuLinks: false,
disableHapticFeedback: false,
@@ -153,16 +174,76 @@ const saveSettings = (settings: Settings) => {
};
export const settingsAtom = atom<Settings | null>(null);
export const pluginSettingsAtom = atom(storage.get<PluginLockableSettings>(STREAMYFIN_PLUGIN_SETTINGS));
export const useSettings = () => {
const [settings, setSettings] = useAtom(settingsAtom);
const [api] = useAtom(apiAtom);
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)
_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.Id === STREAMYFIN_PLUGIN_ID);
if (!streamyfinPlugin || streamyfinPlugin.Status != PluginStatus.Active) {
writeErrorLog(
"Streamyfin plugin is currently not active.\n" +
`Current status is: ${streamyfinPlugin?.Status}`
);
setPluginSettings(undefined);
return;
}
const settings = await api.getStreamyfinPluginConfig()
.then(({data}) => data.settings)
setPluginSettings(settings);
return settings;
}
},
[api]
)
// We do not want to save over users pre-existing settings in case admin ever removes/unlocks a setting.
// If admin sets locked to false but provides a value,
// use user settings first and fallback on admin setting if required.
const settings: Settings = useMemo(() => {
const overrideSettings = Object.entries(pluginSettings || {})
.reduce((acc, [key, setting]) => {
if (setting) {
const {value, locked} = setting
acc = Object.assign(acc, {
[key]: locked ? value : _settings?.[key as keyof Settings] ?? value
})
}
return acc
}, {} as Settings)
return {
..._settings,
...overrideSettings
}
}, [_settings, setSettings, pluginSettings, _setPluginSettings, setPluginSettings])
const updateSettings = (update: Partial<Settings>) => {
if (settings) {
@@ -173,5 +254,5 @@ export const useSettings = () => {
}
};
return [settings, updateSettings] as const;
return [settings, updateSettings, pluginSettings, setPluginSettings, refreshStreamyfinPluginSettings] as const;
};

View File

@@ -10,8 +10,6 @@ import {
* readonly Unknown: "unknown";
readonly Movies: "movies";
readonly Tvshows: "tvshows";
readonly Music: "music";
readonly Musicvideos: "musicvideos";
readonly Trailers: "trailers";
readonly Homevideos: "homevideos";
readonly Boxsets: "boxsets";
@@ -33,8 +31,6 @@ export const colletionTypeToItemType = (
return BaseItemKind.Series;
case CollectionType.Homevideos:
return BaseItemKind.Video;
case CollectionType.Musicvideos:
return BaseItemKind.MusicVideo;
case CollectionType.Books:
return BaseItemKind.Book;
case CollectionType.Playlists: