feat(model-manager): flag Early Access models in CivitAI browse

CivitAI Early Access versions require purchased access and otherwise fail
with 401. Surface version `availability` from the API as an `early_access`
flag (per card and per version), show an amber "EARLY ACCESS" tag on the
card, label such entries in the version dropdown, and warn before
attempting to download one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 16:27:33 -04:00
parent 6871a6d460
commit 505f212c4d
4 changed files with 45 additions and 6 deletions
+13 -1
View File
@@ -176,6 +176,12 @@ def _pick_thumbnail(version: dict) -> Optional[dict]:
return None
def _is_early_access(version: dict) -> bool:
"""A version is early-access (download requires purchase/login) when its
availability is anything other than Public."""
return (version.get("availability") or "Public") != "Public"
def _to_card(item: dict) -> dict:
versions = item.get("modelVersions") or []
v0 = versions[0] if versions else {}
@@ -192,8 +198,14 @@ def _to_card(item: dict) -> dict:
"thumbnail": thumb.get("url") if thumb else None,
"thumbnail_type": thumb.get("type") if thumb else None,
"primary_version_id": v0.get("id"),
"early_access": _is_early_access(v0),
"versions": [
{"id": v.get("id"), "name": v.get("name")} for v in versions
{
"id": v.get("id"),
"name": v.get("name"),
"early_access": _is_early_access(v),
}
for v in versions
],
}
+25 -4
View File
@@ -477,6 +477,12 @@ function renderBrowseCard(m) {
n.textContent = "NSFW";
thumb.appendChild(n);
}
if (m.early_access) {
const ea = document.createElement("span");
ea.className = "ea-tag";
ea.textContent = "EARLY ACCESS";
thumb.appendChild(ea);
}
const body = document.createElement("div");
body.className = "body";
@@ -497,7 +503,8 @@ function renderBrowseCard(m) {
for (const v of m.versions) {
const o = document.createElement("option");
o.value = v.id;
o.textContent = v.name || `v${v.id}`;
o.textContent = (v.name || `v${v.id}`) + (v.early_access ? " — Early Access" : "");
o.dataset.ea = v.early_access ? "1" : "";
versionSel.appendChild(o);
}
foot.appendChild(versionSel);
@@ -507,8 +514,15 @@ function renderBrowseCard(m) {
btn.className = "btn primary";
btn.textContent = "Download";
btn.addEventListener("click", () => {
const vid = versionSel ? Number(versionSel.value) : m.primary_version_id;
downloadVersion(vid, btn);
let vid, isEa;
if (versionSel) {
vid = Number(versionSel.value);
isEa = versionSel.selectedOptions[0]?.dataset.ea === "1";
} else {
vid = m.primary_version_id;
isEa = m.early_access;
}
downloadVersion(vid, btn, isEa);
});
foot.appendChild(btn);
@@ -517,8 +531,15 @@ function renderBrowseCard(m) {
return card;
}
async function downloadVersion(versionId, btn) {
async function downloadVersion(versionId, btn, isEarlyAccess) {
if (!versionId) { toast($("#browseToast"), "No version available", true); return; }
if (isEarlyAccess) {
const ok = await confirmDialog(
"This is an Early Access version. CivitAI only allows downloading it if " +
"your account has purchased/unlocked access — otherwise it will fail with " +
"401. Try anyway?", "Download");
if (!ok) return;
}
btn.disabled = true;
const original = btn.textContent;
btn.textContent = "Queued ✓";
+5
View File
@@ -242,6 +242,11 @@ code { background: var(--bg-3); padding: 1px 5px; border-radius: 4px; font-size:
background: rgba(239,93,107,.85); color: #fff;
font-size: 10px; padding: 2px 7px; border-radius: 8px; font-weight: 600;
}
.browse-card .thumb .ea-tag {
position: absolute; bottom: 8px; left: 8px;
background: rgba(240,180,0,.92); color: #1a1400;
font-size: 10px; padding: 2px 7px; border-radius: 8px; font-weight: 700;
}
.browse-card .body { padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.browse-card .b-name { font-weight: 600; font-size: 13px; line-height: 1.25;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }