From 200d3e69ac5f17c55415cec7ea53a5a96953a22a Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:59:13 -0600 Subject: [PATCH] add low performance mode --- src/assets/locales/en.json | 5 +- src/backend/accounts/settings.ts | 2 + src/components/LinksDropdown.tsx | 13 ++- src/components/layout/Navigation.tsx | 62 +++++++------ src/components/media/MediaCard.tsx | 6 +- .../player/internals/InfoButton.tsx | 9 ++ src/components/utils/Lightbar.tsx | 4 +- src/hooks/auth/useAuthData.ts | 9 ++ src/hooks/useLowPerformanceMode.ts | 16 ++++ src/hooks/useSettingsState.ts | 16 +++- src/pages/HomePage.tsx | 10 ++- src/pages/Settings.tsx | 17 +++- src/pages/parts/settings/AppearancePart.tsx | 67 +++++++++++--- src/pages/parts/settings/PreferencesPart.tsx | 88 ++++++++++++++----- src/setup/App.tsx | 2 + src/stores/preferences/index.tsx | 8 ++ 16 files changed, 261 insertions(+), 73 deletions(-) create mode 100644 src/hooks/useLowPerformanceMode.ts diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 5548ccfa..5a7f6ba6 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -961,6 +961,7 @@ } }, "preferences": { + "title": "Preferences", "language": "Application language", "languageDescription": "Language applied to the entire application, only English has silly stuff 🙁.", "thumbnail": "Generate thumbnails", @@ -972,9 +973,11 @@ "skipCredits": "Skip End Credits", "skipCreditsDescription": "When enabled, automatically play the next episode at 99% completion to skip end credits. When disabled, wait until the episode is fully completed.", "skipCreditsLabel": "Skip end credits", + "lowPerformanceMode": "Low performance/bandwidth mode", + "lowPerformanceModeDescription": "Optimizes the application for slower connections and devices by disabling bandwidth-heavy features. This mode reduces data usage and improves performance while keeping the core search and watch functionality intact. ", + "lowPerformanceModeLabel": "Low performance mode", "sourceOrder": "Reordering sources", "sourceOrderDescription": "Drag and drop to reorder sources. This will determine the order in which sources are checked for the media you are trying to watch. If a source is greyed out, it means the extension is required for that source.

(The default order is best for most users)", - "title": "Preferences", "sourceOrderEnableLabel": "Custom source order" }, "reset": "Reset", diff --git a/src/backend/accounts/settings.ts b/src/backend/accounts/settings.ts index dc747e62..86f6af2a 100644 --- a/src/backend/accounts/settings.ts +++ b/src/backend/accounts/settings.ts @@ -22,6 +22,7 @@ export interface SettingsInput { sourceOrder?: string[]; enableSourceOrder?: boolean; proxyTmdb?: boolean; + enableLowPerformanceMode?: boolean; } export interface SettingsResponse { @@ -42,6 +43,7 @@ export interface SettingsResponse { sourceOrder?: string[]; enableSourceOrder?: boolean; proxyTmdb?: boolean; + enableLowPerformanceMode?: boolean; } export function updateSettings( diff --git a/src/components/LinksDropdown.tsx b/src/components/LinksDropdown.tsx index 43e88d71..5b1329d6 100644 --- a/src/components/LinksDropdown.tsx +++ b/src/components/LinksDropdown.tsx @@ -13,6 +13,7 @@ import { useAuth } from "@/hooks/auth/useAuth"; import { useBackendUrl } from "@/hooks/auth/useBackendUrl"; import { conf } from "@/setup/config"; import { useAuthStore } from "@/stores/auth"; +import { usePreferencesStore } from "@/stores/preferences"; function Divider() { return
; @@ -227,6 +228,10 @@ export function LinksDropdown(props: { children: React.ReactNode }) { setOpen((s) => !s); }, []); + const enableLowPerformanceMode = usePreferencesStore( + (s) => s.enableLowPerformanceMode, + ); + return (
{t("navigation.menu.about")} - - {t("navigation.menu.discover")} - + {!enableLowPerformanceMode && ( + + {t("navigation.menu.discover")} + + )} {deviceName ? ( s.enableLowPerformanceMode, + ); + return ( <> {/* lightbar */} @@ -63,7 +68,7 @@ export function Navigation(props: NavigationProps) { }} >
- +
) : null} @@ -149,33 +154,34 @@ export function Navigation(props: NavigationProps) { navigation /> - {window.location.pathname !== "/discover" ? ( - handleClick("/discover")} - rel="noreferrer" - className="text-xl text-white tabbable rounded-full backdrop-blur-lg" - > - - - ) : ( - handleClick("/")} - rel="noreferrer" - className="text-lg text-white tabbable rounded-full backdrop-blur-lg" - > - - - )} + {!enableLowPerformanceMode && + (window.location.pathname !== "/discover" ? ( + handleClick("/discover")} + rel="noreferrer" + className="text-xl text-white tabbable rounded-full backdrop-blur-lg" + > + + + ) : ( + handleClick("/")} + rel="noreferrer" + className="text-lg text-white tabbable rounded-full backdrop-blur-lg" + > + + + ))}
diff --git a/src/components/media/MediaCard.tsx b/src/components/media/MediaCard.tsx index 95f69755..551cda33 100644 --- a/src/components/media/MediaCard.tsx +++ b/src/components/media/MediaCard.tsx @@ -67,6 +67,10 @@ function MediaCardContent({ const [searchQuery] = useSearchQuery(); + const enableLowPerformanceMode = usePreferencesStore( + (state) => state.enableLowPerformanceMode, + ); + if (isReleased() && media.year) { dotListContent.push(media.year.toFixed()); } @@ -190,7 +194,7 @@ function MediaCardContent({
- {!closable && ( + {!closable && !enableLowPerformanceMode && (
@@ -457,6 +471,7 @@ export function SettingsPage() { setEnableCarouselView={state.enableCarouselView.set} forceCompactEpisodeView={state.forceCompactEpisodeView.state} setForceCompactEpisodeView={state.forceCompactEpisodeView.set} + enableLowPerformanceMode={state.enableLowPerformanceMode.state} />
diff --git a/src/pages/parts/settings/AppearancePart.tsx b/src/pages/parts/settings/AppearancePart.tsx index 78dbf882..e7645d6e 100644 --- a/src/pages/parts/settings/AppearancePart.tsx +++ b/src/pages/parts/settings/AppearancePart.tsx @@ -219,6 +219,8 @@ export function AppearancePart(props: { forceCompactEpisodeView: boolean; setForceCompactEpisodeView: (v: boolean) => void; + + enableLowPerformanceMode: boolean; }) { const { t } = useTranslation(); @@ -227,6 +229,33 @@ export function AppearancePart(props: { const [isAtTop, setIsAtTop] = useState(true); const [isAtBottom, setIsAtBottom] = useState(false); + const { + enableLowPerformanceMode, + setEnableDiscover, + setEnableFeatured, + setEnableDetailsModal, + setEnableImageLogos, + setForceCompactEpisodeView, + } = props; + + // Apply low performance mode restrictions + useEffect(() => { + if (enableLowPerformanceMode) { + setEnableDiscover(false); + setEnableFeatured(false); + setEnableDetailsModal(false); + setEnableImageLogos(false); + setForceCompactEpisodeView(true); + } + }, [ + enableLowPerformanceMode, + setEnableDiscover, + setEnableFeatured, + setEnableDetailsModal, + setEnableImageLogos, + setForceCompactEpisodeView, + ]); + const checkScrollPosition = () => { const container = carouselRef.current; if (!container) return; @@ -285,13 +314,20 @@ export function AppearancePart(props: {

{ - const newDiscoverValue = !props.enableDiscover; - props.setEnableDiscover(newDiscoverValue); - if (!newDiscoverValue) { - props.setEnableFeatured(false); + if (!props.enableLowPerformanceMode) { + const newDiscoverValue = !props.enableDiscover; + props.setEnableDiscover(newDiscoverValue); + if (!newDiscoverValue) { + props.setEnableFeatured(false); + } } }} - className="bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg" + className={classNames( + "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg", + props.enableLowPerformanceMode + ? "cursor-not-allowed opacity-50 pointer-events-none" + : "cursor-pointer opacity-100 pointer-events-auto", + )} >

@@ -300,7 +336,7 @@ export function AppearancePart(props: {

{/* Featured Carousel */} - {props.enableDiscover && ( + {props.enableDiscover && !props.enableLowPerformanceMode && (

{t("settings.appearance.options.featured")} @@ -329,11 +365,14 @@ export function AppearancePart(props: {

+ !props.enableLowPerformanceMode && props.setEnableDetailsModal(!props.enableDetailsModal) } className={classNames( "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg", - "cursor-pointer opacity-100 pointer-events-auto", + props.enableLowPerformanceMode + ? "cursor-not-allowed opacity-50 pointer-events-none" + : "cursor-pointer opacity-100 pointer-events-auto", )} > @@ -356,10 +395,15 @@ export function AppearancePart(props: { {t("settings.appearance.options.logosNotice")}

props.setEnableImageLogos(!props.enableImageLogos)} + onClick={() => + !props.enableLowPerformanceMode && + props.setEnableImageLogos(!props.enableImageLogos) + } className={classNames( "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg", - "cursor-pointer opacity-100 pointer-events-auto", + props.enableLowPerformanceMode + ? "cursor-not-allowed opacity-50 pointer-events-none" + : "cursor-pointer opacity-100 pointer-events-auto", )} > @@ -405,11 +449,14 @@ export function AppearancePart(props: {

+ !props.enableLowPerformanceMode && props.setForceCompactEpisodeView(!props.forceCompactEpisodeView) } className={classNames( "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg", - "cursor-pointer opacity-100 pointer-events-auto", + props.enableLowPerformanceMode + ? "cursor-not-allowed opacity-50 pointer-events-none" + : "cursor-pointer opacity-100 pointer-events-auto", )} > diff --git a/src/pages/parts/settings/PreferencesPart.tsx b/src/pages/parts/settings/PreferencesPart.tsx index 6a6f90ef..17c74497 100644 --- a/src/pages/parts/settings/PreferencesPart.tsx +++ b/src/pages/parts/settings/PreferencesPart.tsx @@ -27,6 +27,8 @@ export function PreferencesPart(props: { setSourceOrder: (v: string[]) => void; enableSourceOrder: boolean; setenableSourceOrder: (v: boolean) => void; + enableLowPerformanceMode: boolean; + setEnableLowPerformanceMode: (v: boolean) => void; }) { const { t } = useTranslation(); const sorted = sortLangCodes(appLanguageOptions.map((item) => item.code)); @@ -58,6 +60,17 @@ export function PreferencesPart(props: { const navigate = useNavigate(); + const handleLowPerformanceModeToggle = () => { + const newMode = !props.enableLowPerformanceMode; + props.setEnableLowPerformanceMode(newMode); + + // When enabling low performance mode, disable bandwidth-heavy features + if (newMode) { + props.setEnableThumbnails(false); + props.setEnableAutoplay(false); + } + }; + return (
{t("settings.preferences.title")} @@ -89,8 +102,17 @@ export function PreferencesPart(props: { {t("settings.preferences.thumbnailDescription")}

props.setEnableThumbnails(!props.enableThumbnails)} - className="bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg" + onClick={() => { + if (!props.enableLowPerformanceMode) { + props.setEnableThumbnails(!props.enableThumbnails); + } + }} + className={classNames( + "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg", + props.enableLowPerformanceMode + ? "cursor-not-allowed opacity-50 pointer-events-none" + : "cursor-pointer opacity-100 pointer-events-auto", + )} >

@@ -109,13 +131,13 @@ export function PreferencesPart(props: {

- allowAutoplay + allowAutoplay && !props.enableLowPerformanceMode ? props.setEnableAutoplay(!props.enableAutoplay) : null } className={classNames( "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg", - allowAutoplay + allowAutoplay && !props.enableLowPerformanceMode ? "cursor-pointer opacity-100 pointer-events-auto" : "cursor-not-allowed opacity-50 pointer-events-none", )} @@ -127,27 +149,47 @@ export function PreferencesPart(props: {
{/* Skip End Credits Preference */} - {props.enableAutoplay && allowAutoplay && ( -
-

- {t("settings.preferences.skipCredits")} -

-

- {t("settings.preferences.skipCreditsDescription")} -

-
- props.setEnableSkipCredits(!props.enableSkipCredits) - } - className="bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg" - > - -

- {t("settings.preferences.skipCreditsLabel")} + {props.enableAutoplay && + allowAutoplay && + !props.enableLowPerformanceMode && ( +

+

+ {t("settings.preferences.skipCredits")}

+

+ {t("settings.preferences.skipCreditsDescription")} +

+
+ props.setEnableSkipCredits(!props.enableSkipCredits) + } + className="bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg" + > + +

+ {t("settings.preferences.skipCreditsLabel")} +

+
-
- )} + )} +
+ {/* Low Performance Mode */} +
+

+ {t("settings.preferences.lowPerformanceMode")} +

+

+ {t("settings.preferences.lowPerformanceModeDescription")} +

+
+ +

+ {t("settings.preferences.lowPerformanceModeLabel")} +

+
diff --git a/src/setup/App.tsx b/src/setup/App.tsx index 601f94b8..8d754c01 100644 --- a/src/setup/App.tsx +++ b/src/setup/App.tsx @@ -11,6 +11,7 @@ import { import { convertLegacyUrl, isLegacyUrl } from "@/backend/metadata/getmeta"; import { generateQuickSearchMediaUrl } from "@/backend/metadata/tmdb"; +import { useLowPerformanceMode } from "@/hooks/useLowPerformanceMode"; import { useOnlineListener } from "@/hooks/usePing"; import { AboutPage } from "@/pages/About"; import { AdminPage } from "@/pages/admin/AdminPage"; @@ -98,6 +99,7 @@ export const maintenanceTime = "March 31th 11:00 PM - 5:00 AM EST"; function App() { useHistoryListener(); useOnlineListener(); + useLowPerformanceMode(); // Check for ?lp parameter in URL const maintenance = false; // Shows maintance page const [showDowntime, setShowDowntime] = useState(maintenance); diff --git a/src/stores/preferences/index.tsx b/src/stores/preferences/index.tsx index f470b777..60387c15 100644 --- a/src/stores/preferences/index.tsx +++ b/src/stores/preferences/index.tsx @@ -17,6 +17,7 @@ export interface PreferencesStore { proxyTmdb: boolean; febboxKey: string | null; realDebridKey: string | null; + enableLowPerformanceMode: boolean; setEnableThumbnails(v: boolean): void; setEnableAutoplay(v: boolean): void; @@ -32,6 +33,7 @@ export interface PreferencesStore { setProxyTmdb(v: boolean): void; setFebboxKey(v: string | null): void; setRealDebridKey(v: string | null): void; + setEnableLowPerformanceMode(v: boolean): void; } export const usePreferencesStore = create( @@ -51,6 +53,7 @@ export const usePreferencesStore = create( proxyTmdb: false, febboxKey: null, realDebridKey: null, + enableLowPerformanceMode: false, setEnableThumbnails(v) { set((s) => { s.enableThumbnails = v; @@ -121,6 +124,11 @@ export const usePreferencesStore = create( s.realDebridKey = v; }); }, + setEnableLowPerformanceMode(v) { + set((s) => { + s.enableLowPerformanceMode = v; + }); + }, })), { name: "__MW::preferences",