Files
DemonGatanjieuandClaude Opus 5.5 6fb69294f2 Civitai: ask civitai.red when civitai.com does not answer
On an AutoDL server civitai.com timed out while civitai.red, Civitai's
other address with the same API, answered in about a second, so scans
there fell back to guessing from the files.

civitai_client.api_hosts gives both addresses, the one that answered
last first; the scan's lookups, import's and Model Check's
(download_sources._civitai_json) and the network test ask the other one
when the first does not answer at all. An HTTP error is Civitai's own
answer and is not asked again. The test answers which host answered,
and Settings says so when it was civitai.red.

The pictures stay on image.civitai.com, which such networks often cannot
reach either: after COVERS_DOWN_AFTER (2) covers in a row that would not
download, a scan fetches no more covers instead of waiting on each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:49:29 +08:00

63 lines
2.6 KiB
Python

"""GET /anomalous/civitai_check: whether this computer reaches Civitai now, through the proxy in
Settings -> Network when one is set (net_proxy.py); for Settings' "Test" button and the first-run
setup. One small request with the key, if one is set, so a key Civitai refuses is told apart;
when civitai.com does not answer, Civitai's other address (civitai.red) is asked too, and the
answer says which one answered (`host`). It goes online, so it answers this computer only
(local_guard LOCAL_GETS)."""
import asyncio
import time
import urllib.error
import urllib.request
from aiohttp import web
from .download_sources import civitai_headers
try:
from ..civitai_client import API_HOSTS, answered
from ..net_proxy import configured, failure, urlopen
except ImportError: # loaded outside the package (tests)
from civitai_client import API_HOSTS, answered
from net_proxy import configured, failure, urlopen
PROBE_PATH = "/api/v1/models?limit=1"
TIMEOUT = 8
def check_civitai():
"""{reachable, key ('set' | 'none' | 'refused'), ms, error, failure (net_proxy.failure), proxy,
host (the address that answered, or '')}."""
headers = civitai_headers()
key = "set" if "Authorization" in headers else "none"
started = time.monotonic()
def answer(reachable, key, error="", why="", host=""):
return {"reachable": reachable, "key": key, "ms": round((time.monotonic() - started) * 1000),
"error": error, "failure": why, "proxy": configured(), "host": host}
first = None
for host in API_HOSTS:
try:
with urlopen(urllib.request.Request(f"https://{host}{PROBE_PATH}", headers=headers), timeout=TIMEOUT) as response:
response.read(1024)
answered(host)
return answer(True, key, host=host)
except urllib.error.HTTPError as error:
# Civitai answered: the network is fine; 401/403 with a key means the key is refused.
answered(host)
if error.code in (401, 403) and key == "set":
return answer(True, "refused", f"HTTP {error.code}", failure(error), host)
return answer(error.code < 500, key, f"HTTP {error.code}", failure(error), host)
except (urllib.error.URLError, OSError, ValueError) as error:
reason = getattr(error, "reason", None) or error
first = first or answer(False, key, str(reason)[:200], failure(error))
return first
async def api_civitai_check(request):
return web.json_response(await asyncio.to_thread(check_civitai))
def register_routes(app):
app.router.add_get("/anomalous/civitai_check", api_civitai_check)