import type { BrowserWindow } from 'electron'; import type { ResolvedConfig } from '../../types'; export type BuildAnilistSetupUrlDeps = { authorizeUrl: string; clientId: string; responseType: string; redirectUri?: string; }; export type ConsumeAnilistSetupCallbackUrlDeps = { rawUrl: string; saveToken: (token: string) => void; setCachedToken: (token: string) => void; setResolvedState: (resolvedAt: number) => void; setSetupPageOpened: (opened: boolean) => void; onSuccess: () => void; closeWindow: () => void; }; export function isAnilistTrackingEnabled(resolved: ResolvedConfig): boolean { return resolved.anilist.enabled; } export function buildAnilistSetupUrl(params: BuildAnilistSetupUrlDeps): string { const authorizeUrl = new URL(params.authorizeUrl); authorizeUrl.searchParams.set('client_id', params.clientId); authorizeUrl.searchParams.set('response_type', params.responseType); if (params.redirectUri && params.redirectUri.trim().length > 0) { authorizeUrl.searchParams.set('redirect_uri', params.redirectUri); } return authorizeUrl.toString(); } export function extractAnilistAccessTokenFromUrl(rawUrl: string): string | null { try { const parsed = new URL(rawUrl); const fromQuery = parsed.searchParams.get('access_token')?.trim(); if (fromQuery && fromQuery.length > 0) { return fromQuery; } const hash = parsed.hash.startsWith('#') ? parsed.hash.slice(1) : parsed.hash; if (hash.length === 0) { return null; } const hashParams = new URLSearchParams(hash); const fromHash = hashParams.get('access_token')?.trim(); if (fromHash && fromHash.length > 0) { return fromHash; } return null; } catch { return null; } } export function findAnilistSetupDeepLinkArgvUrl(argv: readonly string[]): string | null { for (const value of argv) { if (value.startsWith('subminer://anilist-setup')) { return value; } } return null; } export function consumeAnilistSetupCallbackUrl(deps: ConsumeAnilistSetupCallbackUrlDeps): boolean { const token = extractAnilistAccessTokenFromUrl(deps.rawUrl); if (!token) { return false; } const resolvedAt = Date.now(); deps.saveToken(token); deps.setCachedToken(token); deps.setResolvedState(resolvedAt); deps.setSetupPageOpened(false); deps.onSuccess(); deps.closeWindow(); return true; } export function openAnilistSetupInBrowser(params: { authorizeUrl: string; openExternal: (url: string) => Promise; logError: (message: string, error: unknown) => void; }): void { void params.openExternal(params.authorizeUrl).catch((error) => { params.logError('Failed to open AniList authorize URL in browser', error); }); } export function buildAnilistSetupFallbackHtml(params: { reason: string; authorizeUrl: string; developerSettingsUrl: string; }): string { const safeReason = params.reason.replace(//g, '>'); const safeAuth = params.authorizeUrl.replace(/"/g, '"'); const safeDev = params.developerSettingsUrl.replace(/"/g, '"'); return ` AniList Setup

AniList setup

Automatic page load failed (${safeReason}).

Open AniList authorize page

Open AniList developer settings

`; } export function buildAnilistManualTokenEntryHtml(params: { authorizeUrl: string; developerSettingsUrl: string; }): string { const safeAuth = params.authorizeUrl.replace(/"/g, '"'); const safeDev = params.developerSettingsUrl.replace(/"/g, '"'); return ` AniList Setup

AniList setup

Authorize in browser, then paste the access token below.

Open AniList authorize page

Open AniList developer settings



`; } export function loadAnilistSetupFallback(params: { setupWindow: BrowserWindow; reason: string; authorizeUrl: string; developerSettingsUrl: string; logWarn: (message: string, data: unknown) => void; }): void { const html = buildAnilistSetupFallbackHtml({ reason: params.reason, authorizeUrl: params.authorizeUrl, developerSettingsUrl: params.developerSettingsUrl, }); void params.setupWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`); params.logWarn('Loaded AniList setup fallback page', { reason: params.reason }); } export function loadAnilistManualTokenEntry(params: { setupWindow: BrowserWindow; authorizeUrl: string; developerSettingsUrl: string; logWarn: (message: string, data: unknown) => void; }): void { const html = buildAnilistManualTokenEntryHtml({ authorizeUrl: params.authorizeUrl, developerSettingsUrl: params.developerSettingsUrl, }); void params.setupWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`); params.logWarn('Loaded AniList manual token entry page', { authorizeUrl: params.authorizeUrl, }); }