Spaces:
Runtime error
Runtime error
Upload 7 files
Browse files- README.md +1 -1
- app.py +2 -6
- civitai_to_hf.py +441 -104
README.md
CHANGED
|
@@ -4,7 +4,7 @@ emoji: 🤗
|
|
| 4 |
colorFrom: yellow
|
| 5 |
colorTo: blue
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 6.
|
| 8 |
python_version: "3.10.13"
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
|
|
|
| 4 |
colorFrom: yellow
|
| 5 |
colorTo: blue
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 6.13.0
|
| 8 |
python_version: "3.10.13"
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
app.py
CHANGED
|
@@ -35,10 +35,8 @@ from civitai_to_hf import (
|
|
| 35 |
load_all_civitai,
|
| 36 |
refresh_civitai_key_status,
|
| 37 |
probe_civitai_api,
|
| 38 |
-
probe_civitai_green_api,
|
| 39 |
update_civitai_gallery_mode,
|
| 40 |
begin_probe_civitai,
|
| 41 |
-
begin_probe_civitai_green,
|
| 42 |
clear_retry_state,
|
| 43 |
CIVITAI_TYPE,
|
| 44 |
CIVITAI_BASEMODEL,
|
|
@@ -156,13 +154,12 @@ with gr.Blocks(fill_width=True, delete_cache=(60, 3600)) as demo:
|
|
| 156 |
with gr.Row():
|
| 157 |
use_remaining_button = gr.Button(value="Use Remaining URLs", visible=True, variant="secondary")
|
| 158 |
use_failed_button = gr.Button(value="Retry Failed Only", visible=True, variant="secondary")
|
| 159 |
-
clear_remaining_button = gr.Button(value="Clear
|
| 160 |
with gr.Accordion("Probe / Experimental", open=False, visible=CIVITAI_HF_DEBUG):
|
| 161 |
probe_md = gr.Markdown(value="", visible=False)
|
| 162 |
with gr.Row():
|
| 163 |
smoke_test_button = gr.Button(value="Smoke Test", variant="secondary")
|
| 164 |
probe_button = gr.Button("Probe Civitai", variant="secondary")
|
| 165 |
-
probe_green_button = gr.Button("Probe civitai.green", variant="secondary")
|
| 166 |
gr.DuplicateButton(value="Duplicate Space")
|
| 167 |
|
| 168 |
heavy_outputs = [uploaded_urls, urls_md, urls_remain, urls_failed, civitai_key_status, session_state]
|
|
@@ -193,7 +190,7 @@ with gr.Blocks(fill_width=True, delete_cache=(60, 3600)) as demo:
|
|
| 193 |
stop_button.click(fn=None, cancels=[download_evt, smoke_evt], queue=False, api_visibility="undocumented")
|
| 194 |
use_remaining_button.click(use_remaining_urls, [urls_remain, dl_url], [dl_url], queue=False, api_visibility="undocumented")
|
| 195 |
use_failed_button.click(use_failed_urls, [urls_failed, dl_url], [dl_url], queue=False, api_visibility="undocumented")
|
| 196 |
-
clear_remaining_button.click(clear_retry_state, [session_state], [
|
| 197 |
|
| 198 |
gr.on(
|
| 199 |
triggers=[search_civitai_submit.click, search_civitai_query.submit],
|
|
@@ -218,7 +215,6 @@ with gr.Blocks(fill_width=True, delete_cache=(60, 3600)) as demo:
|
|
| 218 |
search_civitai_user.change(refresh_civitai_creators, [search_civitai_user, civitai_key], [search_civitai_user], queue=False, api_visibility="undocumented")
|
| 219 |
civitai_key.change(refresh_civitai_key_status, [civitai_key], [civitai_key_status], queue=False, api_visibility="undocumented")
|
| 220 |
probe_button.click(begin_probe_civitai, [], [probe_md], queue=False, api_visibility="undocumented").then(probe_civitai_api, [search_civitai_query, civitai_key], [probe_md, civitai_key_status], queue=False, api_visibility="undocumented")
|
| 221 |
-
probe_green_button.click(begin_probe_civitai_green, [], [probe_md], queue=False, api_visibility="undocumented").then(probe_civitai_green_api, [search_civitai_query, civitai_key], [probe_md, civitai_key_status], queue=False, api_visibility="undocumented")
|
| 222 |
|
| 223 |
|
| 224 |
demo.queue()
|
|
|
|
| 35 |
load_all_civitai,
|
| 36 |
refresh_civitai_key_status,
|
| 37 |
probe_civitai_api,
|
|
|
|
| 38 |
update_civitai_gallery_mode,
|
| 39 |
begin_probe_civitai,
|
|
|
|
| 40 |
clear_retry_state,
|
| 41 |
CIVITAI_TYPE,
|
| 42 |
CIVITAI_BASEMODEL,
|
|
|
|
| 154 |
with gr.Row():
|
| 155 |
use_remaining_button = gr.Button(value="Use Remaining URLs", visible=True, variant="secondary")
|
| 156 |
use_failed_button = gr.Button(value="Retry Failed Only", visible=True, variant="secondary")
|
| 157 |
+
clear_remaining_button = gr.Button(value="Clear Remaining", visible=True, variant="secondary")
|
| 158 |
with gr.Accordion("Probe / Experimental", open=False, visible=CIVITAI_HF_DEBUG):
|
| 159 |
probe_md = gr.Markdown(value="", visible=False)
|
| 160 |
with gr.Row():
|
| 161 |
smoke_test_button = gr.Button(value="Smoke Test", variant="secondary")
|
| 162 |
probe_button = gr.Button("Probe Civitai", variant="secondary")
|
|
|
|
| 163 |
gr.DuplicateButton(value="Duplicate Space")
|
| 164 |
|
| 165 |
heavy_outputs = [uploaded_urls, urls_md, urls_remain, urls_failed, civitai_key_status, session_state]
|
|
|
|
| 190 |
stop_button.click(fn=None, cancels=[download_evt, smoke_evt], queue=False, api_visibility="undocumented")
|
| 191 |
use_remaining_button.click(use_remaining_urls, [urls_remain, dl_url], [dl_url], queue=False, api_visibility="undocumented")
|
| 192 |
use_failed_button.click(use_failed_urls, [urls_failed, dl_url], [dl_url], queue=False, api_visibility="undocumented")
|
| 193 |
+
clear_remaining_button.click(clear_retry_state, [session_state], [urls_remain, urls_failed, session_state], queue=False, api_visibility="undocumented")
|
| 194 |
|
| 195 |
gr.on(
|
| 196 |
triggers=[search_civitai_submit.click, search_civitai_query.submit],
|
|
|
|
| 215 |
search_civitai_user.change(refresh_civitai_creators, [search_civitai_user, civitai_key], [search_civitai_user], queue=False, api_visibility="undocumented")
|
| 216 |
civitai_key.change(refresh_civitai_key_status, [civitai_key], [civitai_key_status], queue=False, api_visibility="undocumented")
|
| 217 |
probe_button.click(begin_probe_civitai, [], [probe_md], queue=False, api_visibility="undocumented").then(probe_civitai_api, [search_civitai_query, civitai_key], [probe_md, civitai_key_status], queue=False, api_visibility="undocumented")
|
|
|
|
| 218 |
|
| 219 |
|
| 220 |
demo.queue()
|
civitai_to_hf.py
CHANGED
|
@@ -45,8 +45,13 @@ SEARCH_DETAIL_SIZE = (960, 1280)
|
|
| 45 |
LOAD_ALL_BATCH_SIZE = 8
|
| 46 |
NULL_IMAGE_PATH = str(Path(__file__).with_name("null.png"))
|
| 47 |
CIVITAI_DEFAULT_ORIGIN = "https://civitai.com"
|
|
|
|
|
|
|
| 48 |
CIVITAI_GREEN_ORIGIN = "https://civitai.green"
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
| 50 |
PREVIEW_VIDEO_EXTS = {".mp4", ".webm", ".mov", ".m4v", ".avi", ".mkv"}
|
| 51 |
_FFMPEG_PATH = None
|
| 52 |
_FFMPEG_MISSING_LOGGED = False
|
|
@@ -59,6 +64,255 @@ RUN_CANCEL_REGISTRY = {}
|
|
| 59 |
RUN_CANCEL_LOCK = threading.Lock()
|
| 60 |
CREATOR_SUGGEST_CACHE = {}
|
| 61 |
CREATOR_SUGGEST_LOCK = threading.Lock()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
|
| 64 |
CIVITAI_TYPE = ["Checkpoint", "TextualInversion", "Hypernetwork", "AestheticGradient", "LORA", "LoCon", "DoRA",
|
|
@@ -395,7 +649,7 @@ def build_run_outputs(urls, md: str, remain_urls, failed_urls, civitai_key, sess
|
|
| 395 |
final_md = md.strip()
|
| 396 |
if final_md:
|
| 397 |
final_md += "\n"
|
| 398 |
-
return gr.update(value=urls, choices=urls),
|
| 399 |
|
| 400 |
|
| 401 |
def set_stage_progress(progress, current: int, total: int, desc: str):
|
|
@@ -546,7 +800,7 @@ def get_civitai_url_parts(url: str):
|
|
| 546 |
return urllib.parse.urlsplit("")
|
| 547 |
|
| 548 |
def is_civitai_host(netloc: str):
|
| 549 |
-
return
|
| 550 |
|
| 551 |
def is_civitai_download_api_path(path: str):
|
| 552 |
return re.match(r'^/api/download/models/\d+$', str(path or "").strip()) is not None
|
|
@@ -569,7 +823,7 @@ def extract_civitai_model_version_id(url: str):
|
|
| 569 |
def to_civitai_default_download_url(version_id: str, query: str = ""):
|
| 570 |
if not str(version_id or "").isdigit():
|
| 571 |
return ""
|
| 572 |
-
base = f"{
|
| 573 |
return f"{base}?{query}" if query else base
|
| 574 |
|
| 575 |
def normalize_civitai_download_api_url(url: str):
|
|
@@ -583,7 +837,7 @@ def extract_first_civitai_download_url_from_html(html: str):
|
|
| 583 |
return ""
|
| 584 |
page = html_lib.unescape(str(html))
|
| 585 |
patterns = [
|
| 586 |
-
r'https?://(?:www\.)?(?:civitai\.com|civitai\.green)/api/download/models/\d+[^\s\'"<>)\]]*',
|
| 587 |
r"[\"'](/api/download/models/\d+[^\"']*)[\"']",
|
| 588 |
]
|
| 589 |
for pattern in patterns:
|
|
@@ -612,7 +866,7 @@ def resolve_civitai_model_page_to_download_url(url: str, api_key: str = ""):
|
|
| 612 |
version_id = extract_civitai_model_version_id(raw)
|
| 613 |
if version_id:
|
| 614 |
return to_civitai_default_download_url(version_id)
|
| 615 |
-
headers = get_civitai_headers(api_key if parts.netloc
|
| 616 |
headers["Referer"] = f"{parts.scheme or 'https'}://{parts.netloc}/"
|
| 617 |
session = create_retry_session(total=4, backoff_factor=0.8)
|
| 618 |
try:
|
|
@@ -643,18 +897,18 @@ def get_civitai_sha256(dl_url: str, api_key=""):
|
|
| 643 |
return k in qs.keys() and qs[k][0] != json.get(k, None) and json.get(k, None) is not None
|
| 644 |
|
| 645 |
if "https://civitai.com/api/download/models/" not in dl_url: return None
|
| 646 |
-
|
| 647 |
params = {}
|
| 648 |
session = create_retry_session(total=6, backoff_factor=1.0)
|
| 649 |
m = re.match(r'https://civitai.com/api/download/models/(\d+)\??(.+)?', dl_url)
|
| 650 |
if m is None: return None
|
| 651 |
-
url =
|
| 652 |
qs = urllib.parse.parse_qs(m.group(2))
|
| 653 |
if "type" not in qs.keys(): qs["type"] = ["Model"]
|
| 654 |
try:
|
| 655 |
-
r =
|
| 656 |
if not r.ok: return None
|
| 657 |
-
json = dict(
|
| 658 |
if "files" not in json.keys() or not isinstance(json["files"], list): return None
|
| 659 |
hash = None
|
| 660 |
for d in json["files"]:
|
|
@@ -1081,7 +1335,7 @@ def sort_civitai_basemodels(items: list[str]):
|
|
| 1081 |
|
| 1082 |
|
| 1083 |
def fetch_civitai_basemodels(api_key: str = "", pages_per_sort: int = CIVITAI_BASEMODEL_REFRESH_PAGES_PER_SORT):
|
| 1084 |
-
|
| 1085 |
observed = set()
|
| 1086 |
session = create_retry_session(total=6, backoff_factor=1.0)
|
| 1087 |
seeds = [("Newest", "AllTime"), ("Most Downloaded", "AllTime")]
|
|
@@ -1091,13 +1345,13 @@ def fetch_civitai_basemodels(api_key: str = "", pages_per_sort: int = CIVITAI_BA
|
|
| 1091 |
params = {'sort': sort, 'period': period, 'limit': 100, 'page': page_index, 'nsfw': 'true'}
|
| 1092 |
try:
|
| 1093 |
if next_url is None:
|
| 1094 |
-
r =
|
| 1095 |
else:
|
| 1096 |
-
r =
|
| 1097 |
if not r.ok:
|
| 1098 |
print(f"Failed to refresh Civitai base models. sort={sort} page={page_index} status={r.status_code}")
|
| 1099 |
break
|
| 1100 |
-
data =
|
| 1101 |
items = data.get('items', [])
|
| 1102 |
if not isinstance(items, list): break
|
| 1103 |
for item in items:
|
|
@@ -1380,8 +1634,6 @@ def begin_probe_feedback(title: str):
|
|
| 1380 |
return gr.update(value=f"### {title}\n- status: probing...", visible=True)
|
| 1381 |
def begin_probe_civitai():
|
| 1382 |
return begin_probe_feedback("Civitai Probe")
|
| 1383 |
-
def begin_probe_civitai_green():
|
| 1384 |
-
return begin_probe_feedback("civitai.green Probe")
|
| 1385 |
def begin_probe_civitai_keys():
|
| 1386 |
return begin_probe_feedback("Civitai Key Probe")
|
| 1387 |
def begin_probe_civitai_url():
|
|
@@ -1509,7 +1761,7 @@ def load_all_civitai(state: dict, api_key: str = ""):
|
|
| 1509 |
def search_on_civitai(query: str, types: list[str], allow_model: list[str] = [], limit: int = 100,
|
| 1510 |
sort: str = "Highest Rated", period: str = "AllTime", tag: str = "", user: str = "", page: int = 1,
|
| 1511 |
filetype: list[str] = [], api_key: str = "", base_origin: str = CIVITAI_DEFAULT_ORIGIN, progress=gr.Progress(track_tqdm=False)):
|
| 1512 |
-
|
| 1513 |
api_sort = sort if sort in {"Highest Rated", "Most Downloaded", "Newest"} else CIVITAI_SORT[0]
|
| 1514 |
params = {'sort': api_sort, 'period': period, 'limit': int(limit), 'nsfw': 'true'}
|
| 1515 |
clean_types = [str(t).strip() for t in (types or []) if str(t).strip()]
|
|
@@ -1530,19 +1782,19 @@ def search_on_civitai(query: str, types: list[str], allow_model: list[str] = [],
|
|
| 1530 |
if page == 0:
|
| 1531 |
progress(0, desc="Searching page 1...")
|
| 1532 |
print("Searching page 1...")
|
| 1533 |
-
r =
|
| 1534 |
rs.append(r)
|
| 1535 |
if r.ok:
|
| 1536 |
-
json =
|
| 1537 |
next_url = json['metadata']['nextPage'] if 'metadata' in json and 'nextPage' in json['metadata'] else None
|
| 1538 |
i = 2
|
| 1539 |
while next_url is not None:
|
| 1540 |
progress(0, desc=f"Searching page {i}...")
|
| 1541 |
print(f"Searching page {i}...")
|
| 1542 |
-
r =
|
| 1543 |
rs.append(r)
|
| 1544 |
if r.ok:
|
| 1545 |
-
json =
|
| 1546 |
next_url = json['metadata']['nextPage'] if 'metadata' in json and 'nextPage' in json['metadata'] else None
|
| 1547 |
else:
|
| 1548 |
next_url = None
|
|
@@ -1552,18 +1804,18 @@ def search_on_civitai(query: str, types: list[str], allow_model: list[str] = [],
|
|
| 1552 |
else:
|
| 1553 |
progress(0, desc="Searching page 1...")
|
| 1554 |
print("Searching page 1...")
|
| 1555 |
-
r =
|
| 1556 |
rs.append(r)
|
| 1557 |
except requests.exceptions.ConnectTimeout:
|
| 1558 |
print("Request timed out.")
|
| 1559 |
except Exception as e:
|
| 1560 |
print(e)
|
| 1561 |
items = []
|
| 1562 |
-
origin =
|
| 1563 |
for r in rs:
|
| 1564 |
if not r.ok:
|
| 1565 |
continue
|
| 1566 |
-
json =
|
| 1567 |
if 'items' not in json:
|
| 1568 |
continue
|
| 1569 |
for j in json['items']:
|
|
@@ -1586,9 +1838,7 @@ def search_on_civitai(query: str, types: list[str], allow_model: list[str] = [],
|
|
| 1586 |
base_item['img_url'] = images[0].get('url', '') or NULL_IMAGE_PATH
|
| 1587 |
else:
|
| 1588 |
base_item['img_url'] = NULL_IMAGE_PATH
|
| 1589 |
-
model_url =
|
| 1590 |
-
if model.get('id') is not None:
|
| 1591 |
-
model_url += f"?modelVersionId={model.get('id')}"
|
| 1592 |
desc = str(base_item.get('description', '') or '')
|
| 1593 |
base_item['md'] = (
|
| 1594 |
f"Model URL: [{model_url}]({model_url})<br>"
|
|
@@ -1603,7 +1853,7 @@ def search_on_civitai(query: str, types: list[str], allow_model: list[str] = [],
|
|
| 1603 |
if files:
|
| 1604 |
for f in files:
|
| 1605 |
item = base_item.copy()
|
| 1606 |
-
item['dl_url'] = f.get('downloadUrl', '')
|
| 1607 |
item['size_kb'] = f.get('sizeKB', 0.0)
|
| 1608 |
item['file_type'] = f.get('type', '')
|
| 1609 |
if len(filetype) != 0 and f.get('type', '') not in set(filetype):
|
|
@@ -1611,7 +1861,7 @@ def search_on_civitai(query: str, types: list[str], allow_model: list[str] = [],
|
|
| 1611 |
items.append(item)
|
| 1612 |
else:
|
| 1613 |
item = base_item.copy()
|
| 1614 |
-
item['dl_url'] = model.get('downloadUrl', '')
|
| 1615 |
items.append(item)
|
| 1616 |
if sort in CIVITAI_SORT_EXT:
|
| 1617 |
if sort == "Size":
|
|
@@ -1686,17 +1936,17 @@ def get_civitai_json(dl_url: str, is_html: bool=False, image_baseurl: str="", ap
|
|
| 1686 |
default = ("", "", "") if is_html else ""
|
| 1687 |
if "https://civitai.com/api/download/models/" not in dl_url:
|
| 1688 |
return default
|
| 1689 |
-
|
| 1690 |
params = {}
|
| 1691 |
session = create_retry_session(total=6, backoff_factor=1.0)
|
| 1692 |
model_id = re.sub(r'https://civitai.com/api/download/models/(\d+)(?:.+)?', r'\1', dl_url)
|
| 1693 |
-
url =
|
| 1694 |
try:
|
| 1695 |
reset_civitai_key_status(api_key, source='info')
|
| 1696 |
-
r =
|
| 1697 |
if not r.ok:
|
| 1698 |
return default
|
| 1699 |
-
json = dict(
|
| 1700 |
html = ""
|
| 1701 |
image = ""
|
| 1702 |
if "modelId" in json.keys():
|
|
@@ -1704,7 +1954,7 @@ def get_civitai_json(dl_url: str, is_html: bool=False, image_baseurl: str="", ap
|
|
| 1704 |
if is_civitai_host(original_parts.netloc) and re.match(r'^/models/\d+(?:/[^/?#]+)?/?$', original_parts.path or ""):
|
| 1705 |
url = original_dl_url
|
| 1706 |
else:
|
| 1707 |
-
url =
|
| 1708 |
r = civitai_get(session, url, api_key=api_key, params=params, timeout=(5.0, 60), label='Civitai model page', source='info')
|
| 1709 |
if not r.ok:
|
| 1710 |
return json, html, image
|
|
@@ -1742,12 +1992,13 @@ def get_civitai_json(dl_url: str, is_html: bool=False, image_baseurl: str="", ap
|
|
| 1742 |
return default
|
| 1743 |
|
| 1744 |
|
| 1745 |
-
def _load_civitai_choice_list(
|
| 1746 |
query: str="", api_key: str="", limit: int=200,
|
| 1747 |
timeout: tuple[float, float]=(7.0, 15.0), retry_total: int=6,
|
| 1748 |
backoff_factor: float=1.0, default: list[str] | None=None,
|
| 1749 |
cache: dict | None=None, cache_lock: object | None=None,
|
| 1750 |
-
cache_ttl_sec: int=0, startup_name: str=""
|
|
|
|
| 1751 |
clean_query = str(query or "").strip()
|
| 1752 |
cache_key = clean_query.casefold()
|
| 1753 |
now = time.time()
|
|
@@ -1762,12 +2013,22 @@ def _load_civitai_choice_list(base_url: str, label: str, source: str, value_key:
|
|
| 1762 |
params['query'] = clean_query
|
| 1763 |
session = create_retry_session(total=retry_total, backoff_factor=backoff_factor)
|
| 1764 |
try:
|
| 1765 |
-
r =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1766 |
if not r.ok:
|
| 1767 |
if not clean_query and startup_name:
|
| 1768 |
print(f"Failed to load {startup_name} from API at startup. HTTP {r.status_code}.")
|
| 1769 |
return default
|
| 1770 |
-
j = dict(
|
| 1771 |
items = j.get('items', []) if isinstance(j.get('items', []), list) else []
|
| 1772 |
observed = []
|
| 1773 |
for item in items:
|
|
@@ -1790,7 +2051,7 @@ def _load_civitai_choice_list(base_url: str, label: str, source: str, value_key:
|
|
| 1790 |
|
| 1791 |
def get_civitai_creator(query: str="", api_key: str="", limit: int=CREATOR_SUGGEST_LIMIT):
|
| 1792 |
return _load_civitai_choice_list(
|
| 1793 |
-
|
| 1794 |
label='Civitai creators',
|
| 1795 |
source='creators',
|
| 1796 |
value_key='username',
|
|
@@ -1806,6 +2067,7 @@ def get_civitai_creator(query: str="", api_key: str="", limit: int=CREATOR_SUGGE
|
|
| 1806 |
cache_lock=CREATOR_SUGGEST_LOCK,
|
| 1807 |
cache_ttl_sec=CREATOR_CACHE_TTL_SEC,
|
| 1808 |
startup_name='Civitai creators',
|
|
|
|
| 1809 |
)
|
| 1810 |
|
| 1811 |
|
|
@@ -1827,7 +2089,7 @@ def refresh_civitai_creators(user_value: str="", api_key: str=""):
|
|
| 1827 |
|
| 1828 |
def get_civitai_tag():
|
| 1829 |
return _load_civitai_choice_list(
|
| 1830 |
-
|
| 1831 |
label='Civitai tags',
|
| 1832 |
source='tags',
|
| 1833 |
value_key='name',
|
|
@@ -1961,6 +2223,60 @@ def extract_probe_first_download_url(payload: dict):
|
|
| 1961 |
return dl_url, model, version, {}
|
| 1962 |
return "", {}, {}, {}
|
| 1963 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1964 |
|
| 1965 |
def probe_civitai_keys(api_key: str):
|
| 1966 |
effective_api_key = get_effective_probe_api_key(api_key)
|
|
@@ -1975,10 +2291,12 @@ def probe_civitai_keys(api_key: str):
|
|
| 1975 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
| 1976 |
session = create_retry_session(total=4, backoff_factor=0.8)
|
| 1977 |
try:
|
|
|
|
|
|
|
| 1978 |
reset_civitai_key_status(effective_api_key, source="probe-keys")
|
| 1979 |
-
resp =
|
| 1980 |
session,
|
| 1981 |
-
|
| 1982 |
api_key=effective_api_key,
|
| 1983 |
params={"limit": 1, "sort": "Newest", "period": "AllTime", "nsfw": "true"},
|
| 1984 |
timeout=(7.0, 20.0),
|
|
@@ -2137,63 +2455,130 @@ def probe_civitai_sidecar(url_input: str, fallback_query: str, api_key: str):
|
|
| 2137 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
| 2138 |
|
| 2139 |
def probe_civitai_api(query: str, api_key: str):
|
| 2140 |
-
|
| 2141 |
effective_api_key = get_effective_probe_api_key(api_key)
|
| 2142 |
key_source = "input" if str(api_key or "").strip() else ("env" if effective_api_key else "none")
|
| 2143 |
reset_civitai_key_status(effective_api_key, source="probe")
|
| 2144 |
probe_query = str(query or "lora").strip() or "lora"
|
| 2145 |
print(f"Civitai Probe: query={probe_query!r} key_source={key_source}")
|
| 2146 |
-
lines = ["### Civitai Probe", f"- origin: {
|
| 2147 |
session = create_retry_session(total=4, backoff_factor=0.8)
|
|
|
|
|
|
|
| 2148 |
anon_ok = False
|
| 2149 |
try:
|
| 2150 |
-
page_resp = session.get(f"{
|
| 2151 |
lines.append(f"- anonymous page: {'ok' if page_resp.ok else 'fail'} ({page_resp.status_code})")
|
| 2152 |
if page_resp.ok:
|
| 2153 |
first_path = extract_first_model_path(page_resp.text)
|
| 2154 |
if first_path:
|
| 2155 |
lines.append(f"- anonymous first model: {first_path}")
|
| 2156 |
-
anon_resp =
|
| 2157 |
-
|
|
|
|
| 2158 |
params={"query": probe_query, "limit": 1, "sort": "Newest", "period": "AllTime", "nsfw": "true"},
|
| 2159 |
-
headers=get_civitai_headers(""),
|
| 2160 |
timeout=(7.0, 20.0),
|
|
|
|
|
|
|
| 2161 |
)
|
| 2162 |
-
|
| 2163 |
-
|
|
|
|
| 2164 |
except Exception as e:
|
| 2165 |
lines.append(f"- anonymous probe: fail ({type(e).__name__}: {e})")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2166 |
parsed_keys = parse_civitai_api_keys(effective_api_key)
|
| 2167 |
if not parsed_keys:
|
| 2168 |
lines.append("- auth resolve: skipped (no Civitai key)")
|
|
|
|
| 2169 |
probe_text = "\n".join(lines)
|
| 2170 |
print(probe_text)
|
| 2171 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
|
|
|
| 2172 |
try:
|
| 2173 |
reset_civitai_key_status(effective_api_key, source="probe-auth")
|
| 2174 |
-
auth_resp =
|
| 2175 |
session,
|
| 2176 |
-
|
| 2177 |
api_key=effective_api_key,
|
| 2178 |
params={"query": probe_query, "limit": 1, "sort": "Newest", "period": "AllTime", "nsfw": "true"},
|
| 2179 |
timeout=(7.0, 20.0),
|
| 2180 |
label='Civitai probe models',
|
| 2181 |
source='probe-auth',
|
| 2182 |
)
|
| 2183 |
-
|
| 2184 |
-
if auth_resp.ok
|
| 2185 |
-
|
|
|
|
| 2186 |
dl_url, model, version, file_info = extract_probe_first_download_url(payload)
|
| 2187 |
if dl_url:
|
| 2188 |
lines.append(f"- auth first file: {model.get('name', '')} / {version.get('name', '')} / {round(float(file_info.get('sizeKB', 0.0) or 0.0) / 1000.0, 2)}MB")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2189 |
try:
|
| 2190 |
-
resolved_url = resolve_civitai_download_url(
|
| 2191 |
resolved_host = urllib.parse.urlparse(resolved_url).netloc
|
| 2192 |
lines.append(f"- auth resolve: ok ({resolved_host})")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2193 |
except Exception as e:
|
| 2194 |
lines.append(f"- auth resolve: fail ({type(e).__name__}: {e})")
|
|
|
|
| 2195 |
else:
|
| 2196 |
lines.append("- auth resolve: skipped (no downloadUrl in first result)")
|
|
|
|
| 2197 |
except Exception as e:
|
| 2198 |
lines.append(f"- auth probe: fail ({type(e).__name__}: {e})")
|
| 2199 |
if not anon_ok:
|
|
@@ -2201,51 +2586,3 @@ def probe_civitai_api(query: str, api_key: str):
|
|
| 2201 |
probe_text = "\n".join(lines)
|
| 2202 |
print(probe_text)
|
| 2203 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
| 2204 |
-
def probe_civitai_green_api(query: str, api_key: str):
|
| 2205 |
-
effective_api_key = get_effective_probe_api_key(api_key)
|
| 2206 |
-
key_source = "input" if str(api_key or "").strip() else ("env" if effective_api_key else "none")
|
| 2207 |
-
probe_input = str(query or "").strip() or "lora"
|
| 2208 |
-
print(f"civitai.green Probe: input={probe_input!r} key_source={key_source}")
|
| 2209 |
-
lines = ["### civitai.green Probe", f"- input: {probe_input}", f"- key source: {key_source}"]
|
| 2210 |
-
session = create_retry_session(total=4, backoff_factor=0.8)
|
| 2211 |
-
try:
|
| 2212 |
-
parts = get_civitai_url_parts(probe_input)
|
| 2213 |
-
if is_civitai_host(parts.netloc):
|
| 2214 |
-
lines.append(f"- url host: {parts.netloc}")
|
| 2215 |
-
normalized = normalize_civitai_input_url(probe_input, api_key=effective_api_key)
|
| 2216 |
-
if normalized != probe_input:
|
| 2217 |
-
lines.append(f"- normalized download url: {normalize_civitai_download_api_url(normalized)}")
|
| 2218 |
-
if "api/download/models/" in normalized:
|
| 2219 |
-
lines.append("- mode: direct-download probe")
|
| 2220 |
-
try:
|
| 2221 |
-
resolved_url = resolve_civitai_download_url(normalized, effective_api_key, max_tries=1)
|
| 2222 |
-
resolved_host = urllib.parse.urlparse(resolved_url).netloc
|
| 2223 |
-
lines.append(f"- resolve: ok ({resolved_host})")
|
| 2224 |
-
except Exception as e:
|
| 2225 |
-
lines.append(f"- resolve: fail ({type(e).__name__}: {e})")
|
| 2226 |
-
else:
|
| 2227 |
-
lines.append("- mode: page-only probe")
|
| 2228 |
-
page_resp = session.get(probe_input, headers=get_civitai_headers(effective_api_key), timeout=(7.0, 20.0))
|
| 2229 |
-
lines.append(f"- page: {'ok' if page_resp.ok else 'fail'} ({page_resp.status_code})")
|
| 2230 |
-
if page_resp.ok:
|
| 2231 |
-
first_path = extract_first_model_path(page_resp.text)
|
| 2232 |
-
if first_path:
|
| 2233 |
-
lines.append(f"- first model: {first_path}")
|
| 2234 |
-
first_download = extract_first_civitai_download_url_from_html(page_resp.text)
|
| 2235 |
-
lines.append(f"- embedded download url: {'found' if first_download else 'not found'}")
|
| 2236 |
-
else:
|
| 2237 |
-
origin = CIVITAI_GREEN_ORIGIN
|
| 2238 |
-
lines.append(f"- origin: {origin}")
|
| 2239 |
-
lines.append("- mode: query probe")
|
| 2240 |
-
page_resp = session.get(f"{origin}/models", params={"query": probe_input}, headers=get_civitai_headers(effective_api_key), timeout=(7.0, 20.0))
|
| 2241 |
-
lines.append(f"- page: {'ok' if page_resp.ok else 'fail'} ({page_resp.status_code})")
|
| 2242 |
-
if page_resp.ok:
|
| 2243 |
-
first_path = extract_first_model_path(page_resp.text)
|
| 2244 |
-
if first_path:
|
| 2245 |
-
lines.append(f"- first model: {first_path}")
|
| 2246 |
-
lines.append("- api: skipped (HTML-only probe)")
|
| 2247 |
-
except Exception as e:
|
| 2248 |
-
lines.append(f"- probe: fail ({type(e).__name__}: {e})")
|
| 2249 |
-
probe_text = "\n".join(lines)
|
| 2250 |
-
print(probe_text)
|
| 2251 |
-
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
|
|
|
| 45 |
LOAD_ALL_BATCH_SIZE = 8
|
| 46 |
NULL_IMAGE_PATH = str(Path(__file__).with_name("null.png"))
|
| 47 |
CIVITAI_DEFAULT_ORIGIN = "https://civitai.com"
|
| 48 |
+
CIVITAI_CANONICAL_WEB_ORIGIN = CIVITAI_DEFAULT_ORIGIN
|
| 49 |
+
CIVITAI_RED_ORIGIN = "https://civitai.red"
|
| 50 |
CIVITAI_GREEN_ORIGIN = "https://civitai.green"
|
| 51 |
+
CIVITAI_GREEN_HOST_ALIASES = frozenset({"civitai.green", "www.civitai.green"})
|
| 52 |
+
CIVITAI_RED_HOST_ALIASES = frozenset({"civitai.red", "www.civitai.red"})
|
| 53 |
+
CIVITAI_HOST_ALIASES = frozenset({"civitai.com", "www.civitai.com", *CIVITAI_GREEN_HOST_ALIASES, *CIVITAI_RED_HOST_ALIASES})
|
| 54 |
+
CIVITAI_API_ORIGIN_CANDIDATES = (CIVITAI_RED_ORIGIN, CIVITAI_DEFAULT_ORIGIN)
|
| 55 |
PREVIEW_VIDEO_EXTS = {".mp4", ".webm", ".mov", ".m4v", ".avi", ".mkv"}
|
| 56 |
_FFMPEG_PATH = None
|
| 57 |
_FFMPEG_MISSING_LOGGED = False
|
|
|
|
| 64 |
RUN_CANCEL_LOCK = threading.Lock()
|
| 65 |
CREATOR_SUGGEST_CACHE = {}
|
| 66 |
CREATOR_SUGGEST_LOCK = threading.Lock()
|
| 67 |
+
CIVITAI_ACTIVE_API_ORIGIN = ""
|
| 68 |
+
CIVITAI_ACTIVE_API_BASE = ""
|
| 69 |
+
_CIVITAI_API_LOCK = threading.Lock()
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def canonicalize_civitai_netloc(netloc: str):
|
| 73 |
+
host = str(netloc or "").strip().lower()
|
| 74 |
+
if host in CIVITAI_GREEN_HOST_ALIASES or host == "www.civitai.com":
|
| 75 |
+
return "civitai.com"
|
| 76 |
+
if host in CIVITAI_RED_HOST_ALIASES:
|
| 77 |
+
return "civitai.red"
|
| 78 |
+
return host
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def canonicalize_civitai_host(netloc: str):
|
| 82 |
+
return canonicalize_civitai_netloc(netloc)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def normalize_civitai_origin(value: str):
|
| 86 |
+
raw = str(value or "").strip()
|
| 87 |
+
if not raw:
|
| 88 |
+
return CIVITAI_DEFAULT_ORIGIN
|
| 89 |
+
parts = urllib.parse.urlsplit(raw if "://" in raw else f"https://{raw}")
|
| 90 |
+
host = canonicalize_civitai_netloc(parts.netloc or parts.path)
|
| 91 |
+
if host in {"civitai.com", "civitai.red"}:
|
| 92 |
+
return f"https://{host}"
|
| 93 |
+
return CIVITAI_DEFAULT_ORIGIN
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def get_civitai_canonical_web_origin():
|
| 97 |
+
return CIVITAI_CANONICAL_WEB_ORIGIN
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def get_civitai_display_origin():
|
| 101 |
+
return get_civitai_canonical_web_origin()
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def build_civitai_api_base(origin: str):
|
| 105 |
+
raw = str(origin or "").strip().rstrip("/")
|
| 106 |
+
return f"{raw}/api/v1" if raw else ""
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def set_civitai_active_api_origin(origin: str):
|
| 110 |
+
global CIVITAI_ACTIVE_API_ORIGIN, CIVITAI_ACTIVE_API_BASE
|
| 111 |
+
normalized = normalize_civitai_origin(origin)
|
| 112 |
+
if normalized not in CIVITAI_API_ORIGIN_CANDIDATES:
|
| 113 |
+
normalized = CIVITAI_DEFAULT_ORIGIN
|
| 114 |
+
base = build_civitai_api_base(normalized)
|
| 115 |
+
with _CIVITAI_API_LOCK:
|
| 116 |
+
CIVITAI_ACTIVE_API_ORIGIN = normalized
|
| 117 |
+
CIVITAI_ACTIVE_API_BASE = base
|
| 118 |
+
return base
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def set_civitai_api_origin(origin: str):
|
| 122 |
+
set_civitai_active_api_origin(origin)
|
| 123 |
+
return CIVITAI_ACTIVE_API_ORIGIN
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def get_civitai_api_origin():
|
| 127 |
+
with _CIVITAI_API_LOCK:
|
| 128 |
+
return CIVITAI_ACTIVE_API_ORIGIN or ""
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def probe_civitai_api_origin(session, origin: str, timeout: tuple[float, float] = (3.0, 8.0)):
|
| 132 |
+
response = None
|
| 133 |
+
try:
|
| 134 |
+
base_url = build_civitai_api_base(origin)
|
| 135 |
+
if not base_url:
|
| 136 |
+
return False
|
| 137 |
+
response = session.get(
|
| 138 |
+
f"{base_url}/tags",
|
| 139 |
+
params={"limit": 1},
|
| 140 |
+
headers=get_civitai_headers(""),
|
| 141 |
+
timeout=timeout,
|
| 142 |
+
)
|
| 143 |
+
if not response.ok:
|
| 144 |
+
return False
|
| 145 |
+
content_type = str(response.headers.get("content-type") or "").lower()
|
| 146 |
+
if "json" not in content_type:
|
| 147 |
+
return False
|
| 148 |
+
data = response.json()
|
| 149 |
+
return isinstance(data, dict)
|
| 150 |
+
except Exception:
|
| 151 |
+
return False
|
| 152 |
+
finally:
|
| 153 |
+
try:
|
| 154 |
+
if response is not None:
|
| 155 |
+
response.close()
|
| 156 |
+
except Exception:
|
| 157 |
+
pass
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def get_civitai_active_api_origin(force_refresh: bool = False, session=None):
|
| 161 |
+
cached_origin = get_civitai_api_origin()
|
| 162 |
+
if cached_origin and not force_refresh:
|
| 163 |
+
return cached_origin
|
| 164 |
+
if session is None:
|
| 165 |
+
session = create_retry_session(total=4, backoff_factor=0.8)
|
| 166 |
+
for origin in CIVITAI_API_ORIGIN_CANDIDATES:
|
| 167 |
+
if probe_civitai_api_origin(session, origin):
|
| 168 |
+
set_civitai_active_api_origin(origin)
|
| 169 |
+
return get_civitai_api_origin()
|
| 170 |
+
set_civitai_active_api_origin(CIVITAI_DEFAULT_ORIGIN)
|
| 171 |
+
return get_civitai_api_origin()
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def resolve_civitai_api_origin(session=None):
|
| 175 |
+
return get_civitai_active_api_origin(session=session)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def get_civitai_active_api_base(force_refresh: bool = False, session=None):
|
| 179 |
+
with _CIVITAI_API_LOCK:
|
| 180 |
+
cached_base = CIVITAI_ACTIVE_API_BASE or ""
|
| 181 |
+
if cached_base and not force_refresh:
|
| 182 |
+
return cached_base
|
| 183 |
+
get_civitai_active_api_origin(force_refresh=force_refresh, session=session)
|
| 184 |
+
with _CIVITAI_API_LOCK:
|
| 185 |
+
return CIVITAI_ACTIVE_API_BASE or build_civitai_api_base(CIVITAI_DEFAULT_ORIGIN)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def get_civitai_api_candidate_origins(preferred: str = ""):
|
| 189 |
+
origins = list(CIVITAI_API_ORIGIN_CANDIDATES)
|
| 190 |
+
preferred_origin = normalize_civitai_origin(preferred) if preferred else ""
|
| 191 |
+
if preferred_origin in origins:
|
| 192 |
+
return [preferred_origin] + [origin for origin in origins if origin != preferred_origin]
|
| 193 |
+
cached_origin = get_civitai_api_origin()
|
| 194 |
+
if cached_origin in origins:
|
| 195 |
+
return [cached_origin] + [origin for origin in origins if origin != cached_origin]
|
| 196 |
+
return origins
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def iter_civitai_api_bases(preferred: str = "", session=None):
|
| 200 |
+
preferred_origin = normalize_civitai_origin(preferred) if preferred else get_civitai_active_api_origin(session=session)
|
| 201 |
+
return [build_civitai_api_base(origin) for origin in get_civitai_api_candidate_origins(preferred_origin)]
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def build_civitai_api_url(path: str, origin: str = ""):
|
| 205 |
+
clean_path = "/" + str(path or "").strip().lstrip("/")
|
| 206 |
+
if not clean_path.startswith("/api/"):
|
| 207 |
+
clean_path = f"/api/v1{clean_path}"
|
| 208 |
+
base_origin = normalize_civitai_origin(origin) if origin else normalize_civitai_origin(get_civitai_api_origin() or CIVITAI_DEFAULT_ORIGIN)
|
| 209 |
+
return f"{base_origin.rstrip('/')}" + clean_path
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def build_civitai_model_url(model_id, model_version_id=None):
|
| 213 |
+
if model_id is None:
|
| 214 |
+
return ""
|
| 215 |
+
url = f"{get_civitai_canonical_web_origin().rstrip('/')}/models/{model_id}"
|
| 216 |
+
if model_version_id is not None:
|
| 217 |
+
url += f"?modelVersionId={model_version_id}"
|
| 218 |
+
return url
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
def should_fallback_civitai_api_response(response):
|
| 222 |
+
if response is None:
|
| 223 |
+
return True
|
| 224 |
+
try:
|
| 225 |
+
status_code = int(getattr(response, "status_code", 0) or 0)
|
| 226 |
+
except Exception:
|
| 227 |
+
return True
|
| 228 |
+
return status_code in {404, 405, 408, 429} or status_code >= 500
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def request_civitai_api(session, path: str, api_key: str = "", params=None, timeout: tuple[float, float] = (7.0, 30.0),
|
| 232 |
+
label: str = "Civitai API", source: str = "civitai-api", preferred_origin: str = "",
|
| 233 |
+
non_json_fallback_origin: str = ""):
|
| 234 |
+
preferred = normalize_civitai_origin(preferred_origin) if preferred_origin else get_civitai_active_api_origin(session=session)
|
| 235 |
+
non_json_fallback = normalize_civitai_origin(non_json_fallback_origin) if non_json_fallback_origin else ""
|
| 236 |
+
last_response = None
|
| 237 |
+
last_exception = None
|
| 238 |
+
for origin in get_civitai_api_candidate_origins(preferred):
|
| 239 |
+
url = build_civitai_api_url(path, origin=origin)
|
| 240 |
+
try:
|
| 241 |
+
response = civitai_get(session, url, api_key=api_key, params=params, timeout=timeout, label=label, source=source)
|
| 242 |
+
last_response = response
|
| 243 |
+
if response is not None and response.ok:
|
| 244 |
+
try:
|
| 245 |
+
get_civitai_response_json(response)
|
| 246 |
+
except Exception as e:
|
| 247 |
+
last_exception = e
|
| 248 |
+
log_line("retry", f"{label}: non-json response from {origin}")
|
| 249 |
+
try:
|
| 250 |
+
response.close()
|
| 251 |
+
except Exception:
|
| 252 |
+
pass
|
| 253 |
+
if non_json_fallback and origin != non_json_fallback:
|
| 254 |
+
fallback_url = build_civitai_api_url(path, origin=non_json_fallback)
|
| 255 |
+
try:
|
| 256 |
+
fallback_response = civitai_get(session, fallback_url, api_key=api_key, params=params, timeout=timeout, label=label, source=source)
|
| 257 |
+
last_response = fallback_response
|
| 258 |
+
if fallback_response is not None and fallback_response.ok:
|
| 259 |
+
get_civitai_response_json(fallback_response)
|
| 260 |
+
set_civitai_active_api_origin(non_json_fallback)
|
| 261 |
+
return fallback_response
|
| 262 |
+
if fallback_response is not None and not should_fallback_civitai_api_response(fallback_response):
|
| 263 |
+
return fallback_response
|
| 264 |
+
if fallback_response is not None:
|
| 265 |
+
fallback_response.close()
|
| 266 |
+
except Exception as fallback_error:
|
| 267 |
+
last_exception = fallback_error
|
| 268 |
+
continue
|
| 269 |
+
set_civitai_active_api_origin(origin)
|
| 270 |
+
return response
|
| 271 |
+
if not should_fallback_civitai_api_response(response):
|
| 272 |
+
return response
|
| 273 |
+
if response is not None:
|
| 274 |
+
response.close()
|
| 275 |
+
except Exception as e:
|
| 276 |
+
last_exception = e
|
| 277 |
+
continue
|
| 278 |
+
if last_exception is not None:
|
| 279 |
+
raise last_exception
|
| 280 |
+
if last_response is not None:
|
| 281 |
+
return last_response
|
| 282 |
+
return None
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
_CIVITAI_JSON_MISSING = object()
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def get_civitai_response_json(response, default=_CIVITAI_JSON_MISSING):
|
| 289 |
+
if response is None:
|
| 290 |
+
if default is not _CIVITAI_JSON_MISSING:
|
| 291 |
+
return default
|
| 292 |
+
raise ValueError("Civitai response is missing")
|
| 293 |
+
cached = getattr(response, "_civitai_json", _CIVITAI_JSON_MISSING)
|
| 294 |
+
if cached is not _CIVITAI_JSON_MISSING:
|
| 295 |
+
return cached
|
| 296 |
+
try:
|
| 297 |
+
payload = response.json()
|
| 298 |
+
except Exception:
|
| 299 |
+
if default is not _CIVITAI_JSON_MISSING:
|
| 300 |
+
return default
|
| 301 |
+
raise
|
| 302 |
+
setattr(response, "_civitai_json", payload)
|
| 303 |
+
return payload
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
def request_civitai_api_url(session, url: str, api_key: str = "", params=None, timeout: tuple[float, float] = (7.0, 30.0),
|
| 307 |
+
label: str = "Civitai API", source: str = "civitai-api"):
|
| 308 |
+
parts = get_civitai_url_parts(url)
|
| 309 |
+
path = str(parts.path or "")
|
| 310 |
+
if is_civitai_host(parts.netloc) and path.startswith("/api/"):
|
| 311 |
+
api_path = path + (f"?{parts.query}" if parts.query else "")
|
| 312 |
+
return request_civitai_api(session, api_path, api_key=api_key, params=params, timeout=timeout, label=label, source=source)
|
| 313 |
+
response = civitai_get(session, url, api_key=api_key, params=params, timeout=timeout, label=label, source=source)
|
| 314 |
+
get_civitai_response_json(response)
|
| 315 |
+
return response
|
| 316 |
|
| 317 |
|
| 318 |
CIVITAI_TYPE = ["Checkpoint", "TextualInversion", "Hypernetwork", "AestheticGradient", "LORA", "LoCon", "DoRA",
|
|
|
|
| 649 |
final_md = md.strip()
|
| 650 |
if final_md:
|
| 651 |
final_md += "\n"
|
| 652 |
+
return gr.update(value=urls, choices=urls), gr.update(value=final_md), remain_update, failed_update, key_status_update, session_state_output(session_state)
|
| 653 |
|
| 654 |
|
| 655 |
def set_stage_progress(progress, current: int, total: int, desc: str):
|
|
|
|
| 800 |
return urllib.parse.urlsplit("")
|
| 801 |
|
| 802 |
def is_civitai_host(netloc: str):
|
| 803 |
+
return canonicalize_civitai_host(netloc) in {"civitai.com", "civitai.red"}
|
| 804 |
|
| 805 |
def is_civitai_download_api_path(path: str):
|
| 806 |
return re.match(r'^/api/download/models/\d+$', str(path or "").strip()) is not None
|
|
|
|
| 823 |
def to_civitai_default_download_url(version_id: str, query: str = ""):
|
| 824 |
if not str(version_id or "").isdigit():
|
| 825 |
return ""
|
| 826 |
+
base = f"{get_civitai_display_origin()}/api/download/models/{version_id}"
|
| 827 |
return f"{base}?{query}" if query else base
|
| 828 |
|
| 829 |
def normalize_civitai_download_api_url(url: str):
|
|
|
|
| 837 |
return ""
|
| 838 |
page = html_lib.unescape(str(html))
|
| 839 |
patterns = [
|
| 840 |
+
r'https?://(?:www\.)?(?:civitai\.com|civitai\.red|civitai\.green)/api/download/models/\d+[^\s\'"<>)\]]*',
|
| 841 |
r"[\"'](/api/download/models/\d+[^\"']*)[\"']",
|
| 842 |
]
|
| 843 |
for pattern in patterns:
|
|
|
|
| 866 |
version_id = extract_civitai_model_version_id(raw)
|
| 867 |
if version_id:
|
| 868 |
return to_civitai_default_download_url(version_id)
|
| 869 |
+
headers = get_civitai_headers(api_key if canonicalize_civitai_host(parts.netloc) in {"civitai.com", "civitai.red"} else "")
|
| 870 |
headers["Referer"] = f"{parts.scheme or 'https'}://{parts.netloc}/"
|
| 871 |
session = create_retry_session(total=4, backoff_factor=0.8)
|
| 872 |
try:
|
|
|
|
| 897 |
return k in qs.keys() and qs[k][0] != json.get(k, None) and json.get(k, None) is not None
|
| 898 |
|
| 899 |
if "https://civitai.com/api/download/models/" not in dl_url: return None
|
| 900 |
+
base_path = '/model-versions'
|
| 901 |
params = {}
|
| 902 |
session = create_retry_session(total=6, backoff_factor=1.0)
|
| 903 |
m = re.match(r'https://civitai.com/api/download/models/(\d+)\??(.+)?', dl_url)
|
| 904 |
if m is None: return None
|
| 905 |
+
url = f"{base_path}/{m.group(1)}"
|
| 906 |
qs = urllib.parse.parse_qs(m.group(2))
|
| 907 |
if "type" not in qs.keys(): qs["type"] = ["Model"]
|
| 908 |
try:
|
| 909 |
+
r = request_civitai_api(session, url, api_key=api_key, params=params, timeout=(5.0, 15), label='Civitai sha256')
|
| 910 |
if not r.ok: return None
|
| 911 |
+
json = dict(get_civitai_response_json(r, default={}) or {})
|
| 912 |
if "files" not in json.keys() or not isinstance(json["files"], list): return None
|
| 913 |
hash = None
|
| 914 |
for d in json["files"]:
|
|
|
|
| 1335 |
|
| 1336 |
|
| 1337 |
def fetch_civitai_basemodels(api_key: str = "", pages_per_sort: int = CIVITAI_BASEMODEL_REFRESH_PAGES_PER_SORT):
|
| 1338 |
+
base_path = '/models'
|
| 1339 |
observed = set()
|
| 1340 |
session = create_retry_session(total=6, backoff_factor=1.0)
|
| 1341 |
seeds = [("Newest", "AllTime"), ("Most Downloaded", "AllTime")]
|
|
|
|
| 1345 |
params = {'sort': sort, 'period': period, 'limit': 100, 'page': page_index, 'nsfw': 'true'}
|
| 1346 |
try:
|
| 1347 |
if next_url is None:
|
| 1348 |
+
r = request_civitai_api(session, base_path, api_key=api_key, params=params, timeout=(7.0, 30), label=f'Civitai base model refresh sort={sort} page={page_index}', source='base-models')
|
| 1349 |
else:
|
| 1350 |
+
r = request_civitai_api_url(session, next_url, api_key=api_key, timeout=(7.0, 30), label=f'Civitai base model refresh sort={sort} page={page_index}', source='base-models')
|
| 1351 |
if not r.ok:
|
| 1352 |
print(f"Failed to refresh Civitai base models. sort={sort} page={page_index} status={r.status_code}")
|
| 1353 |
break
|
| 1354 |
+
data = get_civitai_response_json(r, default={}) or {}
|
| 1355 |
items = data.get('items', [])
|
| 1356 |
if not isinstance(items, list): break
|
| 1357 |
for item in items:
|
|
|
|
| 1634 |
return gr.update(value=f"### {title}\n- status: probing...", visible=True)
|
| 1635 |
def begin_probe_civitai():
|
| 1636 |
return begin_probe_feedback("Civitai Probe")
|
|
|
|
|
|
|
| 1637 |
def begin_probe_civitai_keys():
|
| 1638 |
return begin_probe_feedback("Civitai Key Probe")
|
| 1639 |
def begin_probe_civitai_url():
|
|
|
|
| 1761 |
def search_on_civitai(query: str, types: list[str], allow_model: list[str] = [], limit: int = 100,
|
| 1762 |
sort: str = "Highest Rated", period: str = "AllTime", tag: str = "", user: str = "", page: int = 1,
|
| 1763 |
filetype: list[str] = [], api_key: str = "", base_origin: str = CIVITAI_DEFAULT_ORIGIN, progress=gr.Progress(track_tqdm=False)):
|
| 1764 |
+
base_path = '/models'
|
| 1765 |
api_sort = sort if sort in {"Highest Rated", "Most Downloaded", "Newest"} else CIVITAI_SORT[0]
|
| 1766 |
params = {'sort': api_sort, 'period': period, 'limit': int(limit), 'nsfw': 'true'}
|
| 1767 |
clean_types = [str(t).strip() for t in (types or []) if str(t).strip()]
|
|
|
|
| 1782 |
if page == 0:
|
| 1783 |
progress(0, desc="Searching page 1...")
|
| 1784 |
print("Searching page 1...")
|
| 1785 |
+
r = request_civitai_api(session, base_path, api_key=api_key, params=params | {'page': 1}, timeout=(7.0, 30), label='Civitai search page=1', source='search')
|
| 1786 |
rs.append(r)
|
| 1787 |
if r.ok:
|
| 1788 |
+
json = get_civitai_response_json(r, default={}) or {}
|
| 1789 |
next_url = json['metadata']['nextPage'] if 'metadata' in json and 'nextPage' in json['metadata'] else None
|
| 1790 |
i = 2
|
| 1791 |
while next_url is not None:
|
| 1792 |
progress(0, desc=f"Searching page {i}...")
|
| 1793 |
print(f"Searching page {i}...")
|
| 1794 |
+
r = request_civitai_api_url(session, next_url, api_key=api_key, timeout=(7.0, 30), label=f'Civitai search page={i}', source='search')
|
| 1795 |
rs.append(r)
|
| 1796 |
if r.ok:
|
| 1797 |
+
json = get_civitai_response_json(r, default={}) or {}
|
| 1798 |
next_url = json['metadata']['nextPage'] if 'metadata' in json and 'nextPage' in json['metadata'] else None
|
| 1799 |
else:
|
| 1800 |
next_url = None
|
|
|
|
| 1804 |
else:
|
| 1805 |
progress(0, desc="Searching page 1...")
|
| 1806 |
print("Searching page 1...")
|
| 1807 |
+
r = request_civitai_api(session, base_path, api_key=api_key, params=params, timeout=(7.0, 30), label='Civitai search page=1', source='search')
|
| 1808 |
rs.append(r)
|
| 1809 |
except requests.exceptions.ConnectTimeout:
|
| 1810 |
print("Request timed out.")
|
| 1811 |
except Exception as e:
|
| 1812 |
print(e)
|
| 1813 |
items = []
|
| 1814 |
+
origin = get_civitai_display_origin().rstrip('/')
|
| 1815 |
for r in rs:
|
| 1816 |
if not r.ok:
|
| 1817 |
continue
|
| 1818 |
+
json = get_civitai_response_json(r, default={}) or {}
|
| 1819 |
if 'items' not in json:
|
| 1820 |
continue
|
| 1821 |
for j in json['items']:
|
|
|
|
| 1838 |
base_item['img_url'] = images[0].get('url', '') or NULL_IMAGE_PATH
|
| 1839 |
else:
|
| 1840 |
base_item['img_url'] = NULL_IMAGE_PATH
|
| 1841 |
+
model_url = build_civitai_model_url(j.get('id', ''), model.get('id'))
|
|
|
|
|
|
|
| 1842 |
desc = str(base_item.get('description', '') or '')
|
| 1843 |
base_item['md'] = (
|
| 1844 |
f"Model URL: [{model_url}]({model_url})<br>"
|
|
|
|
| 1853 |
if files:
|
| 1854 |
for f in files:
|
| 1855 |
item = base_item.copy()
|
| 1856 |
+
item['dl_url'] = normalize_civitai_download_api_url(f.get('downloadUrl', ''))
|
| 1857 |
item['size_kb'] = f.get('sizeKB', 0.0)
|
| 1858 |
item['file_type'] = f.get('type', '')
|
| 1859 |
if len(filetype) != 0 and f.get('type', '') not in set(filetype):
|
|
|
|
| 1861 |
items.append(item)
|
| 1862 |
else:
|
| 1863 |
item = base_item.copy()
|
| 1864 |
+
item['dl_url'] = normalize_civitai_download_api_url(model.get('downloadUrl', ''))
|
| 1865 |
items.append(item)
|
| 1866 |
if sort in CIVITAI_SORT_EXT:
|
| 1867 |
if sort == "Size":
|
|
|
|
| 1936 |
default = ("", "", "") if is_html else ""
|
| 1937 |
if "https://civitai.com/api/download/models/" not in dl_url:
|
| 1938 |
return default
|
| 1939 |
+
base_path = '/model-versions'
|
| 1940 |
params = {}
|
| 1941 |
session = create_retry_session(total=6, backoff_factor=1.0)
|
| 1942 |
model_id = re.sub(r'https://civitai.com/api/download/models/(\d+)(?:.+)?', r'\1', dl_url)
|
| 1943 |
+
url = f"{base_path}/{model_id}"
|
| 1944 |
try:
|
| 1945 |
reset_civitai_key_status(api_key, source='info')
|
| 1946 |
+
r = request_civitai_api(session, url, api_key=api_key, params=params, timeout=(5.0, 60), label='Civitai model-version', source='info')
|
| 1947 |
if not r.ok:
|
| 1948 |
return default
|
| 1949 |
+
json = dict(get_civitai_response_json(r, default={}) or {}).copy()
|
| 1950 |
html = ""
|
| 1951 |
image = ""
|
| 1952 |
if "modelId" in json.keys():
|
|
|
|
| 1954 |
if is_civitai_host(original_parts.netloc) and re.match(r'^/models/\d+(?:/[^/?#]+)?/?$', original_parts.path or ""):
|
| 1955 |
url = original_dl_url
|
| 1956 |
else:
|
| 1957 |
+
url = build_civitai_model_url(json['modelId'])
|
| 1958 |
r = civitai_get(session, url, api_key=api_key, params=params, timeout=(5.0, 60), label='Civitai model page', source='info')
|
| 1959 |
if not r.ok:
|
| 1960 |
return json, html, image
|
|
|
|
| 1992 |
return default
|
| 1993 |
|
| 1994 |
|
| 1995 |
+
def _load_civitai_choice_list(api_path: str, label: str, source: str, value_key: str, count_key: str,
|
| 1996 |
query: str="", api_key: str="", limit: int=200,
|
| 1997 |
timeout: tuple[float, float]=(7.0, 15.0), retry_total: int=6,
|
| 1998 |
backoff_factor: float=1.0, default: list[str] | None=None,
|
| 1999 |
cache: dict | None=None, cache_lock: object | None=None,
|
| 2000 |
+
cache_ttl_sec: int=0, startup_name: str="", preferred_origin: str="",
|
| 2001 |
+
non_json_fallback_origin: str=""):
|
| 2002 |
clean_query = str(query or "").strip()
|
| 2003 |
cache_key = clean_query.casefold()
|
| 2004 |
now = time.time()
|
|
|
|
| 2013 |
params['query'] = clean_query
|
| 2014 |
session = create_retry_session(total=retry_total, backoff_factor=backoff_factor)
|
| 2015 |
try:
|
| 2016 |
+
r = request_civitai_api(
|
| 2017 |
+
session,
|
| 2018 |
+
api_path,
|
| 2019 |
+
api_key=api_key,
|
| 2020 |
+
params=params,
|
| 2021 |
+
timeout=timeout,
|
| 2022 |
+
label=label,
|
| 2023 |
+
source=source,
|
| 2024 |
+
preferred_origin=preferred_origin,
|
| 2025 |
+
non_json_fallback_origin=non_json_fallback_origin,
|
| 2026 |
+
)
|
| 2027 |
if not r.ok:
|
| 2028 |
if not clean_query and startup_name:
|
| 2029 |
print(f"Failed to load {startup_name} from API at startup. HTTP {r.status_code}.")
|
| 2030 |
return default
|
| 2031 |
+
j = dict(get_civitai_response_json(r, default={}) or {}).copy()
|
| 2032 |
items = j.get('items', []) if isinstance(j.get('items', []), list) else []
|
| 2033 |
observed = []
|
| 2034 |
for item in items:
|
|
|
|
| 2051 |
|
| 2052 |
def get_civitai_creator(query: str="", api_key: str="", limit: int=CREATOR_SUGGEST_LIMIT):
|
| 2053 |
return _load_civitai_choice_list(
|
| 2054 |
+
api_path='/creators',
|
| 2055 |
label='Civitai creators',
|
| 2056 |
source='creators',
|
| 2057 |
value_key='username',
|
|
|
|
| 2067 |
cache_lock=CREATOR_SUGGEST_LOCK,
|
| 2068 |
cache_ttl_sec=CREATOR_CACHE_TTL_SEC,
|
| 2069 |
startup_name='Civitai creators',
|
| 2070 |
+
non_json_fallback_origin=CIVITAI_DEFAULT_ORIGIN,
|
| 2071 |
)
|
| 2072 |
|
| 2073 |
|
|
|
|
| 2089 |
|
| 2090 |
def get_civitai_tag():
|
| 2091 |
return _load_civitai_choice_list(
|
| 2092 |
+
api_path='/tags',
|
| 2093 |
label='Civitai tags',
|
| 2094 |
source='tags',
|
| 2095 |
value_key='name',
|
|
|
|
| 2223 |
return dl_url, model, version, {}
|
| 2224 |
return "", {}, {}, {}
|
| 2225 |
|
| 2226 |
+
def probe_json_api_step(session, path: str, *, api_key: str = "", params=None, timeout: tuple[float, float] = (7.0, 20.0),
|
| 2227 |
+
label: str = "Civitai probe", source: str = "probe", preferred_origin: str = "",
|
| 2228 |
+
non_json_fallback_origin: str = ""):
|
| 2229 |
+
response = None
|
| 2230 |
+
try:
|
| 2231 |
+
response = request_civitai_api(
|
| 2232 |
+
session,
|
| 2233 |
+
path,
|
| 2234 |
+
api_key=api_key,
|
| 2235 |
+
params=params,
|
| 2236 |
+
timeout=timeout,
|
| 2237 |
+
label=label,
|
| 2238 |
+
source=source,
|
| 2239 |
+
preferred_origin=preferred_origin,
|
| 2240 |
+
non_json_fallback_origin=non_json_fallback_origin,
|
| 2241 |
+
)
|
| 2242 |
+
ok = bool(response is not None and getattr(response, "ok", False))
|
| 2243 |
+
status = str(getattr(response, "status_code", "-"))
|
| 2244 |
+
response_url = str(getattr(response, "url", "") or "")
|
| 2245 |
+
response_host = urllib.parse.urlparse(response_url).netloc
|
| 2246 |
+
json_ok = False
|
| 2247 |
+
if ok:
|
| 2248 |
+
payload = get_civitai_response_json(response, default=_CIVITAI_JSON_MISSING)
|
| 2249 |
+
json_ok = payload is not _CIVITAI_JSON_MISSING
|
| 2250 |
+
return {"ok": ok, "status": status, "host": response_host, "json_ok": json_ok, "error": ""}
|
| 2251 |
+
except Exception as e:
|
| 2252 |
+
return {"ok": False, "status": "-", "host": "", "json_ok": False, "error": f"{type(e).__name__}: {e}"}
|
| 2253 |
+
finally:
|
| 2254 |
+
try:
|
| 2255 |
+
if response is not None:
|
| 2256 |
+
response.close()
|
| 2257 |
+
except Exception:
|
| 2258 |
+
pass
|
| 2259 |
+
|
| 2260 |
+
|
| 2261 |
+
def probe_download_url_step(session, download_url: str, *, api_key: str = "", timeout: tuple[float, float] = (7.0, 20.0)):
|
| 2262 |
+
response = None
|
| 2263 |
+
try:
|
| 2264 |
+
response = civitai_get(session, download_url, api_key=api_key, timeout=timeout, label='Civitai probe download', source='probe-download')
|
| 2265 |
+
status = str(getattr(response, "status_code", "-"))
|
| 2266 |
+
status_code = int(status) if str(status).isdigit() else 0
|
| 2267 |
+
response_url = str(getattr(response, "url", "") or download_url or "")
|
| 2268 |
+
response_host = urllib.parse.urlparse(response_url).netloc
|
| 2269 |
+
ok = bool(response is not None and (getattr(response, "ok", False) or status_code in {301, 302, 303, 307, 308}))
|
| 2270 |
+
return {"ok": ok, "status": status, "host": response_host, "error": ""}
|
| 2271 |
+
except Exception as e:
|
| 2272 |
+
return {"ok": False, "status": "-", "host": "", "error": f"{type(e).__name__}: {e}"}
|
| 2273 |
+
finally:
|
| 2274 |
+
try:
|
| 2275 |
+
if response is not None:
|
| 2276 |
+
response.close()
|
| 2277 |
+
except Exception:
|
| 2278 |
+
pass
|
| 2279 |
+
|
| 2280 |
|
| 2281 |
def probe_civitai_keys(api_key: str):
|
| 2282 |
effective_api_key = get_effective_probe_api_key(api_key)
|
|
|
|
| 2291 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
| 2292 |
session = create_retry_session(total=4, backoff_factor=0.8)
|
| 2293 |
try:
|
| 2294 |
+
api_origin = resolve_civitai_api_origin(session)
|
| 2295 |
+
lines.append(f"- api origin: {api_origin}")
|
| 2296 |
reset_civitai_key_status(effective_api_key, source="probe-keys")
|
| 2297 |
+
resp = request_civitai_api(
|
| 2298 |
session,
|
| 2299 |
+
"/models",
|
| 2300 |
api_key=effective_api_key,
|
| 2301 |
params={"limit": 1, "sort": "Newest", "period": "AllTime", "nsfw": "true"},
|
| 2302 |
timeout=(7.0, 20.0),
|
|
|
|
| 2455 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
| 2456 |
|
| 2457 |
def probe_civitai_api(query: str, api_key: str):
|
| 2458 |
+
page_origin = get_civitai_display_origin()
|
| 2459 |
effective_api_key = get_effective_probe_api_key(api_key)
|
| 2460 |
key_source = "input" if str(api_key or "").strip() else ("env" if effective_api_key else "none")
|
| 2461 |
reset_civitai_key_status(effective_api_key, source="probe")
|
| 2462 |
probe_query = str(query or "lora").strip() or "lora"
|
| 2463 |
print(f"Civitai Probe: query={probe_query!r} key_source={key_source}")
|
| 2464 |
+
lines = ["### Civitai Probe", f"- page origin: {page_origin}", f"- query: {probe_query}", f"- key source: {key_source}"]
|
| 2465 |
session = create_retry_session(total=4, backoff_factor=0.8)
|
| 2466 |
+
api_origin = resolve_civitai_api_origin(session)
|
| 2467 |
+
lines.insert(1, f"- api origin: {api_origin}")
|
| 2468 |
anon_ok = False
|
| 2469 |
try:
|
| 2470 |
+
page_resp = session.get(f"{page_origin}/models", params={"query": probe_query}, headers=get_civitai_headers(""), timeout=(7.0, 20.0))
|
| 2471 |
lines.append(f"- anonymous page: {'ok' if page_resp.ok else 'fail'} ({page_resp.status_code})")
|
| 2472 |
if page_resp.ok:
|
| 2473 |
first_path = extract_first_model_path(page_resp.text)
|
| 2474 |
if first_path:
|
| 2475 |
lines.append(f"- anonymous first model: {first_path}")
|
| 2476 |
+
anon_resp = request_civitai_api(
|
| 2477 |
+
session,
|
| 2478 |
+
"/models",
|
| 2479 |
params={"query": probe_query, "limit": 1, "sort": "Newest", "period": "AllTime", "nsfw": "true"},
|
|
|
|
| 2480 |
timeout=(7.0, 20.0),
|
| 2481 |
+
label='Civitai anonymous probe models',
|
| 2482 |
+
source='probe-anon',
|
| 2483 |
)
|
| 2484 |
+
anon_status = getattr(anon_resp, "status_code", "-")
|
| 2485 |
+
lines.append(f"- anonymous api: {'ok' if anon_resp and anon_resp.ok else 'fail'} ({anon_status})")
|
| 2486 |
+
anon_ok = bool(anon_resp is not None and anon_resp.ok)
|
| 2487 |
except Exception as e:
|
| 2488 |
lines.append(f"- anonymous probe: fail ({type(e).__name__}: {e})")
|
| 2489 |
+
|
| 2490 |
+
tags_probe = probe_json_api_step(
|
| 2491 |
+
session,
|
| 2492 |
+
"/tags",
|
| 2493 |
+
params={"limit": 1},
|
| 2494 |
+
timeout=(7.0, 20.0),
|
| 2495 |
+
label='Civitai probe tags',
|
| 2496 |
+
source='probe-tags',
|
| 2497 |
+
)
|
| 2498 |
+
if tags_probe["error"]:
|
| 2499 |
+
lines.append(f"- tags api: fail ({tags_probe['error']})")
|
| 2500 |
+
else:
|
| 2501 |
+
tags_state = 'ok' if tags_probe["ok"] and tags_probe["json_ok"] else 'fail'
|
| 2502 |
+
lines.append(f"- tags api: {tags_state} ({tags_probe['status']}, {tags_probe['host'] or '-'})")
|
| 2503 |
+
|
| 2504 |
+
creators_probe = probe_json_api_step(
|
| 2505 |
+
session,
|
| 2506 |
+
"/creators",
|
| 2507 |
+
api_key=effective_api_key,
|
| 2508 |
+
params={"limit": 1, "query": probe_query},
|
| 2509 |
+
timeout=(7.0, 20.0),
|
| 2510 |
+
label='Civitai probe creators',
|
| 2511 |
+
source='probe-creators',
|
| 2512 |
+
preferred_origin=CIVITAI_RED_ORIGIN,
|
| 2513 |
+
non_json_fallback_origin=CIVITAI_DEFAULT_ORIGIN,
|
| 2514 |
+
)
|
| 2515 |
+
if creators_probe["error"]:
|
| 2516 |
+
lines.append(f"- creators api: fail ({creators_probe['error']})")
|
| 2517 |
+
else:
|
| 2518 |
+
creators_state = 'ok' if creators_probe["ok"] and creators_probe["json_ok"] else 'fail'
|
| 2519 |
+
creator_host = canonicalize_civitai_host(creators_probe["host"]) if creators_probe["host"] else ''
|
| 2520 |
+
lines.append(f"- creators api: {creators_state} ({creators_probe['status']}, {creators_probe['host'] or '-'})")
|
| 2521 |
+
if creator_host == 'civitai.com':
|
| 2522 |
+
lines.append("- creators fallback: ok (civitai.com)")
|
| 2523 |
+
elif creator_host == 'civitai.red':
|
| 2524 |
+
lines.append("- creators fallback: not-needed (civitai.red json)")
|
| 2525 |
+
|
| 2526 |
parsed_keys = parse_civitai_api_keys(effective_api_key)
|
| 2527 |
if not parsed_keys:
|
| 2528 |
lines.append("- auth resolve: skipped (no Civitai key)")
|
| 2529 |
+
lines.append("- direct download: skipped (no Civitai key)")
|
| 2530 |
probe_text = "\n".join(lines)
|
| 2531 |
print(probe_text)
|
| 2532 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
| 2533 |
+
|
| 2534 |
try:
|
| 2535 |
reset_civitai_key_status(effective_api_key, source="probe-auth")
|
| 2536 |
+
auth_resp = request_civitai_api(
|
| 2537 |
session,
|
| 2538 |
+
"/models",
|
| 2539 |
api_key=effective_api_key,
|
| 2540 |
params={"query": probe_query, "limit": 1, "sort": "Newest", "period": "AllTime", "nsfw": "true"},
|
| 2541 |
timeout=(7.0, 20.0),
|
| 2542 |
label='Civitai probe models',
|
| 2543 |
source='probe-auth',
|
| 2544 |
)
|
| 2545 |
+
auth_status = getattr(auth_resp, "status_code", "-")
|
| 2546 |
+
lines.append(f"- auth api: {'ok' if auth_resp and auth_resp.ok else 'fail'} ({auth_status})")
|
| 2547 |
+
if auth_resp is not None and auth_resp.ok:
|
| 2548 |
+
payload = get_civitai_response_json(auth_resp, default={}) if auth_resp.content else {}
|
| 2549 |
dl_url, model, version, file_info = extract_probe_first_download_url(payload)
|
| 2550 |
if dl_url:
|
| 2551 |
lines.append(f"- auth first file: {model.get('name', '')} / {version.get('name', '')} / {round(float(file_info.get('sizeKB', 0.0) or 0.0) / 1000.0, 2)}MB")
|
| 2552 |
+
model_url = build_civitai_model_url(model.get('id'), version.get('id'))
|
| 2553 |
+
normalized_model_download = normalize_civitai_input_url(model_url, api_key=effective_api_key)
|
| 2554 |
+
normalized_auth_download = normalize_civitai_download_api_url(dl_url)
|
| 2555 |
+
model_download_parts = get_civitai_url_parts(normalized_model_download)
|
| 2556 |
+
auth_download_parts = get_civitai_url_parts(normalized_auth_download)
|
| 2557 |
+
same_download_path = bool(
|
| 2558 |
+
is_civitai_download_api_path(model_download_parts.path)
|
| 2559 |
+
and is_civitai_download_api_path(auth_download_parts.path)
|
| 2560 |
+
and model_download_parts.path == auth_download_parts.path
|
| 2561 |
+
)
|
| 2562 |
+
lines.append(
|
| 2563 |
+
f"- model page normalize: {'ok' if same_download_path else 'mismatch'} ({sanitize_url_for_log(normalized_model_download)})"
|
| 2564 |
+
)
|
| 2565 |
try:
|
| 2566 |
+
resolved_url = resolve_civitai_download_url(normalized_auth_download, effective_api_key, max_tries=1)
|
| 2567 |
resolved_host = urllib.parse.urlparse(resolved_url).netloc
|
| 2568 |
lines.append(f"- auth resolve: ok ({resolved_host})")
|
| 2569 |
+
download_probe = probe_download_url_step(session, resolved_url, api_key=effective_api_key, timeout=(7.0, 20.0))
|
| 2570 |
+
if download_probe["error"]:
|
| 2571 |
+
lines.append(f"- direct download: fail ({download_probe['error']})")
|
| 2572 |
+
else:
|
| 2573 |
+
lines.append(
|
| 2574 |
+
f"- direct download: {'ok' if download_probe['ok'] else 'fail'} ({download_probe['status']}, {download_probe['host'] or '-'})"
|
| 2575 |
+
)
|
| 2576 |
except Exception as e:
|
| 2577 |
lines.append(f"- auth resolve: fail ({type(e).__name__}: {e})")
|
| 2578 |
+
lines.append("- direct download: skipped (resolve failed)")
|
| 2579 |
else:
|
| 2580 |
lines.append("- auth resolve: skipped (no downloadUrl in first result)")
|
| 2581 |
+
lines.append("- direct download: skipped (no downloadUrl in first result)")
|
| 2582 |
except Exception as e:
|
| 2583 |
lines.append(f"- auth probe: fail ({type(e).__name__}: {e})")
|
| 2584 |
if not anon_ok:
|
|
|
|
| 2586 |
probe_text = "\n".join(lines)
|
| 2587 |
print(probe_text)
|
| 2588 |
return gr.update(value=probe_text, visible=True), gr.update(value=format_civitai_key_status_md(effective_api_key))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|