feat: unlock datanodes and vikingfile through the hydra api

Datanodes no longer scrapes the provider page and VikingFile no longer
calls Nimbus, which is gone. Both now go through the launcher API unlock
endpoints. VikingFile requires an active Hydra Cloud subscription, so the
downloader is gated behind it in both the desktop and big picture
download modals, with a badge and an upsell modal on click.
This commit is contained in:
Moyasee 2026-07-30 18:25:28 +03:00
parent a4f5df46f0
commit 8920e6e42f
21 changed files with 175 additions and 121 deletions

View file

@ -1,6 +1,5 @@
MAIN_VITE_API_URL=
MAIN_VITE_AUTH_URL=
MAIN_VITE_NIMBUS_API_URL=
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID=
RENDERER_VITE_TORBOX_REFERRAL_CODE=
MAIN_VITE_LAUNCHER_SUBDOMAIN=

View file

@ -43,6 +43,7 @@ import {
useNavigationScreenActions,
useBigPictureToast,
useUserPreferences,
useUserDetails,
} from "../../../hooks";
import { useNavigationStore, useVirtualKeyboardStore } from "../../../stores";
@ -1360,15 +1361,17 @@ function DownloadGameOptions({
const [isSubmitting, setIsSubmitting] = useState(false);
const { features } = useFeature();
const userPreferences = useUserPreferences();
const { hasActiveSubscription } = useUserDetails();
const isOptionsInteractionLocked = isSubmitting;
const availableDownloaderOptions = useMemo(() => {
return getDownloaderAvailabilityOptions(
option,
userPreferences,
features
features,
hasActiveSubscription
).filter((downloaderOption) => downloaderOption.isAvailable);
}, [features, option, userPreferences]);
}, [features, option, userPreferences, hasActiveSubscription]);
const downloaderItems = useMemo(() => {
return availableDownloaderOptions.map((downloaderOption) => ({

View file

@ -37,6 +37,17 @@ function isConfiguredDownloader(
return Boolean(preferences?.[preferenceKey]);
}
function isSubscriptionSatisfiedForDownloader(
downloader: Downloader,
hasActiveSubscription?: boolean
) {
if (downloader !== Downloader.VikingFile) {
return true;
}
return Boolean(hasActiveSubscription);
}
function isFeatureEnabledForDownloader(
downloader: Downloader,
enabledFeatures?: string[] | null
@ -89,7 +100,8 @@ export function sortAvailableDownloaders(
export function getDownloaderAvailabilityOptions(
repack: Pick<GameRepack, "uris" | "unavailableUris">,
preferences?: UserPreferences | null,
enabledFeatures?: string[] | null
enabledFeatures?: string[] | null,
hasActiveSubscription?: boolean
) {
const unavailableUrisSet = new Set(repack.unavailableUris ?? []);
const downloaderMap = new Map<
@ -144,7 +156,9 @@ export function getDownloaderAvailabilityOptions(
const status = downloaderMap.get(downloader);
const canHandle = status !== undefined;
const hasAvailableUri = status?.hasAvailable ?? false;
const isConfigured = isConfiguredDownloader(downloader, preferences);
const isConfigured =
isConfiguredDownloader(downloader, preferences) &&
isSubscriptionSatisfiedForDownloader(downloader, hasActiveSubscription);
const isAvailable = canHandle && hasAvailableUri && isConfigured;
return {

View file

@ -540,7 +540,6 @@
"download_error_alldebrid_rate_limit_exceeded": "Превишен е лимитът на AllDebrid. Моля, изчакай и опитай отново.",
"download_error_premiumize_unavailable": "Premiumize временно не е достъпен. Моля, опитай по-късно.",
"download_error_alldebrid_unavailable": "AllDebrid временно не е достъпен. Моля, опитай по-късно.",
"download_error_vikingfile_nimbus_quota_exceeded": "Квотата на Nimbus за VikingFile е изтекла за днес. Моля, опитайте отново утре.",
"download_error_invalid_magnet": "Невалиден магнет линк.",
"download_error_torrent_metadata_timeout": "Време за преобразуване на метаданни на торента. Моля, опитайте отново.",
"download_error_torrent_metadata_incomplete": "Метаданните на торента са непълни и не могат да бъдат обработени в момента.",

View file

@ -330,6 +330,8 @@
"retry_fetch_torrent_files": "Retry",
"downloader_online": "Online",
"downloader_not_configured": "Available but not configured",
"downloader_requires_hydra_cloud": "Requires an active Hydra Cloud subscription",
"hydra_cloud": "Hydra Cloud",
"downloader_offline": "Link is offline",
"downloader_not_available": "Not available",
"go_to_settings": "Go to Settings",
@ -581,7 +583,11 @@
"download_error_alldebrid_rate_limit_exceeded": "AllDebrid rate limit exceeded. Please wait and try again.",
"download_error_premiumize_unavailable": "Premiumize is temporarily unavailable. Please try again later.",
"download_error_alldebrid_unavailable": "AllDebrid is temporarily unavailable. Please try again later.",
"download_error_vikingfile_nimbus_quota_exceeded": "Nimbus quota for VikingFile has ended for today. Please try again tomorrow.",
"download_error_vikingfile_quota_exceeded": "You've reached the daily VikingFile limit. Please try again tomorrow.",
"download_error_vikingfile_subscription_required": "VikingFile downloads require an active Hydra Cloud subscription.",
"download_error_hoster_unlock_login_required": "You need to be signed in to download from this hoster.",
"download_error_hoster_unlock_file_not_found": "This file is no longer available on the hoster.",
"download_error_hoster_unlock_unavailable": "The hoster is unavailable right now. Please try again later.",
"download_error_invalid_magnet": "Invalid magnet link.",
"download_error_torrent_metadata_timeout": "Timed out while fetching torrent metadata. Please try again.",
"download_error_torrent_metadata_incomplete": "Torrent metadata is incomplete and cannot be processed right now.",

View file

@ -308,6 +308,8 @@
"retry_fetch_torrent_files": "Reintentar",
"downloader_online": "En línea",
"downloader_not_configured": "Disponible pero no configurado",
"downloader_requires_hydra_cloud": "Requiere una suscripción activa de Hydra Cloud",
"hydra_cloud": "Hydra Cloud",
"downloader_offline": "El enlace está fuera de línea",
"downloader_not_available": "No disponible",
"go_to_settings": "Ir a Ajustes",
@ -414,7 +416,11 @@
"download_error_alldebrid_rate_limit_exceeded": "Límite de solicitudes de AllDebrid excedido. Probá de nuevo más tarde.",
"download_error_premiumize_unavailable": "Premiumize está temporalmente fuera de servicio. Probá de nuevo más tarde.",
"download_error_alldebrid_unavailable": "AllDebrid está temporalmente fuera de servicio. Probá de nuevo más tarde.",
"download_error_vikingfile_nimbus_quota_exceeded": "La cuota de Nimbus para VikingFile se terminó por hoy. Probá de nuevo mañana.",
"download_error_vikingfile_quota_exceeded": "Alcanzaste el límite diario de VikingFile. Probá de nuevo mañana.",
"download_error_vikingfile_subscription_required": "Las descargas por VikingFile requieren una suscripción activa de Hydra Cloud.",
"download_error_hoster_unlock_login_required": "Necesitás iniciar sesión para descargar desde este host.",
"download_error_hoster_unlock_file_not_found": "Este archivo ya no está disponible en el host.",
"download_error_hoster_unlock_unavailable": "El host no está disponible en este momento. Probá de nuevo más tarde.",
"update_playtime_title": "Actualizar tiempo de juego",
"update_playtime_description": "Actualizar manualmente el tiempo de juego para {{game}}",
"update_playtime": "Actualizar tiempo de juego",

View file

@ -601,7 +601,6 @@
"select_all_files": "Tout sélectionner",
"clear_file_selection": "Effacer",
"retry_fetch_torrent_files": "Réessayer",
"download_error_vikingfile_nimbus_quota_exceeded": "Le quota Nimbus pour VikingFile est atteint pour aujourd'hui. Veuillez réessayer demain.",
"download_error_invalid_magnet": "Lien magnet invalide.",
"download_error_torrent_metadata_timeout": "Délai dépassé lors de la récupération des métadonnées du torrent. Veuillez réessayer.",
"download_error_torrent_metadata_incomplete": "Les métadonnées du torrent sont incomplètes et ne peuvent pas être traitées pour le moment.",

View file

@ -456,7 +456,6 @@
"download_error_alldebrid_rate_limit_exceeded": "Az AllDebrid sebességkorlátja túllépve. Várj egy kicsit, majd próbáld újra.",
"download_error_premiumize_unavailable": "A Premiumize átmenetileg nem elérhető. Próbáld meg később.",
"download_error_alldebrid_unavailable": "Az AllDebrid átmenetileg nem elérhető. Próbáld meg később.",
"download_error_vikingfile_nimbus_quota_exceeded": "A VikingFile Nimbus kvótája mára elfogyott. Próbáld újra holnap.",
"download_error_invalid_magnet": "Érvénytelen magnet link.",
"download_error_torrent_metadata_timeout": "Időtúllépés történt a torrent metaadatok lekérése közben. Próbáld újra.",
"download_error_torrent_metadata_incomplete": "A torrent metaadatai hiányosak, ezért jelenleg nem dolgozhatók fel.",

View file

@ -557,7 +557,6 @@
"download_error_alldebrid_rate_limit_exceeded": "Batas kecepatan AllDebrid terlampaui. Harap tunggu dan coba lagi.",
"download_error_premiumize_unavailable": "Premiumize sedang tidak tersedia untuk sementara. Silakan coba lagi nanti.",
"download_error_alldebrid_unavailable": "AllDebrid sedang tidak tersedia untuk sementara. Silakan coba lagi nanti.",
"download_error_vikingfile_nimbus_quota_exceeded": "Kuota Nimbus untuk VikingFile hari ini telah habis. Silakan coba lagi besok.",
"download_error_invalid_magnet": "Link magnet tidak valid.",
"download_error_torrent_metadata_timeout": "Waktu habis saat mengambil metadata torrent. Silakan coba lagi.",
"download_error_torrent_metadata_incomplete": "Metadata torrent tidak lengkap dan tidak dapat diproses saat ini.",

View file

@ -456,7 +456,6 @@
"download_error_alldebrid_rate_limit_exceeded": "AllDebridのレート制限を超えました。しばらく待ってから再試行してください。",
"download_error_premiumize_unavailable": "Premiumizeは一時的に利用できません。後でもう一度お試しください。",
"download_error_alldebrid_unavailable": "AllDebridは一時的に利用できません。後でもう一度お試しください。",
"download_error_vikingfile_nimbus_quota_exceeded": "VikingFileのNimbusクォータが本日終了しました。明日再試行してください。",
"download_error_invalid_magnet": "無効なマグネットリンクです。",
"download_error_torrent_metadata_timeout": "トレントメタデータの取得がタイムアウトしました。再試行してください。",
"download_error_torrent_metadata_incomplete": "トレントメタデータが不完全で現在処理できません。",

View file

@ -546,7 +546,6 @@
"protondb_tier_platinum": "Platynowy",
"protondb_tier_unknown": "Nieznany",
"protondb_badge_title": "ProtonDB {{tier}}",
"download_error_vikingfile_nimbus_quota_exceeded": "Limit Nimbus dla VikingFile na dziś się wyczerpał. Spróbuj ponownie jutro.",
"create_steam_shortcut_modal_vr_flag": "Gra VR (w Steamie: zaktualizuj flagę VR lub usuń/utwórz ponownie skrót)",
"create_steam_shortcut_modal_create_button": "Utwórz",
"create_steam_shortcut_modal_cancel_button": "Anuluj",

View file

@ -281,6 +281,8 @@
"retry_fetch_torrent_files": "Tentar novamente",
"downloader_online": "Online",
"downloader_not_configured": "Disponível mas não configurado",
"downloader_requires_hydra_cloud": "Requer uma assinatura ativa do Hydra Cloud",
"hydra_cloud": "Hydra Cloud",
"downloader_offline": "Link está offline",
"downloader_not_available": "Não disponível",
"go_to_settings": "Ir para Configurações",
@ -389,7 +391,11 @@
"download_error_alldebrid_rate_limit_exceeded": "Limite de requisições do AllDebrid excedido. Tente novamente mais tarde.",
"download_error_premiumize_unavailable": "Premiumize está temporariamente indisponível. Tente novamente mais tarde.",
"download_error_alldebrid_unavailable": "AllDebrid está temporariamente indisponível. Tente novamente mais tarde.",
"download_error_vikingfile_nimbus_quota_exceeded": "A cota do Nimbus para o VikingFile terminou hoje. Tente novamente amanhã.",
"download_error_vikingfile_quota_exceeded": "Você atingiu o limite diário do VikingFile. Tente novamente amanhã.",
"download_error_vikingfile_subscription_required": "Downloads pelo VikingFile exigem uma assinatura ativa do Hydra Cloud.",
"download_error_hoster_unlock_login_required": "Você precisa estar conectado para baixar desse hospedeiro.",
"download_error_hoster_unlock_file_not_found": "Este arquivo não está mais disponível no hospedeiro.",
"download_error_hoster_unlock_unavailable": "O hospedeiro está indisponível no momento. Tente novamente mais tarde.",
"download_error_not_cached_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.",
"download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.",
"game_removed_from_favorites": "Jogo removido dos favoritos",

View file

@ -236,6 +236,8 @@
"retry_fetch_torrent_files": "Tentar novamente",
"downloader_online": "Online",
"downloader_not_configured": "Disponível mas não configurado",
"downloader_requires_hydra_cloud": "Requer uma subscrição ativa do Hydra Cloud",
"hydra_cloud": "Hydra Cloud",
"downloader_offline": "Link offline",
"downloader_not_available": "Não disponível",
"go_to_settings": "Ir para Definições",
@ -409,7 +411,11 @@
"download_error_alldebrid_rate_limit_exceeded": "Limite de pedidos AllDebrid excedido. Por favor, tente novamente mais tarde.",
"download_error_premiumize_unavailable": "O Premiumize está temporariamente indisponível. Por favor, tente novamente mais tarde.",
"download_error_alldebrid_unavailable": "O AllDebrid está temporariamente indisponível. Por favor, tente novamente mais tarde.",
"download_error_vikingfile_nimbus_quota_exceeded": "A quota do Nimbus para o VikingFile terminou hoje. Por favor, tente novamente amanhã.",
"download_error_vikingfile_quota_exceeded": "Atingiu o limite diário do VikingFile. Por favor, tente novamente amanhã.",
"download_error_vikingfile_subscription_required": "As transferências pelo VikingFile exigem uma subscrição ativa do Hydra Cloud.",
"download_error_hoster_unlock_login_required": "Precisa de ter sessão iniciada para transferir deste anfitrião.",
"download_error_hoster_unlock_file_not_found": "Este ficheiro já não está disponível no anfitrião.",
"download_error_hoster_unlock_unavailable": "O anfitrião está indisponível de momento. Por favor, tente novamente mais tarde.",
"update_playtime_title": "Atualizar tempo de jogo",
"update_playtime_description": "Atualizar manualmente o tempo de jogo de {{game}}",
"update_playtime": "Atualizar tempo de jogo",

View file

@ -328,6 +328,8 @@
"retry_fetch_torrent_files": "Повторить",
"downloader_online": "Онлайн",
"downloader_not_configured": "Доступен, но не настроен",
"downloader_requires_hydra_cloud": "Требуется активная подписка Hydra Cloud",
"hydra_cloud": "Hydra Cloud",
"downloader_offline": "Ссылка недоступна",
"downloader_not_available": "Недоступно",
"go_to_settings": "Перейти в настройки",
@ -578,7 +580,11 @@
"download_error_alldebrid_rate_limit_exceeded": "Превышен лимит запросов AllDebrid. Пожалуйста, попробуйте позже.",
"download_error_premiumize_unavailable": "Premiumize временно недоступен. Пожалуйста, попробуйте позже.",
"download_error_alldebrid_unavailable": "AllDebrid временно недоступен. Пожалуйста, попробуйте позже.",
"download_error_vikingfile_nimbus_quota_exceeded": "Квота Nimbus для VikingFile на сегодня закончилась. Пожалуйста, попробуйте снова завтра.",
"download_error_vikingfile_quota_exceeded": "Вы достигли дневного лимита VikingFile. Пожалуйста, попробуйте снова завтра.",
"download_error_vikingfile_subscription_required": "Для загрузок через VikingFile нужна активная подписка Hydra Cloud.",
"download_error_hoster_unlock_login_required": "Чтобы скачивать с этого хостинга, нужно войти в аккаунт.",
"download_error_hoster_unlock_file_not_found": "Этот файл больше недоступен на хостинге.",
"download_error_hoster_unlock_unavailable": "Хостинг сейчас недоступен. Пожалуйста, попробуйте позже.",
"download_error_invalid_magnet": "Недействительная magnet-ссылка.",
"download_error_torrent_metadata_timeout": "Истекло время ожидания при получении метаданных торрента. Пожалуйста, попробуйте снова.",
"download_error_torrent_metadata_incomplete": "Метаданные торрента неполные и не могут быть обработаны в данный момент.",

View file

@ -1,9 +1,56 @@
import { AxiosError } from "axios";
import { Downloader, DownloadError } from "@shared";
import {
Downloader,
DownloadError,
SubscriptionRequiredError,
UserNotLoggedInError,
} from "@shared";
type DownloadErrorResult = { ok: false; error?: string };
const KNOWN_DOWNLOAD_ERRORS = new Set<string>(Object.values(DownloadError));
const HYDRA_UNLOCK_DOWNLOADERS = new Set<Downloader>([
Downloader.Datanodes,
Downloader.VikingFile,
]);
const handleHydraUnlockError = (
err: AxiosError,
downloader: Downloader
): DownloadErrorResult | null => {
if (!HYDRA_UNLOCK_DOWNLOADERS.has(downloader)) return null;
const status = err.response?.status;
const message = (err.response?.data as { message?: unknown } | undefined)
?.message;
if (status === 429 && downloader === Downloader.VikingFile) {
return { ok: false, error: DownloadError.VikingFileQuotaExceeded };
}
if (
status === 400 &&
downloader === Downloader.VikingFile &&
typeof message === "string"
) {
return { ok: false, error: DownloadError.VikingFileSubscriptionRequired };
}
if (status === 401) {
return { ok: false, error: DownloadError.HosterUnlockLoginRequired };
}
if (status === 404) {
return { ok: false, error: DownloadError.HosterUnlockFileNotFound };
}
if (status === 502) {
return { ok: false, error: DownloadError.HosterUnlockUnavailable };
}
return null;
};
const handleAxiosError = (
err: AxiosError,
downloader: Downloader
@ -83,9 +130,8 @@ const handleAxiosError = (
return { ok: false, error: DownloadError.AllDebridUnavailable };
}
if (err.response?.status === 429 && downloader === Downloader.VikingFile) {
return { ok: false, error: DownloadError.VikingFileNimbusQuotaExceeded };
}
const hydraUnlockResult = handleHydraUnlockError(err, downloader);
if (hydraUnlockResult) return hydraUnlockResult;
if (downloader === Downloader.TorBox) {
const data = err.response?.data as { detail?: string } | undefined;
@ -165,6 +211,20 @@ export const handleDownloadError = (
if (result) return result;
}
if (
err instanceof SubscriptionRequiredError &&
downloader === Downloader.VikingFile
) {
return { ok: false, error: DownloadError.VikingFileSubscriptionRequired };
}
if (
err instanceof UserNotLoggedInError &&
HYDRA_UNLOCK_DOWNLOADERS.has(downloader)
) {
return { ok: false, error: DownloadError.HosterUnlockLoginRequired };
}
if (err instanceof Error) {
if (downloader === Downloader.Torrent) {
const mapped = mapTorrentErrorCode(err.message);

View file

@ -1,72 +1,20 @@
import axios, { AxiosResponse } from "axios";
import { wrapper } from "axios-cookiejar-support";
import { CookieJar } from "tough-cookie";
import { logger } from "@main/services";
import { HydraApi } from "../hydra-api";
const DATANODES_USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0";
interface UnlockResponse {
link: string;
}
export class DatanodesApi {
private static readonly jar = new CookieJar();
public static async getDownloadUrl(uri: string): Promise<string> {
const response = await HydraApi.post<UnlockResponse>(
"/hosters/datanodes/unlock",
{ url: uri }
);
private static readonly session = wrapper(
axios.create({
jar: DatanodesApi.jar,
withCredentials: true,
})
);
public static async getDownloadUrl(downloadUrl: string): Promise<string> {
try {
const parsedUrl = new URL(downloadUrl);
const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
const fileCode = pathSegments[0];
if (!fileCode) {
throw new Error("Invalid datanodes URL");
}
await this.jar.setCookie("lang=english;", "https://datanodes.to");
const formData = new FormData();
formData.append("op", "download2");
formData.append("id", fileCode);
formData.append("rand", "");
formData.append("referer", "https://datanodes.to/download");
formData.append("method_free", "Free Download >>");
formData.append("method_premium", "");
formData.append("__dl", "1");
formData.append("g_captch__a", "1");
const response: AxiosResponse = await this.session.post(
"https://datanodes.to/download",
formData,
{
headers: {
accept: "*/*",
"accept-language": "en-US,en;q=0.9",
priority: "u=1, i",
"sec-ch-ua":
'"Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
Referer: "https://datanodes.to/download",
"User-Agent": DATANODES_USER_AGENT,
},
}
);
if (typeof response.data === "object" && response.data.url) {
return decodeURIComponent(response.data.url);
}
throw new Error("Failed to get the download link");
} catch (error) {
logger.error("Error fetching download URL:", error);
throw error;
if (!response?.link) {
throw new Error("Failed to unlock Datanodes URL");
}
return response.link;
}
}

View file

@ -1,46 +1,21 @@
import axios from "axios";
import { logger } from "../logger";
import { HydraApi } from "../hydra-api";
interface UnlockResponse {
link: string;
hoster: string;
}
export class VikingFileApi {
public static async getDownloadUrl(uri: string): Promise<string> {
const unlockResponse = await axios.post<UnlockResponse>(
`${import.meta.env.MAIN_VITE_NIMBUS_API_URL}/hosters/unlock`,
{ url: uri }
const response = await HydraApi.post<UnlockResponse>(
"/hosters/vikingfile/unlock",
{ url: uri },
{ needsSubscription: true }
);
if (!unlockResponse.data.link) {
if (!response?.link) {
throw new Error("Failed to unlock VikingFile URL");
}
const redirectUrl = unlockResponse.data.link;
try {
const redirectResponse = await axios.head(redirectUrl, {
maxRedirects: 0,
validateStatus: (status) =>
status === 301 || status === 302 || status === 200,
});
if (
redirectResponse.headers.location ||
redirectResponse.status === 301 ||
redirectResponse.status === 302
) {
return redirectResponse.headers.location || redirectUrl;
}
return redirectUrl;
} catch (error) {
logger.error(
`[VikingFile] Error following redirect, using redirect URL:`,
error
);
return redirectUrl;
}
return response.link;
}
}

View file

@ -6,7 +6,6 @@ interface ImportMetaEnv {
readonly MAIN_VITE_AUTH_URL: string;
readonly MAIN_VITE_CHECKOUT_URL: string;
readonly MAIN_VITE_EXTERNAL_RESOURCES_URL: string;
readonly MAIN_VITE_NIMBUS_API_URL: string;
readonly MAIN_VITE_LAUNCHER_SUBDOMAIN: string;
readonly ELECTRON_RENDERER_URL: string;
}

View file

@ -23,7 +23,9 @@ import {
useDownload,
useFeature,
useToast,
useUserDetails,
} from "@renderer/hooks";
import { useSubscription } from "@renderer/hooks/use-subscription";
import {
DownloadError,
Downloader,
@ -299,6 +301,8 @@ export function DownloadSettingsModal({
const startAbortControllerRef = useRef<AbortController | null>(null);
const { isFeatureEnabled, Feature } = useFeature();
const { hasActiveSubscription } = useUserDetails();
const { showHydraCloudModal } = useSubscription();
const selectedUri = useMemo(() => {
if (!repack || selectedDownloader === null) return null;
@ -417,6 +421,8 @@ export function DownloadSettingsModal({
isConfigured = !!userPreferences?.allDebridApiToken;
} else if (downloader === Downloader.TorBox) {
isConfigured = !!userPreferences?.torBoxApiToken;
} else if (downloader === Downloader.VikingFile) {
isConfigured = hasActiveSubscription;
}
// } else if (downloader === Downloader.Hydra) {
// isConfigured = isFeatureEnabled(Feature.Nimbus);
@ -441,6 +447,7 @@ export function DownloadSettingsModal({
userPreferences?.premiumizeApiToken,
userPreferences?.allDebridApiToken,
userPreferences?.torBoxApiToken,
hasActiveSubscription,
isFeatureEnabled,
Feature,
]);
@ -1277,7 +1284,11 @@ export function DownloadSettingsModal({
<span
className={`download-settings-modal__availability-indicator download-settings-modal__availability-indicator--warning`}
data-tooltip-id={tooltipId}
data-tooltip-content={t("downloader_not_configured")}
data-tooltip-content={
option.downloader === Downloader.VikingFile
? t("downloader_requires_hydra_cloud")
: t("downloader_not_configured")
}
/>
);
}
@ -1333,6 +1344,17 @@ export function DownloadSettingsModal({
);
}
if (
option.downloader === Downloader.VikingFile &&
option.canHandle
) {
return (
<div className="download-settings-modal__recommendation-badge">
<Badge>{t("hydra_cloud")}</Badge>
</div>
);
}
return null;
};
@ -1363,6 +1385,11 @@ export function DownloadSettingsModal({
option.isAvailableButNotConfigured
) {
setShowRealDebridModal(true);
} else if (
option.downloader === Downloader.VikingFile &&
option.isAvailableButNotConfigured
) {
showHydraCloudModal("vikingfile");
} else {
setSelectedDownloader(option.downloader);
}

View file

@ -75,7 +75,11 @@ export enum DownloadError {
AllDebridRateLimitExceeded = "download_error_alldebrid_rate_limit_exceeded",
PremiumizeUnavailable = "download_error_premiumize_unavailable",
AllDebridUnavailable = "download_error_alldebrid_unavailable",
VikingFileNimbusQuotaExceeded = "download_error_vikingfile_nimbus_quota_exceeded",
VikingFileQuotaExceeded = "download_error_vikingfile_quota_exceeded",
VikingFileSubscriptionRequired = "download_error_vikingfile_subscription_required",
HosterUnlockLoginRequired = "download_error_hoster_unlock_login_required",
HosterUnlockFileNotFound = "download_error_hoster_unlock_file_not_found",
HosterUnlockUnavailable = "download_error_hoster_unlock_unavailable",
InvalidMagnet = "download_error_invalid_magnet",
TorrentMetadataTimeout = "download_error_torrent_metadata_timeout",
TorrentMetadataIncomplete = "download_error_torrent_metadata_incomplete",

View file

@ -16,7 +16,8 @@ export type HydraCloudFeature =
| "achievements"
| "backup"
| "achievements-points"
| "customization";
| "customization"
| "vikingfile";
export interface DiskUsage {
free: number;