Spaces:
Running on Zero
Running on Zero
Upload app.py
Browse files
app.py
CHANGED
|
@@ -1223,6 +1223,90 @@ def enhance_prompt(prompt, use_model, camera, framing, lighting, pace, add_quali
|
|
| 1223 |
)
|
| 1224 |
|
| 1225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1226 |
def _target_size(image):
|
| 1227 |
"""Same math as resize_image(), but returns only the final (w, h)."""
|
| 1228 |
if image is None:
|
|
@@ -1378,7 +1462,7 @@ CSS = """
|
|
| 1378 |
box-shadow: 0 8px 22px rgba(124, 58, 237, .28);
|
| 1379 |
}
|
| 1380 |
#estimate-btn, #enhance-btn { min-height: 42px !important; border-radius: 12px !important; }
|
| 1381 |
-
#profile-load, #profile-save, #profile-delete, #profile-refresh {
|
| 1382 |
min-height: 42px !important; border-radius: 12px !important; font-weight: 600 !important;
|
| 1383 |
}
|
| 1384 |
#stop-btn, #clear-btn { min-height: 46px !important; border-radius: 12px !important; }
|
|
@@ -1630,8 +1714,15 @@ with gr.Blocks(delete_cache=(3600, 86400)) as demo:
|
|
| 1630 |
"two separate files, so paste the low-noise link straight into the Low box "
|
| 1631 |
"(both boxes accept a pasted link). The Space downloads it itself, so being "
|
| 1632 |
"signed in to CivitAI in your browser does not help a gated model: add "
|
| 1633 |
-
"`CIVITAI_TOKEN` under Settings → Variables and secrets. Downloads are cached."
|
|
|
|
|
|
|
|
|
|
| 1634 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1635 |
_my_rows = []
|
| 1636 |
for _i in range(1, 6):
|
| 1637 |
with gr.Accordion(f"Custom lora {_i}", open=(_i == 1)):
|
|
@@ -1871,6 +1962,16 @@ with gr.Blocks(delete_cache=(3600, 86400)) as demo:
|
|
| 1871 |
outputs=[merged_video, merged_file, merge_queue, merge_status],
|
| 1872 |
)
|
| 1873 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1874 |
_SETTINGS_FIELDS = [
|
| 1875 |
prompt_input, negative_prompt_input, duration_seconds_input, steps_slider, quality_slider,
|
| 1876 |
seed_input, randomize_seed_checkbox, scheduler_dropdown, flow_shift_slider, frame_multi,
|
|
|
|
| 1223 |
)
|
| 1224 |
|
| 1225 |
|
| 1226 |
+
# ---------------------------------------------------------------------------
|
| 1227 |
+
# Naming CivitAI links
|
| 1228 |
+
# ---------------------------------------------------------------------------
|
| 1229 |
+
# A CivitAI download URL is a bare number, which is useless once three slots are
|
| 1230 |
+
# filled. The number is the *model version* id, and CivitAI's public API turns it
|
| 1231 |
+
# into the model's title, the version, the file behind `fileId`, and the words the
|
| 1232 |
+
# adapter was trained on. Public, cached, and never fatal.
|
| 1233 |
+
|
| 1234 |
+
from functools import lru_cache
|
| 1235 |
+
|
| 1236 |
+
CIVITAI_DOWNLOAD_RE = re.compile(r"/api/download/models/(\d+)")
|
| 1237 |
+
|
| 1238 |
+
|
| 1239 |
+
@lru_cache(maxsize=128)
|
| 1240 |
+
def civitai_details(version_id: str, file_id: str = ""):
|
| 1241 |
+
import requests
|
| 1242 |
+
|
| 1243 |
+
headers = {"User-Agent": "Mozilla/5.0"}
|
| 1244 |
+
token = os.environ.get("CIVITAI_TOKEN", "").strip()
|
| 1245 |
+
if token:
|
| 1246 |
+
headers["Authorization"] = f"Bearer {token}"
|
| 1247 |
+
|
| 1248 |
+
try:
|
| 1249 |
+
response = requests.get(
|
| 1250 |
+
f"https://civitai.com/api/v1/model-versions/{version_id}", headers=headers, timeout=20
|
| 1251 |
+
)
|
| 1252 |
+
response.raise_for_status()
|
| 1253 |
+
data = response.json()
|
| 1254 |
+
except Exception as error:
|
| 1255 |
+
return f"CivitAI {version_id} (name unavailable: {type(error).__name__})", []
|
| 1256 |
+
|
| 1257 |
+
model_name = (data.get("model") or {}).get("name") or f"model {data.get('modelId', '?')}"
|
| 1258 |
+
version_name = data.get("name") or ""
|
| 1259 |
+
label = f"{model_name} · {version_name}".strip(" ·")
|
| 1260 |
+
|
| 1261 |
+
if file_id:
|
| 1262 |
+
for entry in data.get("files") or []:
|
| 1263 |
+
if str(entry.get("id")) == str(file_id):
|
| 1264 |
+
label += f" · {entry.get('name', '')}"
|
| 1265 |
+
break
|
| 1266 |
+
|
| 1267 |
+
return label, [word for word in (data.get("trainedWords") or []) if word]
|
| 1268 |
+
|
| 1269 |
+
|
| 1270 |
+
def describe_lora(reference: str):
|
| 1271 |
+
"""A readable line for whatever is in a box: a CivitAI link becomes its real title."""
|
| 1272 |
+
reference = (reference or "").strip()
|
| 1273 |
+
if not reference or reference == "(None)":
|
| 1274 |
+
return "", []
|
| 1275 |
+
|
| 1276 |
+
match = CIVITAI_DOWNLOAD_RE.search(reference)
|
| 1277 |
+
if match:
|
| 1278 |
+
file_id = ""
|
| 1279 |
+
if "fileId=" in reference:
|
| 1280 |
+
file_id = reference.split("fileId=", 1)[1].split("&")[0]
|
| 1281 |
+
return civitai_details(match.group(1), file_id)
|
| 1282 |
+
|
| 1283 |
+
if reference.startswith(("http://", "https://")):
|
| 1284 |
+
return os.path.basename(reference.split("?")[0]) or reference, []
|
| 1285 |
+
return reference, []
|
| 1286 |
+
|
| 1287 |
+
|
| 1288 |
+
def identify_loras(*fields):
|
| 1289 |
+
"""Name every filled box across the five slots. `fields` arrives as
|
| 1290 |
+
`repo, high, low` per slot, so each slot is reported on one line."""
|
| 1291 |
+
lines = []
|
| 1292 |
+
for slot in range(len(fields) // 3):
|
| 1293 |
+
repo, high, low = fields[slot * 3: slot * 3 + 3]
|
| 1294 |
+
parts = []
|
| 1295 |
+
for role, value in (("repo", repo), ("high", high), ("low", low)):
|
| 1296 |
+
label, words = describe_lora(value)
|
| 1297 |
+
if not label:
|
| 1298 |
+
continue
|
| 1299 |
+
entry = f"{role}: {label}"
|
| 1300 |
+
if words:
|
| 1301 |
+
entry += f" — triggers: {', '.join(words[:6])}"
|
| 1302 |
+
parts.append(entry)
|
| 1303 |
+
if parts:
|
| 1304 |
+
lines.append(f"<b>Custom lora {slot + 1}</b> — " + " · ".join(parts))
|
| 1305 |
+
if not lines:
|
| 1306 |
+
return "<div class='status-idle'>Nothing in the slots yet.</div>"
|
| 1307 |
+
return "<div class='status-ok'>" + "<br>".join(lines) + "</div>"
|
| 1308 |
+
|
| 1309 |
+
|
| 1310 |
def _target_size(image):
|
| 1311 |
"""Same math as resize_image(), but returns only the final (w, h)."""
|
| 1312 |
if image is None:
|
|
|
|
| 1462 |
box-shadow: 0 8px 22px rgba(124, 58, 237, .28);
|
| 1463 |
}
|
| 1464 |
#estimate-btn, #enhance-btn { min-height: 42px !important; border-radius: 12px !important; }
|
| 1465 |
+
#lora-identify, #profile-load, #profile-save, #profile-delete, #profile-refresh {
|
| 1466 |
min-height: 42px !important; border-radius: 12px !important; font-weight: 600 !important;
|
| 1467 |
}
|
| 1468 |
#stop-btn, #clear-btn { min-height: 46px !important; border-radius: 12px !important; }
|
|
|
|
| 1714 |
"two separate files, so paste the low-noise link straight into the Low box "
|
| 1715 |
"(both boxes accept a pasted link). The Space downloads it itself, so being "
|
| 1716 |
"signed in to CivitAI in your browser does not help a gated model: add "
|
| 1717 |
+
"`CIVITAI_TOKEN` under Settings → Variables and secrets. Downloads are cached.\n\n"
|
| 1718 |
+
"A CivitAI link is just a number, so press **🔎 name the CivitAI links** and "
|
| 1719 |
+
"each box is replaced by its real title, version, file name and trigger words, "
|
| 1720 |
+
"read from CivitAI's public API."
|
| 1721 |
)
|
| 1722 |
+
with gr.Row():
|
| 1723 |
+
lora_identify_btn = gr.Button("🔎 name the CivitAI links",
|
| 1724 |
+
variant="secondary", elem_id="lora-identify")
|
| 1725 |
+
lora_names = gr.HTML("<div class='status-idle'>Nothing in the slots yet.</div>")
|
| 1726 |
_my_rows = []
|
| 1727 |
for _i in range(1, 6):
|
| 1728 |
with gr.Accordion(f"Custom lora {_i}", open=(_i == 1)):
|
|
|
|
| 1962 |
outputs=[merged_video, merged_file, merge_queue, merge_status],
|
| 1963 |
)
|
| 1964 |
|
| 1965 |
+
# Naming the links. Registered here with the rest of the additions, and reading every box of every slot.
|
| 1966 |
+
_IDENTIFY_FIELDS = [
|
| 1967 |
+
my1_repo, my1_high, my1_low,
|
| 1968 |
+
my2_repo, my2_high, my2_low,
|
| 1969 |
+
my3_repo, my3_high, my3_low,
|
| 1970 |
+
my4_repo, my4_high, my4_low,
|
| 1971 |
+
my5_repo, my5_high, my5_low,
|
| 1972 |
+
]
|
| 1973 |
+
lora_identify_btn.click(identify_loras, _IDENTIFY_FIELDS, lora_names)
|
| 1974 |
+
|
| 1975 |
_SETTINGS_FIELDS = [
|
| 1976 |
prompt_input, negative_prompt_input, duration_seconds_input, steps_slider, quality_slider,
|
| 1977 |
seed_input, randomize_seed_checkbox, scheduler_dropdown, flow_shift_slider, frame_multi,
|