"""Platform → download-backend dispatch (one place that knows which platforms are served by the native FC ingester vs. the gallery-dl subprocess). gallery-dl wasn't built to be driven by an automated scheduler — no native checkpoint/resume, no structured logs, per-file HEADs that dominate wall-clock. The native ingester (services/patreon_ingester.py, plan #697) replaces it for Patreon and is the path we grow as more platforms migrate. To keep that migration DRY, every caller that has to behave differently per backend — download routing, the credential-verify probe, cursor handling — asks THIS module instead of testing ``platform == "patreon"`` inline. When a platform gets a native ingester, it moves into ``NATIVE_INGESTER_PLATFORMS`` here and both the download path and verify switch over together. The backend surfaces share a UNIFORM signature so a caller invokes the same function regardless of platform: - verify_credential(...) → (ok: bool|None, message: str) - (download stays in download_service for now; uses_native_ingester() is the shared predicate it routes on, so the decision lives here too.) """ from __future__ import annotations from pathlib import Path # Platforms whose download + verify go through the native ingester rather than # gallery-dl. gallery-dl still serves every other platform (subscribestar, # hentaifoundry, discord, pixiv, deviantart) unchanged. NATIVE_INGESTER_PLATFORMS = frozenset({"patreon"}) def uses_native_ingester(platform: str) -> bool: """True when `platform` is served by the native ingester (not gallery-dl). The single predicate the download path and verify both route on.""" return platform in NATIVE_INGESTER_PLATFORMS async def verify_source_credential( *, platform: str, url: str, artist_slug: str, config_overrides: dict | None, cookies_path: str | None, auth_token: str | None, images_root: Path, ) -> tuple[bool | None, str]: """Uniform credential probe across backends. Returns `(ok, message)`: True = authenticated, False = rejected, None = inconclusive (drift / network / nothing to test). Callers don't branch on platform — they call this and render the result. """ if uses_native_ingester(platform): # Native ingester platforms verify via their own lightweight auth probe # (resolve campaign id + one authenticated API page). Patreon today. from .patreon_ingester import verify_patreon_credential return await verify_patreon_credential(url, cookies_path, config_overrides) # gallery-dl platforms: --simulate one item; the extractor errors before it # can list if auth is bad. from .gallery_dl import GalleryDLService, SourceConfig gdl = GalleryDLService(images_root=images_root) return await gdl.verify( url=url, artist_slug=artist_slug, platform=platform, source_config=SourceConfig.from_dict(config_overrides or {}), cookies_path=cookies_path, auth_token=auth_token, )