Update debrid key entry

This commit is contained in:
Pas 2025-11-19 12:35:19 -07:00
parent a4f48b3e97
commit 0a7816ff77
11 changed files with 209 additions and 276 deletions

View file

@ -1267,14 +1267,18 @@
"invalid_token": "Failed to fetch a 'VIP' stream. Your token is invalid!" "invalid_token": "Failed to fetch a 'VIP' stream. Your token is invalid!"
} }
}, },
"realdebrid": { "debrid": {
"title": "Real Debrid (Beta)", "title": "Debrid (Beta)",
"description": "Enter your Real Debrid API key to access Real Debrid. Extension required.", "description": "Enter your Debrid API key to access Debrid services. Requires a paid <0>Real-Debrid</0> or <1>TorBox</1> account!",
"tokenLabel": "API Key", "tokenLabel": "API Key",
"serviceOptions": {
"realdebrid": "Real Debrid",
"torbox": "TorBox"
},
"status": { "status": {
"failure": "Failed to connect to Real Debrid. Please check your API key.", "failure": "Failed to connect to Debrid. Please check your API key.",
"api_down": "Real Debrid API is currently unavailable. Please try again later.", "api_down": "Hmm, something went wrong. Please try again later.",
"invalid_token": "Invalid API key or non-premium account. Real Debrid requires a premium account." "invalid_token": "Invalid API key or non-premium account. Debrid requires a premium account."
} }
}, },
"watchParty": { "watchParty": {

View file

@ -9,7 +9,8 @@ export interface SettingsInput {
defaultSubtitleLanguage?: string; defaultSubtitleLanguage?: string;
proxyUrls?: string[] | null; proxyUrls?: string[] | null;
febboxKey?: string | null; febboxKey?: string | null;
realDebridKey?: string | null; debridToken?: string | null;
debridService?: string;
enableThumbnails?: boolean; enableThumbnails?: boolean;
enableAutoplay?: boolean; enableAutoplay?: boolean;
enableSkipCredits?: boolean; enableSkipCredits?: boolean;
@ -42,7 +43,8 @@ export interface SettingsResponse {
defaultSubtitleLanguage?: string | null; defaultSubtitleLanguage?: string | null;
proxyUrls?: string[] | null; proxyUrls?: string[] | null;
febboxKey?: string | null; febboxKey?: string | null;
realDebridKey?: string | null; debridToken?: string | null;
debridService?: string;
enableThumbnails?: boolean; enableThumbnails?: boolean;
enableAutoplay?: boolean; enableAutoplay?: boolean;
enableSkipCredits?: boolean; enableSkipCredits?: boolean;

View file

@ -32,7 +32,8 @@ export function useAuthData() {
(s) => s.importSubtitleLanguage, (s) => s.importSubtitleLanguage,
); );
const setFebboxKey = usePreferencesStore((s) => s.setFebboxKey); const setFebboxKey = usePreferencesStore((s) => s.setFebboxKey);
const setRealDebridKey = usePreferencesStore((s) => s.setRealDebridKey); const setdebridToken = usePreferencesStore((s) => s.setdebridToken);
const setdebridService = usePreferencesStore((s) => s.setdebridService);
const replaceBookmarks = useBookmarkStore((s) => s.replaceBookmarks); const replaceBookmarks = useBookmarkStore((s) => s.replaceBookmarks);
const replaceItems = useProgressStore((s) => s.replaceItems); const replaceItems = useProgressStore((s) => s.replaceItems);
@ -232,8 +233,12 @@ export function useAuthData() {
setFebboxKey(settings.febboxKey); setFebboxKey(settings.febboxKey);
} }
if (settings.realDebridKey !== undefined) { if (settings.debridToken !== undefined) {
setRealDebridKey(settings.realDebridKey); setdebridToken(settings.debridToken);
}
if (settings.debridService !== undefined) {
setdebridService(settings.debridService);
} }
if (settings.enableLowPerformanceMode !== undefined) { if (settings.enableLowPerformanceMode !== undefined) {
@ -288,7 +293,8 @@ export function useAuthData() {
setDisabledEmbeds, setDisabledEmbeds,
setProxyTmdb, setProxyTmdb,
setFebboxKey, setFebboxKey,
setRealDebridKey, setdebridToken,
setdebridService,
setEnableLowPerformanceMode, setEnableLowPerformanceMode,
setEnableNativeSubtitles, setEnableNativeSubtitles,
setEnableHoldToBoost, setEnableHoldToBoost,

View file

@ -46,7 +46,8 @@ export function useSettingsState(
proxyUrls: string[] | null, proxyUrls: string[] | null,
backendUrl: string | null, backendUrl: string | null,
febboxKey: string | null, febboxKey: string | null,
realDebridKey: string | null, debridToken: string | null,
debridService: string,
profile: profile:
| { | {
colorA: string; colorA: string;
@ -86,11 +87,17 @@ export function useSettingsState(
const [febboxKeyState, setFebboxKey, resetFebboxKey, febboxKeyChanged] = const [febboxKeyState, setFebboxKey, resetFebboxKey, febboxKeyChanged] =
useDerived(febboxKey); useDerived(febboxKey);
const [ const [
realDebridKeyState, debridTokenState,
setRealDebridKey, setdebridToken,
resetRealDebridKey, resetdebridToken,
realDebridKeyChanged, debridTokenChanged,
] = useDerived(realDebridKey); ] = useDerived(debridToken);
const [
debridServiceState,
setdebridService,
_resetdebridService,
debridServiceChanged,
] = useDerived(debridService);
const [themeState, setTheme, resetTheme, themeChanged] = useDerived(theme); const [themeState, setTheme, resetTheme, themeChanged] = useDerived(theme);
const setPreviewTheme = usePreviewThemeStore((s) => s.setPreviewTheme); const setPreviewTheme = usePreviewThemeStore((s) => s.setPreviewTheme);
const resetPreviewTheme = useCallback( const resetPreviewTheme = useCallback(
@ -264,7 +271,7 @@ export function useSettingsState(
resetProxyUrls(); resetProxyUrls();
resetBackendUrl(); resetBackendUrl();
resetFebboxKey(); resetFebboxKey();
resetRealDebridKey(); resetdebridToken();
resetDeviceName(); resetDeviceName();
resetNickname(); resetNickname();
resetProfile(); resetProfile();
@ -303,7 +310,7 @@ export function useSettingsState(
backendUrlChanged || backendUrlChanged ||
proxyUrlsChanged || proxyUrlsChanged ||
febboxKeyChanged || febboxKeyChanged ||
realDebridKeyChanged || debridTokenChanged ||
profileChanged || profileChanged ||
enableThumbnailsChanged || enableThumbnailsChanged ||
enableAutoplayChanged || enableAutoplayChanged ||
@ -373,10 +380,15 @@ export function useSettingsState(
set: setFebboxKey, set: setFebboxKey,
changed: febboxKeyChanged, changed: febboxKeyChanged,
}, },
realDebridKey: { debridToken: {
state: realDebridKeyState, state: debridTokenState,
set: setRealDebridKey, set: setdebridToken,
changed: realDebridKeyChanged, changed: debridTokenChanged,
},
debridService: {
state: debridServiceState,
set: setdebridService,
changed: debridServiceChanged,
}, },
profile: { profile: {
state: profileState, state: profileState,

View file

@ -369,8 +369,10 @@ export function SettingsPage() {
const febboxKey = usePreferencesStore((s) => s.febboxKey); const febboxKey = usePreferencesStore((s) => s.febboxKey);
const setFebboxKey = usePreferencesStore((s) => s.setFebboxKey); const setFebboxKey = usePreferencesStore((s) => s.setFebboxKey);
const realDebridKey = usePreferencesStore((s) => s.realDebridKey); const debridToken = usePreferencesStore((s) => s.debridToken);
const setRealDebridKey = usePreferencesStore((s) => s.setRealDebridKey); const setdebridToken = usePreferencesStore((s) => s.setdebridToken);
const debridService = usePreferencesStore((s) => s.debridService);
const setdebridService = usePreferencesStore((s) => s.setdebridService);
const enableThumbnails = usePreferencesStore((s) => s.enableThumbnails); const enableThumbnails = usePreferencesStore((s) => s.enableThumbnails);
const setEnableThumbnails = usePreferencesStore((s) => s.setEnableThumbnails); const setEnableThumbnails = usePreferencesStore((s) => s.setEnableThumbnails);
@ -511,13 +513,16 @@ export function SettingsPage() {
if (settings.febboxKey) { if (settings.febboxKey) {
setFebboxKey(settings.febboxKey); setFebboxKey(settings.febboxKey);
} }
if (settings.realDebridKey) { if (settings.debridToken) {
setRealDebridKey(settings.realDebridKey); setdebridToken(settings.debridToken);
}
if (settings.debridService) {
setdebridService(settings.debridService);
} }
} }
}; };
loadSettings(); loadSettings();
}, [account, backendUrl, setFebboxKey, setRealDebridKey]); }, [account, backendUrl, setFebboxKey, setdebridToken, setdebridService]);
const state = useSettingsState( const state = useSettingsState(
activeTheme, activeTheme,
@ -528,7 +533,8 @@ export function SettingsPage() {
proxySet, proxySet,
backendUrlSetting, backendUrlSetting,
febboxKey, febboxKey,
realDebridKey, debridToken,
debridService,
account ? account.profile : undefined, account ? account.profile : undefined,
enableThumbnails, enableThumbnails,
enableAutoplay, enableAutoplay,
@ -598,7 +604,8 @@ export function SettingsPage() {
state.theme.changed || state.theme.changed ||
state.proxyUrls.changed || state.proxyUrls.changed ||
state.febboxKey.changed || state.febboxKey.changed ||
state.realDebridKey.changed || state.debridToken.changed ||
state.debridService.changed ||
state.enableThumbnails.changed || state.enableThumbnails.changed ||
state.enableAutoplay.changed || state.enableAutoplay.changed ||
state.enableSkipCredits.changed || state.enableSkipCredits.changed ||
@ -625,7 +632,8 @@ export function SettingsPage() {
applicationTheme: state.theme.state, applicationTheme: state.theme.state,
proxyUrls: state.proxyUrls.state?.filter((v) => v !== "") ?? null, proxyUrls: state.proxyUrls.state?.filter((v) => v !== "") ?? null,
febboxKey: state.febboxKey.state, febboxKey: state.febboxKey.state,
realDebridKey: state.realDebridKey.state, debridToken: state.debridToken.state,
debridService: state.debridService.state,
enableThumbnails: state.enableThumbnails.state, enableThumbnails: state.enableThumbnails.state,
enableAutoplay: state.enableAutoplay.state, enableAutoplay: state.enableAutoplay.state,
enableSkipCredits: state.enableSkipCredits.state, enableSkipCredits: state.enableSkipCredits.state,
@ -690,7 +698,8 @@ export function SettingsPage() {
setProxySet(state.proxyUrls.state?.filter((v) => v !== "") ?? null); setProxySet(state.proxyUrls.state?.filter((v) => v !== "") ?? null);
setEnableSourceOrder(state.enableSourceOrder.state); setEnableSourceOrder(state.enableSourceOrder.state);
setFebboxKey(state.febboxKey.state); setFebboxKey(state.febboxKey.state);
setRealDebridKey(state.realDebridKey.state); setdebridToken(state.debridToken.state);
setdebridService(state.debridService.state);
setProxyTmdb(state.proxyTmdb.state); setProxyTmdb(state.proxyTmdb.state);
setEnableCarouselView(state.enableCarouselView.state); setEnableCarouselView(state.enableCarouselView.state);
setForceCompactEpisodeView(state.forceCompactEpisodeView.state); setForceCompactEpisodeView(state.forceCompactEpisodeView.state);
@ -720,7 +729,8 @@ export function SettingsPage() {
backendUrl, backendUrl,
setEnableThumbnails, setEnableThumbnails,
setFebboxKey, setFebboxKey,
setRealDebridKey, setdebridToken,
setdebridService,
state, state,
setEnableAutoplay, setEnableAutoplay,
setEnableSkipCredits, setEnableSkipCredits,
@ -881,8 +891,10 @@ export function SettingsPage() {
setProxyUrls={state.proxyUrls.set} setProxyUrls={state.proxyUrls.set}
febboxKey={state.febboxKey.state} febboxKey={state.febboxKey.state}
setFebboxKey={state.febboxKey.set} setFebboxKey={state.febboxKey.set}
realDebridKey={state.realDebridKey.state} debridToken={state.debridToken.state}
setRealDebridKey={state.realDebridKey.set} setdebridToken={state.debridToken.set}
debridService={state.debridService.state}
setdebridService={state.debridService.set}
proxyTmdb={state.proxyTmdb.state} proxyTmdb={state.proxyTmdb.state}
setProxyTmdb={state.proxyTmdb.set} setProxyTmdb={state.proxyTmdb.set}
/> />

View file

@ -1,10 +1,7 @@
import { useEffect, useState } from "react";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { Button } from "@/components/buttons/Button"; import { Button } from "@/components/buttons/Button";
import { Toggle } from "@/components/buttons/Toggle";
import { Icon, Icons } from "@/components/Icon"; import { Icon, Icons } from "@/components/Icon";
import { SettingsCard } from "@/components/layout/SettingsCard";
import { Stepper } from "@/components/layout/Stepper"; import { Stepper } from "@/components/layout/Stepper";
import { BiggerCenterContainer } from "@/components/layout/ThinContainer"; import { BiggerCenterContainer } from "@/components/layout/ThinContainer";
import { VerticalLine } from "@/components/layout/VerticalLine"; import { VerticalLine } from "@/components/layout/VerticalLine";
@ -14,11 +11,6 @@ import {
ModalCard, ModalCard,
useModal, useModal,
} from "@/components/overlays/Modal"; } from "@/components/overlays/Modal";
import {
StatusCircle,
StatusCircleProps,
} from "@/components/player/internals/StatusCircle";
import { AuthInputBox } from "@/components/text-inputs/AuthInputBox";
import { Divider } from "@/components/utils/Divider"; import { Divider } from "@/components/utils/Divider";
import { Ol } from "@/components/utils/Ol"; import { Ol } from "@/components/utils/Ol";
import { import {
@ -43,133 +35,7 @@ import { conf } from "@/setup/config";
import { usePreferencesStore } from "@/stores/preferences"; import { usePreferencesStore } from "@/stores/preferences";
import { getProxyUrls } from "@/utils/proxyUrls"; import { getProxyUrls } from "@/utils/proxyUrls";
import { FebboxSetup } from "../parts/settings/ConnectionsPart"; import { DebridEdit, FebboxSetup } from "../parts/settings/ConnectionsPart";
import { Status, testRealDebridKey } from "../parts/settings/SetupPart";
async function getRealDebridKeyStatus(realDebridKey: string | null) {
if (realDebridKey) {
const status: Status = await testRealDebridKey(realDebridKey);
return status;
}
return "unset";
}
export function RealDebridSetup() {
const { t } = useTranslation();
const realDebridKey = usePreferencesStore((s) => s.realDebridKey);
const setRealDebridKey = usePreferencesStore((s) => s.setRealDebridKey);
// Initialize isExpanded based on whether realDebridKey has a value
const [isExpanded, setIsExpanded] = useState(
realDebridKey !== null && realDebridKey !== "",
);
// Add a separate effect to set the initial state
useEffect(() => {
// If we have a valid key, make sure the section is expanded
if (realDebridKey && realDebridKey.length > 0) {
setIsExpanded(true);
}
}, [realDebridKey]);
const [status, setStatus] = useState<Status>("unset");
const statusMap: Record<Status, StatusCircleProps["type"]> = {
error: "error",
success: "success",
unset: "noresult",
api_down: "error",
invalid_token: "error",
};
useEffect(() => {
const checkTokenStatus = async () => {
const result = await getRealDebridKeyStatus(realDebridKey);
setStatus(result);
};
checkTokenStatus();
}, [realDebridKey]);
// Toggle handler that preserves the key
const toggleExpanded = () => {
if (isExpanded) {
// Store the key temporarily instead of setting to null
setRealDebridKey("");
setIsExpanded(false);
} else {
setIsExpanded(true);
}
};
if (conf().ALLOW_REAL_DEBRID_KEY) {
return (
<div className="mt-6">
<SettingsCard>
<div className="flex justify-between items-center gap-4">
<div className="my-3">
<p className="text-white font-bold mb-3">
{t("settings.connections.realdebrid.title", "Real Debrid API")}
</p>
<p className="max-w-[30rem] font-medium">
{t(
"settings.connections.realdebrid.description",
"Enter your Real Debrid API key to access premium sources.",
)}
</p>
</div>
<div>
<Toggle onClick={toggleExpanded} enabled={isExpanded} />
</div>
</div>
{isExpanded ? (
<>
<Divider marginClass="my-6 px-8 box-content -mx-8" />
<p className="text-white font-bold mb-3">
{t("settings.connections.realdebrid.tokenLabel", "API Key")}
</p>
<div className="flex items-center w-full">
<StatusCircle type={statusMap[status]} className="mx-2 mr-4" />
<AuthInputBox
onChange={(newToken) => {
setRealDebridKey(newToken);
}}
value={realDebridKey ?? ""}
placeholder="API Key"
passwordToggleable
className="flex-grow"
/>
</div>
{status === "error" && (
<p className="text-type-danger mt-4">
{t(
"settings.connections.realdebrid.status.failure",
"Failed to connect to Real Debrid. Please check your API key.",
)}
</p>
)}
{status === "api_down" && (
<p className="text-type-danger mt-4">
{t(
"settings.connections.realdebrid.status.api_down",
"Real Debrid API is currently unavailable. Please try again later.",
)}
</p>
)}
{status === "invalid_token" && (
<p className="text-type-danger mt-4">
{t(
"settings.connections.realdebrid.status.invalid_token",
"Invalid API key or non-premium account. Real Debrid requires a premium account.",
)}
</p>
)}
</>
) : null}
</SettingsCard>
</div>
);
}
return null;
}
function Item(props: { title: string; children: React.ReactNode }) { function Item(props: { title: string; children: React.ReactNode }) {
return ( return (
@ -419,7 +285,6 @@ export function OnboardingPage() {
)} )}
</div> </div>
{/* <RealDebridSetup /> */}
<div className="mt-6"> <div className="mt-6">
<FebboxSetup <FebboxSetup
febboxKey={usePreferencesStore((s) => s.febboxKey)} febboxKey={usePreferencesStore((s) => s.febboxKey)}
@ -427,6 +292,14 @@ export function OnboardingPage() {
mode="onboarding" mode="onboarding"
/> />
</div> </div>
<div className="mt-6">
<DebridEdit
debridToken={usePreferencesStore((s) => s.debridToken)}
setdebridToken={usePreferencesStore((s) => s.setdebridToken)}
debridService={usePreferencesStore((s) => s.debridService)}
setdebridService={usePreferencesStore((s) => s.setdebridService)}
/>
</div>
</BiggerCenterContainer> </BiggerCenterContainer>
</MinimalPageLayout> </MinimalPageLayout>
); );

View file

@ -59,7 +59,8 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
disabledEmbeds: store.disabledEmbeds, disabledEmbeds: store.disabledEmbeds,
proxyTmdb: store.proxyTmdb, proxyTmdb: store.proxyTmdb,
febboxKey: store.febboxKey, febboxKey: store.febboxKey,
realDebridKey: store.realDebridKey, debridToken: store.debridToken,
debridService: store.debridService,
enableLowPerformanceMode: store.enableLowPerformanceMode, enableLowPerformanceMode: store.enableLowPerformanceMode,
enableNativeSubtitles: store.enableNativeSubtitles, enableNativeSubtitles: store.enableNativeSubtitles,
enableHoldToBoost: store.enableHoldToBoost, enableHoldToBoost: store.enableHoldToBoost,

View file

@ -9,6 +9,7 @@ import { Trans, useTranslation } from "react-i18next";
import { Button } from "@/components/buttons/Button"; import { Button } from "@/components/buttons/Button";
import { Toggle } from "@/components/buttons/Toggle"; import { Toggle } from "@/components/buttons/Toggle";
import { Dropdown } from "@/components/form/Dropdown";
import { Icon, Icons } from "@/components/Icon"; import { Icon, Icons } from "@/components/Icon";
import { SettingsCard } from "@/components/layout/SettingsCard"; import { SettingsCard } from "@/components/layout/SettingsCard";
import { Modal, ModalCard, useModal } from "@/components/overlays/Modal"; import { Modal, ModalCard, useModal } from "@/components/overlays/Modal";
@ -24,7 +25,7 @@ import {
SetupPart, SetupPart,
Status, Status,
testFebboxKey, testFebboxKey,
testRealDebridKey, testdebridToken,
} from "@/pages/parts/settings/SetupPart"; } from "@/pages/parts/settings/SetupPart";
import { conf } from "@/setup/config"; import { conf } from "@/setup/config";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
@ -49,9 +50,11 @@ interface FebboxKeyProps {
setFebboxKey: (value: string | null) => void; setFebboxKey: (value: string | null) => void;
} }
interface RealDebridKeyProps { interface DebridProps {
realDebridKey: string | null; debridToken: string | null;
setRealDebridKey: Dispatch<SetStateAction<string | null>>; setdebridToken: (value: string | null) => void;
debridService: string;
setdebridService: (value: string) => void;
} }
function ProxyEdit({ function ProxyEdit({
@ -460,33 +463,30 @@ export function FebboxSetup({
} }
} }
async function getRealDebridKeyStatus(realDebridKey: string | null) { async function getdebridTokenStatus(debridToken: string | null) {
if (realDebridKey) { if (debridToken) {
const status: Status = await testRealDebridKey(realDebridKey); const status: Status = await testdebridToken(debridToken);
return status; return status;
} }
return "unset"; return "unset";
} }
function RealDebridKeyEdit({ export function DebridEdit({
realDebridKey, debridToken,
setRealDebridKey, setdebridToken,
}: RealDebridKeyProps) { debridService,
setdebridService,
}: DebridProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const user = useAuthStore(); const user = useAuthStore();
const preferences = usePreferencesStore(); const preferences = usePreferencesStore();
// Enable Real Debrid token when account is loaded and we have a token // Enable Real Debrid token when account is loaded and we have a token
useEffect(() => { useEffect(() => {
if (user.account && realDebridKey === null && preferences.realDebridKey) { if (user.account && debridToken === null && preferences.debridToken) {
setRealDebridKey(preferences.realDebridKey); setdebridToken(preferences.debridToken);
} }
}, [ }, [user.account, debridToken, preferences.debridToken, setdebridToken]);
user.account,
realDebridKey,
preferences.realDebridKey,
setRealDebridKey,
]);
const [status, setStatus] = useState<Status>("unset"); const [status, setStatus] = useState<Status>("unset");
const statusMap: Record<Status, StatusCircleProps["type"]> = { const statusMap: Record<Status, StatusCircleProps["type"]> = {
@ -499,73 +499,78 @@ function RealDebridKeyEdit({
useEffect(() => { useEffect(() => {
const checkTokenStatus = async () => { const checkTokenStatus = async () => {
const result = await getRealDebridKeyStatus(realDebridKey); const result = await getdebridTokenStatus(debridToken);
setStatus(result); setStatus(result);
}; };
checkTokenStatus(); checkTokenStatus();
}, [realDebridKey]); }, [debridToken]);
if (conf().ALLOW_REAL_DEBRID_KEY) { if (conf().ALLOW_DEBRID_KEY) {
return ( return (
<SettingsCard> <SettingsCard>
<div className="flex justify-between items-center gap-4"> <div className="flex justify-between items-center gap-4">
<div className="my-3"> <div className="my-3">
<p className="text-white font-bold mb-3">{t("realdebrid.title")}</p> <p className="text-white font-bold mb-3">{t("debrid.title")}</p>
<p className="max-w-[30rem] font-medium"> <Trans i18nKey="debrid.description">
{t("realdebrid.description")} <MwLink to="https://real-debrid.com/" />
</p> {/* fifth's referral code */}
<MwLink> <MwLink to="https://torbox.app/subscription?referral=3f665ece-0405-4012-9db7-c6f90e8567e1" />
<a </Trans>
href="https://real-debrid.com/"
target="_blank"
rel="noreferrer"
>
real-debrid.com
</a>
</MwLink>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Toggle <Toggle
onClick={() => setRealDebridKey((s) => (s === null ? "" : null))} onClick={() => setdebridToken(debridToken === null ? "" : null)}
enabled={realDebridKey !== null} enabled={debridToken !== null}
/> />
</div> </div>
</div> </div>
{realDebridKey !== null ? ( <Divider marginClass="my-6 px-8 box-content -mx-8" />
<> <p className="text-white font-bold mb-3">{t("debrid.tokenLabel")}</p>
<Divider marginClass="my-6 px-8 box-content -mx-8" /> <div className="flex md:flex-row flex-col items-center w-full gap-4">
<p className="text-white font-bold mb-3"> <div className="flex items-center w-full">
{t("realdebrid.tokenLabel")} <StatusCircle type={statusMap[status]} className="mx-2 mr-4" />
</p> <AuthInputBox
<div className="flex items-center w-full"> onChange={(newToken) => {
<StatusCircle type={statusMap[status]} className="mx-2 mr-4" /> setdebridToken(newToken);
<AuthInputBox }}
onChange={(newToken) => { value={debridToken ?? ""}
setRealDebridKey(newToken); placeholder="ABC123..."
}} passwordToggleable
value={realDebridKey ?? ""} className="flex-grow"
placeholder="ABC123..." />
passwordToggleable </div>
className="flex-grow" <div className="flex items-center">
/> <Dropdown
</div> options={[
{status === "error" && ( {
<p className="text-type-danger mt-4"> id: "realdebrid",
{t("realdebrid.status.failure")} name: t("debrid.serviceOptions.realdebrid"),
</p> },
)} {
{status === "api_down" && ( id: "torbox",
<p className="text-type-danger mt-4"> name: t("debrid.serviceOptions.torbox"),
{t("realdebrid.status.api_down")} },
</p> ]}
)} selectedItem={{
{status === "invalid_token" && ( id: debridService,
<p className="text-type-danger mt-4"> name: t(`debrid.serviceOptions.${debridService}`),
{t("realdebrid.status.invalid_token")} }}
</p> setSelectedItem={(item) => setdebridService(item.id)}
)} direction="up"
</> />
) : null} </div>
</div>
{status === "error" && (
<p className="text-type-danger mt-4">{t("debrid.status.failure")}</p>
)}
{status === "api_down" && (
<p className="text-type-danger mt-4">{t("debrid.status.api_down")}</p>
)}
{status === "invalid_token" && (
<p className="text-type-danger mt-4">
{t("debrid.status.invalid_token")}
</p>
)}
</SettingsCard> </SettingsCard>
); );
} }
@ -573,10 +578,7 @@ function RealDebridKeyEdit({
} }
export function ConnectionsPart( export function ConnectionsPart(
props: BackendEditProps & props: BackendEditProps & ProxyEditProps & FebboxKeyProps & DebridProps,
ProxyEditProps &
FebboxKeyProps &
RealDebridKeyProps,
) { ) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -594,15 +596,17 @@ export function ConnectionsPart(
backendUrl={props.backendUrl} backendUrl={props.backendUrl}
setBackendUrl={props.setBackendUrl} setBackendUrl={props.setBackendUrl}
/> />
<RealDebridKeyEdit
realDebridKey={props.realDebridKey}
setRealDebridKey={props.setRealDebridKey}
/>
<FebboxSetup <FebboxSetup
febboxKey={props.febboxKey} febboxKey={props.febboxKey}
setFebboxKey={props.setFebboxKey} setFebboxKey={props.setFebboxKey}
mode="settings" mode="settings"
/> />
<DebridEdit
debridToken={props.debridToken}
setdebridToken={props.setdebridToken}
debridService={props.debridService}
setdebridService={props.setdebridService}
/>
</div> </div>
</div> </div>
); );

View file

@ -42,7 +42,7 @@ type SetupData = {
proxy: Status; proxy: Status;
defaultProxy: Status; defaultProxy: Status;
febboxKeyTest?: Status; febboxKeyTest?: Status;
realDebridKeyTest?: Status; debridTokenTest?: Status;
}; };
function testProxy(url: string) { function testProxy(url: string) {
@ -142,10 +142,10 @@ export async function testFebboxKey(febboxKey: string | null): Promise<Status> {
return "api_down"; return "api_down";
} }
export async function testRealDebridKey( export async function testdebridToken(
realDebridKey: string | null, debridToken: string | null,
): Promise<Status> { ): Promise<Status> {
if (!realDebridKey) { if (!debridToken) {
return "unset"; return "unset";
} }
@ -160,7 +160,7 @@ export async function testRealDebridKey(
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${realDebridKey}`, Authorization: `Bearer ${debridToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -191,10 +191,21 @@ export async function testRealDebridKey(
return "api_down"; return "api_down";
} }
export async function testTorboxToken(
torboxToken: string | null,
): Promise<Status> {
if (!torboxToken) {
return "unset";
}
// TODO: Implement Torbox token test
return "success";
}
function useIsSetup() { function useIsSetup() {
const proxyUrls = useAuthStore((s) => s.proxySet); const proxyUrls = useAuthStore((s) => s.proxySet);
const febboxKey = usePreferencesStore((s) => s.febboxKey); const febboxKey = usePreferencesStore((s) => s.febboxKey);
const realDebridKey = usePreferencesStore((s) => s.realDebridKey); const debridToken = usePreferencesStore((s) => s.debridToken);
const { loading, value } = useAsync(async (): Promise<SetupData> => { const { loading, value } = useAsync(async (): Promise<SetupData> => {
const extensionStatus: Status = (await isExtensionActive()) const extensionStatus: Status = (await isExtensionActive())
? "success" ? "success"
@ -210,7 +221,7 @@ function useIsSetup() {
} }
const febboxKeyStatus: Status = await testFebboxKey(febboxKey); const febboxKeyStatus: Status = await testFebboxKey(febboxKey);
const realDebridKeyStatus: Status = await testRealDebridKey(realDebridKey); const debridTokenStatus: Status = await testdebridToken(debridToken);
return { return {
extension: extensionStatus, extension: extensionStatus,
@ -219,23 +230,23 @@ function useIsSetup() {
...(conf().ALLOW_FEBBOX_KEY && { ...(conf().ALLOW_FEBBOX_KEY && {
febboxKeyTest: febboxKeyStatus, febboxKeyTest: febboxKeyStatus,
}), }),
realDebridKeyTest: realDebridKeyStatus, debridTokenTest: debridTokenStatus,
}; };
}, [proxyUrls, febboxKey, realDebridKey]); }, [proxyUrls, febboxKey, debridToken]);
let globalState: Status = "unset"; let globalState: Status = "unset";
if ( if (
value?.extension === "success" || value?.extension === "success" ||
value?.proxy === "success" || value?.proxy === "success" ||
value?.febboxKeyTest === "success" || value?.febboxKeyTest === "success" ||
value?.realDebridKeyTest === "success" value?.debridTokenTest === "success"
) )
globalState = "success"; globalState = "success";
if ( if (
value?.proxy === "error" || value?.proxy === "error" ||
value?.extension === "error" || value?.extension === "error" ||
value?.febboxKeyTest === "error" || value?.febboxKeyTest === "error" ||
value?.realDebridKeyTest === "error" value?.debridTokenTest === "error"
) )
globalState = "error"; globalState = "error";
@ -375,9 +386,9 @@ export function SetupPart() {
> >
{t("settings.connections.setup.items.default")} {t("settings.connections.setup.items.default")}
</SetupCheckList> </SetupCheckList>
{conf().ALLOW_REAL_DEBRID_KEY && ( {conf().ALLOW_DEBRID_KEY && (
<SetupCheckList status={setupStates.realDebridKeyTest || "unset"}> <SetupCheckList status={setupStates.debridTokenTest || "unset"}>
Real Debrid token Debrid Service
</SetupCheckList> </SetupCheckList>
)} )}
{conf().ALLOW_FEBBOX_KEY && ( {conf().ALLOW_FEBBOX_KEY && (

View file

@ -26,7 +26,7 @@ interface Config {
ONBOARDING_PROXY_INSTALL_LINK: string; ONBOARDING_PROXY_INSTALL_LINK: string;
ALLOW_AUTOPLAY: boolean; ALLOW_AUTOPLAY: boolean;
ALLOW_FEBBOX_KEY: boolean; ALLOW_FEBBOX_KEY: boolean;
ALLOW_REAL_DEBRID_KEY: boolean; ALLOW_DEBRID_KEY: boolean;
SHOW_AD: boolean; SHOW_AD: boolean;
AD_CONTENT_URL: string; AD_CONTENT_URL: string;
TRACK_SCRIPT: string; // like <script src="https://umami.com/script.js"></script> TRACK_SCRIPT: string; // like <script src="https://umami.com/script.js"></script>
@ -42,7 +42,7 @@ export interface RuntimeConfig {
DMCA_EMAIL: string | null; DMCA_EMAIL: string | null;
TWITTER_LINK: string; TWITTER_LINK: string;
TMDB_READ_API_KEY: string | null; TMDB_READ_API_KEY: string | null;
ALLOW_REAL_DEBRID_KEY: boolean; ALLOW_DEBRID_KEY: boolean;
NORMAL_ROUTER: boolean; NORMAL_ROUTER: boolean;
PROXY_URLS: string[]; PROXY_URLS: string[];
M3U8_PROXY_URLS: string[]; M3U8_PROXY_URLS: string[];
@ -87,7 +87,7 @@ const env: Record<keyof Config, undefined | string> = {
HAS_ONBOARDING: import.meta.env.VITE_HAS_ONBOARDING, HAS_ONBOARDING: import.meta.env.VITE_HAS_ONBOARDING,
ALLOW_AUTOPLAY: import.meta.env.VITE_ALLOW_AUTOPLAY, ALLOW_AUTOPLAY: import.meta.env.VITE_ALLOW_AUTOPLAY,
ALLOW_FEBBOX_KEY: import.meta.env.VITE_ALLOW_FEBBOX_KEY, ALLOW_FEBBOX_KEY: import.meta.env.VITE_ALLOW_FEBBOX_KEY,
ALLOW_REAL_DEBRID_KEY: import.meta.env.VITE_ALLOW_REAL_DEBRID_KEY, ALLOW_DEBRID_KEY: import.meta.env.VITE_ALLOW_DEBRID_KEY,
SHOW_AD: import.meta.env.VITE_SHOW_AD, SHOW_AD: import.meta.env.VITE_SHOW_AD,
AD_CONTENT_URL: import.meta.env.VITE_AD_CONTENT_URL, AD_CONTENT_URL: import.meta.env.VITE_AD_CONTENT_URL,
TRACK_SCRIPT: import.meta.env.VITE_TRACK_SCRIPT, TRACK_SCRIPT: import.meta.env.VITE_TRACK_SCRIPT,
@ -159,7 +159,7 @@ export function conf(): RuntimeConfig {
) )
.filter((v) => v.length === 2), // The format is <beforeA>:<afterA>,<beforeB>:<afterB> .filter((v) => v.length === 2), // The format is <beforeA>:<afterA>,<beforeB>:<afterB>
ALLOW_FEBBOX_KEY: getKey("ALLOW_FEBBOX_KEY", "false") === "true", ALLOW_FEBBOX_KEY: getKey("ALLOW_FEBBOX_KEY", "false") === "true",
ALLOW_REAL_DEBRID_KEY: getKey("ALLOW_REAL_DEBRID_KEY", "false") === "true", ALLOW_DEBRID_KEY: getKey("ALLOW_DEBRID_KEY", "false") === "true",
SHOW_AD: getKey("SHOW_AD", "false") === "true", SHOW_AD: getKey("SHOW_AD", "false") === "true",
AD_CONTENT_URL: getKey("AD_CONTENT_URL", "") AD_CONTENT_URL: getKey("AD_CONTENT_URL", "")
.split(",") .split(",")

View file

@ -22,7 +22,8 @@ export interface PreferencesStore {
disabledEmbeds: string[]; disabledEmbeds: string[];
proxyTmdb: boolean; proxyTmdb: boolean;
febboxKey: string | null; febboxKey: string | null;
realDebridKey: string | null; debridToken: string | null;
debridService: string;
enableLowPerformanceMode: boolean; enableLowPerformanceMode: boolean;
enableNativeSubtitles: boolean; enableNativeSubtitles: boolean;
enableHoldToBoost: boolean; enableHoldToBoost: boolean;
@ -49,7 +50,8 @@ export interface PreferencesStore {
setDisabledEmbeds(v: string[]): void; setDisabledEmbeds(v: string[]): void;
setProxyTmdb(v: boolean): void; setProxyTmdb(v: boolean): void;
setFebboxKey(v: string | null): void; setFebboxKey(v: string | null): void;
setRealDebridKey(v: string | null): void; setdebridToken(v: string | null): void;
setdebridService(v: string): void;
setEnableLowPerformanceMode(v: boolean): void; setEnableLowPerformanceMode(v: boolean): void;
setEnableNativeSubtitles(v: boolean): void; setEnableNativeSubtitles(v: boolean): void;
setEnableHoldToBoost(v: boolean): void; setEnableHoldToBoost(v: boolean): void;
@ -80,7 +82,8 @@ export const usePreferencesStore = create(
disabledEmbeds: [], disabledEmbeds: [],
proxyTmdb: false, proxyTmdb: false,
febboxKey: null, febboxKey: null,
realDebridKey: null, debridToken: null,
debridService: "realdebrid",
enableLowPerformanceMode: false, enableLowPerformanceMode: false,
enableNativeSubtitles: false, enableNativeSubtitles: false,
enableHoldToBoost: true, enableHoldToBoost: true,
@ -182,9 +185,14 @@ export const usePreferencesStore = create(
s.febboxKey = v; s.febboxKey = v;
}); });
}, },
setRealDebridKey(v) { setdebridToken(v) {
set((s) => { set((s) => {
s.realDebridKey = v; s.debridToken = v;
});
},
setdebridService(v) {
set((s) => {
s.debridService = v;
}); });
}, },
setEnableLowPerformanceMode(v) { setEnableLowPerformanceMode(v) {