Add CB asset URL downloader
Browse files
app.py
CHANGED
|
@@ -20,6 +20,7 @@ from datetime import datetime
|
|
| 20 |
from functools import lru_cache
|
| 21 |
from io import BytesIO
|
| 22 |
from pathlib import Path
|
|
|
|
| 23 |
from zoneinfo import ZoneInfo
|
| 24 |
|
| 25 |
ASYNCIO_FD_ERROR = "Invalid file descriptor: -1"
|
|
@@ -193,6 +194,10 @@ MODEL_SUFFIXES = (
|
|
| 193 |
NUMBERED_MODEL_KINDS = ("checkpoints", "loras")
|
| 194 |
MODEL_NUMBER = re.compile(r"^(\d+)_(.+)$")
|
| 195 |
ANIMA_PREFIX = "anima"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
DEFAULT_NEGATIVE = (
|
| 197 |
"(censored, mosaic censoring, bar censor:1.1), bad quality, worst quality, "
|
| 198 |
"worst detail, bad anatomy, extra fingers, extra toes, extra legs, 4 toes, "
|
|
@@ -284,6 +289,7 @@ pool = ThreadPoolExecutor(max_workers=1)
|
|
| 284 |
backup_pool = ThreadPoolExecutor(max_workers=1)
|
| 285 |
matrix_pool = ThreadPoolExecutor(max_workers=1)
|
| 286 |
model_pool = ThreadPoolExecutor(max_workers=1)
|
|
|
|
| 287 |
matrix_grids = set()
|
| 288 |
local_client = None
|
| 289 |
local_client_lock = threading.Lock()
|
|
@@ -398,6 +404,11 @@ def download_assets(models):
|
|
| 398 |
)
|
| 399 |
|
| 400 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
def index_bucket_models():
|
| 402 |
models = {kind: {} for kind in MODEL_KINDS}
|
| 403 |
items = [
|
|
@@ -414,7 +425,7 @@ def index_bucket_models():
|
|
| 414 |
kind, separator, name = item.path.partition("/")
|
| 415 |
match = MODEL_NUMBER.match(name)
|
| 416 |
if separator and kind in counters and match:
|
| 417 |
-
anima = match.group(2)
|
| 418 |
counters[kind][anima] = max(counters[kind][anima], int(match.group(1)))
|
| 419 |
|
| 420 |
copies = []
|
|
@@ -424,7 +435,7 @@ def index_bucket_models():
|
|
| 424 |
if not separator or kind not in models:
|
| 425 |
continue
|
| 426 |
if kind in counters and not MODEL_NUMBER.match(name):
|
| 427 |
-
anima =
|
| 428 |
counters[kind][anima] += 1
|
| 429 |
name = f"{counters[kind][anima]}_{name}"
|
| 430 |
path = f"{kind}/{name}"
|
|
@@ -451,6 +462,78 @@ def index_bucket_models():
|
|
| 451 |
return added
|
| 452 |
|
| 453 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
def model_kind(name):
|
| 455 |
return "diffusion_models" if is_anima_model(name) else "checkpoints"
|
| 456 |
|
|
@@ -3480,6 +3563,23 @@ with gr.Blocks(title="Image generation") as demo:
|
|
| 3480 |
)
|
| 3481 |
model_state = gr.State(DEFAULT_MODEL)
|
| 3482 |
refresh_button = gr.Button("Refresh", scale=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3483 |
prompt_input = gr.Textbox(label="Prompt", lines=6)
|
| 3484 |
negative_input = gr.Textbox(
|
| 3485 |
DEFAULT_NEGATIVE,
|
|
@@ -3839,6 +3939,18 @@ with gr.Blocks(title="Image generation") as demo:
|
|
| 3839 |
],
|
| 3840 |
queue=False,
|
| 3841 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3842 |
|
| 3843 |
api_button.click(
|
| 3844 |
api_generate,
|
|
|
|
| 20 |
from functools import lru_cache
|
| 21 |
from io import BytesIO
|
| 22 |
from pathlib import Path
|
| 23 |
+
from urllib.parse import unquote, urlparse
|
| 24 |
from zoneinfo import ZoneInfo
|
| 25 |
|
| 26 |
ASYNCIO_FD_ERROR = "Invalid file descriptor: -1"
|
|
|
|
| 194 |
NUMBERED_MODEL_KINDS = ("checkpoints", "loras")
|
| 195 |
MODEL_NUMBER = re.compile(r"^(\d+)_(.+)$")
|
| 196 |
ANIMA_PREFIX = "anima"
|
| 197 |
+
MODEL_LOCATION_CHOICES = [
|
| 198 |
+
(kind.replace("_", " ").title(), kind)
|
| 199 |
+
for kind in MODEL_KINDS
|
| 200 |
+
]
|
| 201 |
DEFAULT_NEGATIVE = (
|
| 202 |
"(censored, mosaic censoring, bar censor:1.1), bad quality, worst quality, "
|
| 203 |
"worst detail, bad anatomy, extra fingers, extra toes, extra legs, 4 toes, "
|
|
|
|
| 289 |
backup_pool = ThreadPoolExecutor(max_workers=1)
|
| 290 |
matrix_pool = ThreadPoolExecutor(max_workers=1)
|
| 291 |
model_pool = ThreadPoolExecutor(max_workers=1)
|
| 292 |
+
model_download_lock = threading.Lock()
|
| 293 |
matrix_grids = set()
|
| 294 |
local_client = None
|
| 295 |
local_client_lock = threading.Lock()
|
|
|
|
| 404 |
)
|
| 405 |
|
| 406 |
|
| 407 |
+
def is_anima_asset(name):
|
| 408 |
+
name = name.casefold()
|
| 409 |
+
return name.startswith(ANIMA_PREFIX) and not name.startswith("animag")
|
| 410 |
+
|
| 411 |
+
|
| 412 |
def index_bucket_models():
|
| 413 |
models = {kind: {} for kind in MODEL_KINDS}
|
| 414 |
items = [
|
|
|
|
| 425 |
kind, separator, name = item.path.partition("/")
|
| 426 |
match = MODEL_NUMBER.match(name)
|
| 427 |
if separator and kind in counters and match:
|
| 428 |
+
anima = is_anima_asset(match.group(2))
|
| 429 |
counters[kind][anima] = max(counters[kind][anima], int(match.group(1)))
|
| 430 |
|
| 431 |
copies = []
|
|
|
|
| 435 |
if not separator or kind not in models:
|
| 436 |
continue
|
| 437 |
if kind in counters and not MODEL_NUMBER.match(name):
|
| 438 |
+
anima = is_anima_asset(name)
|
| 439 |
counters[kind][anima] += 1
|
| 440 |
name = f"{counters[kind][anima]}_{name}"
|
| 441 |
path = f"{kind}/{name}"
|
|
|
|
| 462 |
return added
|
| 463 |
|
| 464 |
|
| 465 |
+
def bucket_url_filename(response):
|
| 466 |
+
name = response.headers.get_filename()
|
| 467 |
+
if not name:
|
| 468 |
+
name = Path(urlparse(response.geturl()).path).name
|
| 469 |
+
name = unquote(name).replace("\\", "/").rsplit("/", 1)[-1].strip()
|
| 470 |
+
match = MODEL_NUMBER.match(name)
|
| 471 |
+
return match.group(2) if match else name
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
def next_bucket_number(kind, anima):
|
| 475 |
+
numbers = []
|
| 476 |
+
for item in list_bucket_tree(
|
| 477 |
+
BUCKET_ID,
|
| 478 |
+
prefix=f"{kind}/",
|
| 479 |
+
recursive=True,
|
| 480 |
+
token=False,
|
| 481 |
+
):
|
| 482 |
+
if item.type != "file" or "/" in item.path.removeprefix(f"{kind}/"):
|
| 483 |
+
continue
|
| 484 |
+
match = MODEL_NUMBER.match(Path(item.path).name)
|
| 485 |
+
if match and is_anima_asset(match.group(2)) == anima:
|
| 486 |
+
numbers.append(int(match.group(1)))
|
| 487 |
+
return max(numbers, default=0) + 1
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
def download_bucket_asset(url, kind, anima, password):
|
| 491 |
+
if not valid_pass(password):
|
| 492 |
+
raise gr.Error("Invalid password")
|
| 493 |
+
url = url.strip()
|
| 494 |
+
parsed = urlparse(url)
|
| 495 |
+
if parsed.scheme not in ("http", "https") or not parsed.netloc:
|
| 496 |
+
raise gr.Error("Enter a valid URL")
|
| 497 |
+
if kind not in MODEL_KINDS:
|
| 498 |
+
raise gr.Error("Invalid CB location")
|
| 499 |
+
if anima and kind == "checkpoints":
|
| 500 |
+
kind = "diffusion_models"
|
| 501 |
+
|
| 502 |
+
temp = None
|
| 503 |
+
try:
|
| 504 |
+
request = urllib.request.Request(
|
| 505 |
+
url,
|
| 506 |
+
headers={"User-Agent": "Mozilla/5.0"},
|
| 507 |
+
)
|
| 508 |
+
with urllib.request.urlopen(request) as response:
|
| 509 |
+
name = bucket_url_filename(response)
|
| 510 |
+
if Path(name).suffix.casefold() not in MODEL_SUFFIXES:
|
| 511 |
+
raise gr.Error("URL did not return a model file")
|
| 512 |
+
if anima and not is_anima_asset(name):
|
| 513 |
+
name = f"{ANIMA_PREFIX}_{name}"
|
| 514 |
+
with tempfile.NamedTemporaryFile(
|
| 515 |
+
suffix=Path(name).suffix,
|
| 516 |
+
delete=False,
|
| 517 |
+
) as file:
|
| 518 |
+
temp = Path(file.name)
|
| 519 |
+
shutil.copyfileobj(response, file)
|
| 520 |
+
|
| 521 |
+
with model_download_lock:
|
| 522 |
+
number = next_bucket_number(kind, anima)
|
| 523 |
+
name = f"{number}_{name}"
|
| 524 |
+
path = f"{kind}/{name}"
|
| 525 |
+
batch_bucket_files(
|
| 526 |
+
BUCKET_ID,
|
| 527 |
+
add=[(temp, path)],
|
| 528 |
+
token=IMAGE_TOKEN,
|
| 529 |
+
)
|
| 530 |
+
index_bucket_models()
|
| 531 |
+
return f"Added {path}"
|
| 532 |
+
finally:
|
| 533 |
+
if temp:
|
| 534 |
+
temp.unlink(missing_ok=True)
|
| 535 |
+
|
| 536 |
+
|
| 537 |
def model_kind(name):
|
| 538 |
return "diffusion_models" if is_anima_model(name) else "checkpoints"
|
| 539 |
|
|
|
|
| 3563 |
)
|
| 3564 |
model_state = gr.State(DEFAULT_MODEL)
|
| 3565 |
refresh_button = gr.Button("Refresh", scale=1)
|
| 3566 |
+
with gr.Accordion("Add CB asset", open=False):
|
| 3567 |
+
model_url_input = gr.Textbox(label="URL")
|
| 3568 |
+
with gr.Row():
|
| 3569 |
+
model_location_input = gr.Dropdown(
|
| 3570 |
+
MODEL_LOCATION_CHOICES,
|
| 3571 |
+
value="checkpoints",
|
| 3572 |
+
label="CB location",
|
| 3573 |
+
)
|
| 3574 |
+
anima_model_input = gr.Checkbox(
|
| 3575 |
+
False,
|
| 3576 |
+
label="Anima model",
|
| 3577 |
+
)
|
| 3578 |
+
model_download_button = gr.Button("Download")
|
| 3579 |
+
model_download_status = gr.Textbox(
|
| 3580 |
+
label="Status",
|
| 3581 |
+
interactive=False,
|
| 3582 |
+
)
|
| 3583 |
prompt_input = gr.Textbox(label="Prompt", lines=6)
|
| 3584 |
negative_input = gr.Textbox(
|
| 3585 |
DEFAULT_NEGATIVE,
|
|
|
|
| 3939 |
],
|
| 3940 |
queue=False,
|
| 3941 |
)
|
| 3942 |
+
model_download_button.click(
|
| 3943 |
+
download_bucket_asset,
|
| 3944 |
+
[
|
| 3945 |
+
model_url_input,
|
| 3946 |
+
model_location_input,
|
| 3947 |
+
anima_model_input,
|
| 3948 |
+
pass_input,
|
| 3949 |
+
],
|
| 3950 |
+
model_download_status,
|
| 3951 |
+
queue=False,
|
| 3952 |
+
api_visibility="private",
|
| 3953 |
+
)
|
| 3954 |
|
| 3955 |
api_button.click(
|
| 3956 |
api_generate,
|