pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add realdebrid setup

Pas 715c26e6 29c41270

+378 -8
+10
src/assets/locales/en.json
··· 977 977 "invalid_token": "Failed to fetch a 'VIP' stream. Your token is invalid!" 978 978 } 979 979 }, 980 + "realdebrid": { 981 + "title": "Real Debrid (Beta)", 982 + "description": "Enter your Real Debrid API key to access Real Debrid. Extension required.", 983 + "tokenLabel": "API Key", 984 + "status": { 985 + "failure": "Failed to connect to Real Debrid. Please check your API key.", 986 + "api_down": "Real Debrid API is currently unavailable. Please try again later.", 987 + "invalid_token": "Invalid API key or non-premium account. Real Debrid requires a premium account." 988 + } 989 + }, 980 990 "watchParty": { 981 991 "status": { 982 992 "inSync": "In sync",
+2
src/backend/accounts/settings.ts
··· 9 9 defaultSubtitleLanguage?: string; 10 10 proxyUrls?: string[] | null; 11 11 febboxKey?: string | null; 12 + realDebridKey?: string | null; 12 13 enableThumbnails?: boolean; 13 14 enableAutoplay?: boolean; 14 15 enableSkipCredits?: boolean; ··· 28 29 defaultSubtitleLanguage?: string | null; 29 30 proxyUrls?: string[] | null; 30 31 febboxKey?: string | null; 32 + realDebridKey?: string | null; 31 33 enableThumbnails?: boolean; 32 34 enableAutoplay?: boolean; 33 35 enableSkipCredits?: boolean;
+14
src/hooks/useSettingsState.ts
··· 44 44 proxyUrls: string[] | null, 45 45 backendUrl: string | null, 46 46 febboxKey: string | null, 47 + realDebridKey: string | null, 47 48 profile: 48 49 | { 49 50 colorA: string; ··· 69 70 useDerived(backendUrl); 70 71 const [febboxKeyState, setFebboxKey, resetFebboxKey, febboxKeyChanged] = 71 72 useDerived(febboxKey); 73 + const [ 74 + realDebridKeyState, 75 + setRealDebridKey, 76 + resetRealDebridKey, 77 + realDebridKeyChanged, 78 + ] = useDerived(realDebridKey); 72 79 const [themeState, setTheme, resetTheme, themeChanged] = useDerived(theme); 73 80 const setPreviewTheme = usePreviewThemeStore((s) => s.setPreviewTheme); 74 81 const resetPreviewTheme = useCallback( ··· 162 169 resetProxyUrls(); 163 170 resetBackendUrl(); 164 171 resetFebboxKey(); 172 + resetRealDebridKey(); 165 173 resetDeviceName(); 166 174 resetProfile(); 167 175 resetEnableThumbnails(); ··· 185 193 backendUrlChanged || 186 194 proxyUrlsChanged || 187 195 febboxKeyChanged || 196 + realDebridKeyChanged || 188 197 profileChanged || 189 198 enableThumbnailsChanged || 190 199 enableAutoplayChanged || ··· 235 244 state: febboxKeyState, 236 245 set: setFebboxKey, 237 246 changed: febboxKeyChanged, 247 + }, 248 + realDebridKey: { 249 + state: realDebridKeyState, 250 + set: setRealDebridKey, 251 + changed: realDebridKeyChanged, 238 252 }, 239 253 profile: { 240 254 state: profileState,
+14 -1
src/pages/Settings.tsx
··· 134 134 const febboxKey = usePreferencesStore((s) => s.febboxKey); 135 135 const setFebboxKey = usePreferencesStore((s) => s.setFebboxKey); 136 136 137 + const realDebridKey = usePreferencesStore((s) => s.realDebridKey); 138 + const setRealDebridKey = usePreferencesStore((s) => s.setRealDebridKey); 139 + 137 140 const enableThumbnails = usePreferencesStore((s) => s.enableThumbnails); 138 141 const setEnableThumbnails = usePreferencesStore((s) => s.setEnableThumbnails); 139 142 ··· 195 198 if (settings.febboxKey) { 196 199 setFebboxKey(settings.febboxKey); 197 200 } 201 + if (settings.realDebridKey) { 202 + setRealDebridKey(settings.realDebridKey); 203 + } 198 204 } 199 205 }; 200 206 loadSettings(); 201 - }, [account, backendUrl, setFebboxKey]); 207 + }, [account, backendUrl, setFebboxKey, setRealDebridKey]); 202 208 203 209 const state = useSettingsState( 204 210 activeTheme, ··· 208 214 proxySet, 209 215 backendUrlSetting, 210 216 febboxKey, 217 + realDebridKey, 211 218 account ? account.profile : undefined, 212 219 enableThumbnails, 213 220 enableAutoplay, ··· 264 271 state.theme.changed || 265 272 state.proxyUrls.changed || 266 273 state.febboxKey.changed || 274 + state.realDebridKey.changed || 267 275 state.enableThumbnails.changed || 268 276 state.enableAutoplay.changed || 269 277 state.enableSkipCredits.changed || ··· 281 289 applicationTheme: state.theme.state, 282 290 proxyUrls: state.proxyUrls.state?.filter((v) => v !== "") ?? null, 283 291 febboxKey: state.febboxKey.state, 292 + realDebridKey: state.realDebridKey.state, 284 293 enableThumbnails: state.enableThumbnails.state, 285 294 enableAutoplay: state.enableAutoplay.state, 286 295 enableSkipCredits: state.enableSkipCredits.state, ··· 325 334 setProxySet(state.proxyUrls.state?.filter((v) => v !== "") ?? null); 326 335 setEnableSourceOrder(state.enableSourceOrder.state); 327 336 setFebboxKey(state.febboxKey.state); 337 + setRealDebridKey(state.realDebridKey.state); 328 338 setProxyTmdb(state.proxyTmdb.state); 329 339 setEnableCarouselView(state.enableCarouselView.state); 330 340 ··· 348 358 backendUrl, 349 359 setEnableThumbnails, 350 360 setFebboxKey, 361 + setRealDebridKey, 351 362 state, 352 363 setEnableAutoplay, 353 364 setEnableSkipCredits, ··· 448 459 setProxyUrls={state.proxyUrls.set} 449 460 febboxKey={state.febboxKey.state} 450 461 setFebboxKey={state.febboxKey.set} 462 + realDebridKey={state.realDebridKey.state} 463 + setRealDebridKey={state.realDebridKey.set} 451 464 proxyTmdb={state.proxyTmdb.state} 452 465 setProxyTmdb={state.proxyTmdb.set} 453 466 />
+124 -2
src/pages/onboarding/Onboarding.tsx
··· 41 41 } from "@/pages/onboarding/utils"; 42 42 import { PageTitle } from "@/pages/parts/util/PageTitle"; 43 43 import { conf } from "@/setup/config"; 44 - import { useAuthStore } from "@/stores/auth"; 45 44 import { usePreferencesStore } from "@/stores/preferences"; 46 45 import { getProxyUrls } from "@/utils/proxyUrls"; 47 46 48 - import { Status, testFebboxKey } from "../parts/settings/SetupPart"; 47 + import { 48 + Status, 49 + testFebboxKey, 50 + testRealDebridKey, 51 + } from "../parts/settings/SetupPart"; 49 52 50 53 async function getFebboxKeyStatus(febboxKey: string | null) { 51 54 if (febboxKey) { ··· 218 221 ); 219 222 } 220 223 } 224 + 225 + async function getRealDebridKeyStatus(realDebridKey: string | null) { 226 + if (realDebridKey) { 227 + const status: Status = await testRealDebridKey(realDebridKey); 228 + return status; 229 + } 230 + return "unset"; 231 + } 232 + 233 + export function RealDebridSetup() { 234 + const { t } = useTranslation(); 235 + const realDebridKey = usePreferencesStore((s) => s.realDebridKey); 236 + const setRealDebridKey = usePreferencesStore((s) => s.setRealDebridKey); 237 + 238 + // Initialize isExpanded based on whether realDebridKey has a value 239 + const [isExpanded, setIsExpanded] = useState( 240 + realDebridKey !== null && realDebridKey !== "", 241 + ); 242 + 243 + // Add a separate effect to set the initial state 244 + useEffect(() => { 245 + // If we have a valid key, make sure the section is expanded 246 + if (realDebridKey && realDebridKey.length > 0) { 247 + setIsExpanded(true); 248 + } 249 + }, [realDebridKey]); 250 + 251 + const [status, setStatus] = useState<Status>("unset"); 252 + const statusMap: Record<Status, StatusCircleProps["type"]> = { 253 + error: "error", 254 + success: "success", 255 + unset: "noresult", 256 + api_down: "error", 257 + invalid_token: "error", 258 + }; 259 + 260 + useEffect(() => { 261 + const checkTokenStatus = async () => { 262 + const result = await getRealDebridKeyStatus(realDebridKey); 263 + setStatus(result); 264 + }; 265 + checkTokenStatus(); 266 + }, [realDebridKey]); 267 + 268 + // Toggle handler that preserves the key 269 + const toggleExpanded = () => { 270 + if (isExpanded) { 271 + // Store the key temporarily instead of setting to null 272 + setRealDebridKey(""); 273 + setIsExpanded(false); 274 + } else { 275 + setIsExpanded(true); 276 + } 277 + }; 278 + 279 + if (conf().ALLOW_REAL_DEBRID_KEY) { 280 + return ( 281 + <div className="mt-6"> 282 + <SettingsCard> 283 + <div className="flex justify-between items-center gap-4"> 284 + <div className="my-3"> 285 + <p className="text-white font-bold mb-3"> 286 + {t("settings.connections.realdebrid.title", "Real Debrid API")} 287 + </p> 288 + <p className="max-w-[30rem] font-medium"> 289 + {t( 290 + "settings.connections.realdebrid.description", 291 + "Enter your Real Debrid API key to access premium sources.", 292 + )} 293 + </p> 294 + </div> 295 + <div> 296 + <Toggle onClick={toggleExpanded} enabled={isExpanded} /> 297 + </div> 298 + </div> 299 + {isExpanded ? ( 300 + <> 301 + <Divider marginClass="my-6 px-8 box-content -mx-8" /> 302 + <p className="text-white font-bold mb-3"> 303 + {t("settings.connections.realdebrid.tokenLabel", "API Key")} 304 + </p> 305 + <div className="flex items-center w-full"> 306 + <StatusCircle type={statusMap[status]} className="mx-2 mr-4" /> 307 + <AuthInputBox 308 + onChange={(newToken) => { 309 + setRealDebridKey(newToken); 310 + }} 311 + value={realDebridKey ?? ""} 312 + placeholder="API Key" 313 + passwordToggleable 314 + className="flex-grow" 315 + /> 316 + </div> 317 + {status === "error" && ( 318 + <p className="text-type-danger mt-4"> 319 + {t( 320 + "settings.connections.realdebrid.status.failure", 321 + "Failed to connect to Real Debrid. Please check your API key.", 322 + )} 323 + </p> 324 + )} 325 + {status === "api_down" && ( 326 + <p className="text-type-danger mt-4"> 327 + {t( 328 + "settings.connections.realdebrid.status.api_down", 329 + "Real Debrid API is currently unavailable. Please try again later.", 330 + )} 331 + </p> 332 + )} 333 + {status === "invalid_token" && ( 334 + <p className="text-type-danger mt-4"> 335 + {t( 336 + "settings.connections.realdebrid.status.invalid_token", 337 + "Invalid API key or non-premium account. Real Debrid requires a premium account.", 338 + )} 339 + </p> 340 + )} 221 341 </> 222 342 ) : null} 223 343 </SettingsCard> 224 344 </div> 225 345 ); 226 346 } 347 + return null; 227 348 } 228 349 229 350 function Item(props: { title: string; children: React.ReactNode }) { ··· 474 595 )} 475 596 </div> 476 597 598 + {/* <RealDebridSetup /> */} 477 599 <FEDAPISetup /> 478 600 </BiggerCenterContainer> 479 601 </MinimalPageLayout>
+138 -1
src/pages/parts/settings/ConnectionsPart.tsx
··· 7 7 } from "react"; 8 8 import { Trans, useTranslation } from "react-i18next"; 9 9 10 + import { isExtensionActive } from "@/backend/extension/messaging"; 10 11 import { Button } from "@/components/buttons/Button"; 11 12 import { Toggle } from "@/components/buttons/Toggle"; 12 13 import { Icon, Icons } from "@/components/Icon"; ··· 23 24 SetupPart, 24 25 Status, 25 26 testFebboxKey, 27 + testRealDebridKey, 26 28 } from "@/pages/parts/settings/SetupPart"; 27 29 import { conf } from "@/setup/config"; 28 30 import { useAuthStore } from "@/stores/auth"; ··· 43 45 interface FebboxKeyProps { 44 46 febboxKey: string | null; 45 47 setFebboxKey: Dispatch<SetStateAction<string | null>>; 48 + } 49 + 50 + interface RealDebridKeyProps { 51 + realDebridKey: string | null; 52 + setRealDebridKey: Dispatch<SetStateAction<string | null>>; 46 53 } 47 54 48 55 function ProxyEdit({ ··· 368 375 } 369 376 } 370 377 378 + async function getRealDebridKeyStatus(realDebridKey: string | null) { 379 + if (realDebridKey) { 380 + const status: Status = await testRealDebridKey(realDebridKey); 381 + return status; 382 + } 383 + return "unset"; 384 + } 385 + 386 + function RealDebridKeyEdit({ 387 + realDebridKey, 388 + setRealDebridKey, 389 + }: RealDebridKeyProps) { 390 + const { t } = useTranslation(); 391 + const user = useAuthStore(); 392 + const preferences = usePreferencesStore(); 393 + const [hasExtension, setHasExtension] = useState(false); 394 + 395 + // Check for extension 396 + useEffect(() => { 397 + isExtensionActive().then(setHasExtension); 398 + }, []); 399 + 400 + // Enable Real Debrid token when account is loaded and we have a token 401 + useEffect(() => { 402 + if (user.account && realDebridKey === null && preferences.realDebridKey) { 403 + setRealDebridKey(preferences.realDebridKey); 404 + } 405 + }, [ 406 + user.account, 407 + realDebridKey, 408 + preferences.realDebridKey, 409 + setRealDebridKey, 410 + ]); 411 + 412 + const [status, setStatus] = useState<Status>("unset"); 413 + const statusMap: Record<Status, StatusCircleProps["type"]> = { 414 + error: "error", 415 + success: "success", 416 + unset: "noresult", 417 + api_down: "error", 418 + invalid_token: "error", 419 + }; 420 + 421 + useEffect(() => { 422 + const checkTokenStatus = async () => { 423 + const result = await getRealDebridKeyStatus(realDebridKey); 424 + setStatus(result); 425 + }; 426 + checkTokenStatus(); 427 + }, [realDebridKey]); 428 + 429 + if (conf().ALLOW_REAL_DEBRID_KEY) { 430 + return ( 431 + <SettingsCard> 432 + <div className="flex justify-between items-center gap-4"> 433 + <div className="my-3"> 434 + <p className="text-white font-bold mb-3">{t("realdebrid.title")}</p> 435 + <p className="max-w-[30rem] font-medium"> 436 + {t("realdebrid.description")} 437 + </p> 438 + <MwLink> 439 + <a 440 + href="https://real-debrid.com/" 441 + target="_blank" 442 + rel="noreferrer" 443 + > 444 + real-debrid.com 445 + </a> 446 + </MwLink> 447 + </div> 448 + <div className="flex items-center gap-3"> 449 + {!hasExtension && <Icon icon={Icons.UNPLUG} className="text-lg" />} 450 + <Toggle 451 + onClick={() => 452 + hasExtension 453 + ? setRealDebridKey((s) => (s === null ? "" : null)) 454 + : null 455 + } 456 + enabled={realDebridKey !== null && hasExtension} 457 + /> 458 + </div> 459 + </div> 460 + {realDebridKey !== null && hasExtension ? ( 461 + <> 462 + <Divider marginClass="my-6 px-8 box-content -mx-8" /> 463 + <p className="text-white font-bold mb-3"> 464 + {t("realdebrid.tokenLabel")} 465 + </p> 466 + <div className="flex items-center w-full"> 467 + <StatusCircle type={statusMap[status]} className="mx-2 mr-4" /> 468 + <AuthInputBox 469 + onChange={(newToken) => { 470 + setRealDebridKey(newToken); 471 + }} 472 + value={realDebridKey ?? ""} 473 + placeholder="ABC123..." 474 + passwordToggleable 475 + className="flex-grow" 476 + /> 477 + </div> 478 + {status === "error" && ( 479 + <p className="text-type-danger mt-4"> 480 + {t("realdebrid.status.failure")} 481 + </p> 482 + )} 483 + {status === "api_down" && ( 484 + <p className="text-type-danger mt-4"> 485 + {t("realdebrid.status.api_down")} 486 + </p> 487 + )} 488 + {status === "invalid_token" && ( 489 + <p className="text-type-danger mt-4"> 490 + {t("realdebrid.status.invalid_token")} 491 + </p> 492 + )} 493 + </> 494 + ) : null} 495 + </SettingsCard> 496 + ); 497 + } 498 + return null; 499 + } 500 + 371 501 export function ConnectionsPart( 372 - props: BackendEditProps & ProxyEditProps & FebboxKeyProps, 502 + props: BackendEditProps & 503 + ProxyEditProps & 504 + FebboxKeyProps & 505 + RealDebridKeyProps, 373 506 ) { 374 507 const { t } = useTranslation(); 375 508 return ( ··· 386 519 <BackendEdit 387 520 backendUrl={props.backendUrl} 388 521 setBackendUrl={props.setBackendUrl} 522 + /> 523 + <RealDebridKeyEdit 524 + realDebridKey={props.realDebridKey} 525 + setRealDebridKey={props.setRealDebridKey} 389 526 /> 390 527 <FebboxKeyEdit 391 528 febboxKey={props.febboxKey}
+64 -4
src/pages/parts/settings/SetupPart.tsx
··· 6 6 import { useAsync } from "react-use"; 7 7 8 8 import { isExtensionActive } from "@/backend/extension/messaging"; 9 - import { singularProxiedFetch } from "@/backend/helpers/fetch"; 9 + import { proxiedFetch, singularProxiedFetch } from "@/backend/helpers/fetch"; 10 10 import { Button } from "@/components/buttons/Button"; 11 11 import { Icon, Icons } from "@/components/Icon"; 12 12 import { Loading } from "@/components/layout/Loading"; ··· 74 74 proxy: Status; 75 75 defaultProxy: Status; 76 76 febboxKeyTest?: Status; 77 + realDebridKeyTest?: Status; 77 78 }; 78 79 79 80 function testProxy(url: string) { ··· 174 175 return "api_down"; 175 176 } 176 177 178 + export async function testRealDebridKey( 179 + realDebridKey: string | null, 180 + ): Promise<Status> { 181 + if (!realDebridKey) { 182 + return "unset"; 183 + } 184 + 185 + const maxAttempts = 2; 186 + let attempts = 0; 187 + 188 + while (attempts < maxAttempts) { 189 + try { 190 + console.log(`RD API attempt ${attempts + 1}`); 191 + const data = await proxiedFetch( 192 + "https://api.real-debrid.com/rest/1.0/user", 193 + { 194 + method: "GET", 195 + headers: { 196 + Authorization: `Bearer ${realDebridKey}`, 197 + "Content-Type": "application/json", 198 + }, 199 + }, 200 + ); 201 + 202 + // If we have data and it indicates premium status, return success immediately 203 + if (data && typeof data === "object" && data.type === "premium") { 204 + console.log("RD premium status confirmed"); 205 + return "success"; 206 + } 207 + 208 + console.log("RD response did not indicate premium status"); 209 + attempts += 1; 210 + if (attempts === maxAttempts) { 211 + return "invalid_token"; 212 + } 213 + await sleep(3000); 214 + } catch (error) { 215 + console.error("RD API error:", error); 216 + attempts += 1; 217 + if (attempts === maxAttempts) { 218 + return "api_down"; 219 + } 220 + await sleep(3000); 221 + } 222 + } 223 + 224 + return "api_down"; 225 + } 226 + 177 227 function useIsSetup() { 178 228 const proxyUrls = useAuthStore((s) => s.proxySet); 179 229 const febboxKey = usePreferencesStore((s) => s.febboxKey); 230 + const realDebridKey = usePreferencesStore((s) => s.realDebridKey); 180 231 const { loading, value } = useAsync(async (): Promise<SetupData> => { 181 232 const extensionStatus: Status = (await isExtensionActive()) 182 233 ? "success" ··· 192 243 } 193 244 194 245 const febboxKeyStatus: Status = await testFebboxKey(febboxKey); 246 + const realDebridKeyStatus: Status = await testRealDebridKey(realDebridKey); 195 247 196 248 return { 197 249 extension: extensionStatus, ··· 200 252 ...(conf().ALLOW_FEBBOX_KEY && { 201 253 febboxKeyTest: febboxKeyStatus, 202 254 }), 255 + realDebridKeyTest: realDebridKeyStatus, 203 256 }; 204 - }, [proxyUrls, febboxKey]); 257 + }, [proxyUrls, febboxKey, realDebridKey]); 205 258 206 259 let globalState: Status = "unset"; 207 260 if ( 208 261 value?.extension === "success" || 209 262 value?.proxy === "success" || 210 - value?.febboxKeyTest === "success" 263 + value?.febboxKeyTest === "success" || 264 + value?.realDebridKeyTest === "success" 211 265 ) 212 266 globalState = "success"; 213 267 if ( 214 268 value?.proxy === "error" || 215 269 value?.extension === "error" || 216 - value?.febboxKeyTest === "error" 270 + value?.febboxKeyTest === "error" || 271 + value?.realDebridKeyTest === "error" 217 272 ) 218 273 globalState = "error"; 219 274 ··· 354 409 > 355 410 {t("settings.connections.setup.items.default")} 356 411 </SetupCheckList> 412 + {conf().ALLOW_REAL_DEBRID_KEY && ( 413 + <SetupCheckList status={setupStates.realDebridKeyTest || "unset"}> 414 + Real Debrid token 415 + </SetupCheckList> 416 + )} 357 417 {conf().ALLOW_FEBBOX_KEY && ( 358 418 <SetupCheckList status={setupStates.febboxKeyTest || "unset"}> 359 419 Febbox UI token
+4
src/setup/config.ts
··· 26 26 ONBOARDING_PROXY_INSTALL_LINK: string; 27 27 ALLOW_AUTOPLAY: boolean; 28 28 ALLOW_FEBBOX_KEY: boolean; 29 + ALLOW_REAL_DEBRID_KEY: boolean; 29 30 SHOW_AD: boolean; 30 31 AD_CONTENT_URL: string; 31 32 TRACK_SCRIPT: string; ··· 38 39 DMCA_EMAIL: string | null; 39 40 TWITTER_LINK: string; 40 41 TMDB_READ_API_KEY: string | null; 42 + ALLOW_REAL_DEBRID_KEY: boolean; 41 43 NORMAL_ROUTER: boolean; 42 44 PROXY_URLS: string[]; 43 45 M3U8_PROXY_URLS: string[]; ··· 79 81 HAS_ONBOARDING: import.meta.env.VITE_HAS_ONBOARDING, 80 82 ALLOW_AUTOPLAY: import.meta.env.VITE_ALLOW_AUTOPLAY, 81 83 ALLOW_FEBBOX_KEY: import.meta.env.VITE_ALLOW_FEBBOX_KEY, 84 + ALLOW_REAL_DEBRID_KEY: import.meta.env.VITE_ALLOW_REAL_DEBRID_KEY, 82 85 SHOW_AD: import.meta.env.VITE_SHOW_AD, 83 86 AD_CONTENT_URL: import.meta.env.VITE_AD_CONTENT_URL, 84 87 TRACK_SCRIPT: import.meta.env.VITE_TRACK_SCRIPT, ··· 147 150 ) 148 151 .filter((v) => v.length === 2), // The format is <beforeA>:<afterA>,<beforeB>:<afterB> 149 152 ALLOW_FEBBOX_KEY: getKey("ALLOW_FEBBOX_KEY", "false") === "true", 153 + ALLOW_REAL_DEBRID_KEY: getKey("ALLOW_REAL_DEBRID_KEY", "false") === "true", 150 154 SHOW_AD: getKey("SHOW_AD", "false") === "true", 151 155 AD_CONTENT_URL: getKey("AD_CONTENT_URL", "") 152 156 .split(",")
+8
src/stores/preferences/index.tsx
··· 15 15 enableSourceOrder: boolean; 16 16 proxyTmdb: boolean; 17 17 febboxKey: string | null; 18 + realDebridKey: string | null; 18 19 19 20 setEnableThumbnails(v: boolean): void; 20 21 setEnableAutoplay(v: boolean): void; ··· 28 29 setEnableSourceOrder(v: boolean): void; 29 30 setProxyTmdb(v: boolean): void; 30 31 setFebboxKey(v: string | null): void; 32 + setRealDebridKey(v: string | null): void; 31 33 } 32 34 33 35 export const usePreferencesStore = create( ··· 45 47 enableSourceOrder: false, 46 48 proxyTmdb: false, 47 49 febboxKey: null, 50 + realDebridKey: null, 48 51 setEnableThumbnails(v) { 49 52 set((s) => { 50 53 s.enableThumbnails = v; ··· 103 106 setFebboxKey(v) { 104 107 set((s) => { 105 108 s.febboxKey = v; 109 + }); 110 + }, 111 + setRealDebridKey(v) { 112 + set((s) => { 113 + s.realDebridKey = v; 106 114 }); 107 115 }, 108 116 })),