Support URL CB asset uploads
Browse files
app.py
CHANGED
|
@@ -20,7 +20,7 @@ from datetime import datetime
|
|
| 20 |
from functools import lru_cache
|
| 21 |
from io import BytesIO
|
| 22 |
from pathlib import Path
|
| 23 |
-
from urllib.parse import quote
|
| 24 |
from zoneinfo import ZoneInfo
|
| 25 |
|
| 26 |
ASYNCIO_FD_ERROR = "Invalid file descriptor: -1"
|
|
@@ -197,6 +197,15 @@ MODEL_LOCATION_CHOICES = [
|
|
| 197 |
(kind.replace("_", " ").title(), kind)
|
| 198 |
for kind in MODEL_KINDS
|
| 199 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
DEFAULT_NEGATIVE = (
|
| 201 |
"(censored, mosaic censoring, bar censor:1.1), bad quality, worst quality, "
|
| 202 |
"worst detail, bad anatomy, extra fingers, extra toes, extra legs, 4 toes, "
|
|
@@ -502,54 +511,90 @@ def bucket_numbers(kind, anima):
|
|
| 502 |
return numbers
|
| 503 |
|
| 504 |
|
| 505 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
if not valid_pass(password):
|
| 507 |
raise gr.Error("Invalid password")
|
| 508 |
if kind not in MODEL_KINDS:
|
| 509 |
raise gr.Error("Invalid CB location")
|
| 510 |
-
|
| 511 |
-
|
|
|
|
|
|
|
| 512 |
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
if
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
next_number += 1
|
| 540 |
-
number
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
)
|
| 551 |
-
|
| 552 |
-
|
|
|
|
| 553 |
|
| 554 |
|
| 555 |
def model_kind(name):
|
|
@@ -3612,6 +3657,7 @@ with gr.Blocks(title="Image generation") as demo:
|
|
| 3612 |
file_types=list(MODEL_SUFFIXES),
|
| 3613 |
type="filepath",
|
| 3614 |
)
|
|
|
|
| 3615 |
with gr.Row():
|
| 3616 |
model_location_input = gr.Dropdown(
|
| 3617 |
MODEL_LOCATION_CHOICES,
|
|
@@ -3622,7 +3668,7 @@ with gr.Blocks(title="Image generation") as demo:
|
|
| 3622 |
False,
|
| 3623 |
label="Anima model",
|
| 3624 |
)
|
| 3625 |
-
model_upload_button = gr.Button("
|
| 3626 |
model_upload_status = gr.Textbox(
|
| 3627 |
label="Status",
|
| 3628 |
interactive=False,
|
|
@@ -3990,6 +4036,7 @@ with gr.Blocks(title="Image generation") as demo:
|
|
| 3990 |
upload_bucket_assets,
|
| 3991 |
[
|
| 3992 |
model_files_input,
|
|
|
|
| 3993 |
model_location_input,
|
| 3994 |
anima_model_input,
|
| 3995 |
pass_input,
|
|
|
|
| 20 |
from functools import lru_cache
|
| 21 |
from io import BytesIO
|
| 22 |
from pathlib import Path
|
| 23 |
+
from urllib.parse import quote, unquote, urlparse
|
| 24 |
from zoneinfo import ZoneInfo
|
| 25 |
|
| 26 |
ASYNCIO_FD_ERROR = "Invalid file descriptor: -1"
|
|
|
|
| 197 |
(kind.replace("_", " ").title(), kind)
|
| 198 |
for kind in MODEL_KINDS
|
| 199 |
]
|
| 200 |
+
DOWNLOAD_HEADERS = {
|
| 201 |
+
"User-Agent": (
|
| 202 |
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) "
|
| 203 |
+
"Gecko/20100101 Firefox/140.0"
|
| 204 |
+
),
|
| 205 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
| 206 |
+
"Accept-Language": "en-US,en;q=0.5",
|
| 207 |
+
"Referer": "https://civitai.com/",
|
| 208 |
+
}
|
| 209 |
DEFAULT_NEGATIVE = (
|
| 210 |
"(censored, mosaic censoring, bar censor:1.1), bad quality, worst quality, "
|
| 211 |
"worst detail, bad anatomy, extra fingers, extra toes, extra legs, 4 toes, "
|
|
|
|
| 511 |
return numbers
|
| 512 |
|
| 513 |
|
| 514 |
+
def bucket_url_filename(response):
|
| 515 |
+
name = response.headers.get_filename()
|
| 516 |
+
if not name:
|
| 517 |
+
name = Path(urlparse(response.geturl()).path).name
|
| 518 |
+
name = unquote(name).replace("\\", "/").rsplit("/", 1)[-1].strip()
|
| 519 |
+
match = MODEL_NUMBER.match(name)
|
| 520 |
+
return match.group(2) if match else name
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
def upload_bucket_assets(files, url, kind, anima, password):
|
| 524 |
if not valid_pass(password):
|
| 525 |
raise gr.Error("Invalid password")
|
| 526 |
if kind not in MODEL_KINDS:
|
| 527 |
raise gr.Error("Invalid CB location")
|
| 528 |
+
files = files or []
|
| 529 |
+
url = (url or "").strip()
|
| 530 |
+
if not files and not url:
|
| 531 |
+
raise gr.Error("Select a file or enter a URL")
|
| 532 |
|
| 533 |
+
temp = None
|
| 534 |
+
try:
|
| 535 |
+
assets = []
|
| 536 |
+
for file in files:
|
| 537 |
+
source = Path(file)
|
| 538 |
+
match = MODEL_NUMBER.match(source.name)
|
| 539 |
+
number, name = (int(match.group(1)), match.group(2)) \
|
| 540 |
+
if match else (None, source.name)
|
| 541 |
+
if source.suffix.casefold() not in MODEL_SUFFIXES:
|
| 542 |
+
raise gr.Error(f"Unsupported model file: {name}")
|
| 543 |
+
if anima and not is_anima_asset(name):
|
| 544 |
+
name = f"{ANIMA_PREFIX}_{name}"
|
| 545 |
+
assets.append((source, number, name))
|
| 546 |
+
|
| 547 |
+
if url:
|
| 548 |
+
parsed = urlparse(url)
|
| 549 |
+
if parsed.scheme not in ("http", "https") or not parsed.netloc:
|
| 550 |
+
raise gr.Error("Enter a valid URL")
|
| 551 |
+
request = urllib.request.Request(url, headers=DOWNLOAD_HEADERS)
|
| 552 |
+
with urllib.request.urlopen(request) as response:
|
| 553 |
+
name = bucket_url_filename(response)
|
| 554 |
+
suffix = Path(name).suffix.casefold()
|
| 555 |
+
if suffix not in MODEL_SUFFIXES:
|
| 556 |
+
raise gr.Error("URL did not return a model file")
|
| 557 |
+
if anima and not is_anima_asset(name):
|
| 558 |
+
name = f"{ANIMA_PREFIX}_{name}"
|
| 559 |
+
with tempfile.NamedTemporaryFile(
|
| 560 |
+
suffix=suffix,
|
| 561 |
+
delete=False,
|
| 562 |
+
) as file:
|
| 563 |
+
temp = Path(file.name)
|
| 564 |
+
shutil.copyfileobj(response, file)
|
| 565 |
+
assets.append((temp, None, name))
|
| 566 |
+
|
| 567 |
+
with model_upload_lock:
|
| 568 |
+
used = bucket_numbers(kind, anima)
|
| 569 |
+
reserved = {
|
| 570 |
+
number for _, number, _ in assets
|
| 571 |
+
if number is not None and number not in used
|
| 572 |
+
}
|
| 573 |
+
next_number = max(used, default=0) + 1
|
| 574 |
+
additions = []
|
| 575 |
+
paths = []
|
| 576 |
+
for source, number, name in assets:
|
| 577 |
+
if number is not None and number in reserved:
|
| 578 |
+
reserved.remove(number)
|
| 579 |
+
else:
|
| 580 |
+
while next_number in used or next_number in reserved:
|
| 581 |
+
next_number += 1
|
| 582 |
+
number = next_number
|
| 583 |
next_number += 1
|
| 584 |
+
used.add(number)
|
| 585 |
+
path = f"{kind}/{number}_{name}"
|
| 586 |
+
additions.append((source, path))
|
| 587 |
+
paths.append(path)
|
| 588 |
+
batch_bucket_files(
|
| 589 |
+
BUCKET_ID,
|
| 590 |
+
add=additions,
|
| 591 |
+
token=IMAGE_TOKEN,
|
| 592 |
+
)
|
| 593 |
+
index_bucket_models()
|
| 594 |
+
return "Added " + ", ".join(paths)
|
| 595 |
+
finally:
|
| 596 |
+
if temp:
|
| 597 |
+
temp.unlink(missing_ok=True)
|
| 598 |
|
| 599 |
|
| 600 |
def model_kind(name):
|
|
|
|
| 3657 |
file_types=list(MODEL_SUFFIXES),
|
| 3658 |
type="filepath",
|
| 3659 |
)
|
| 3660 |
+
model_url_input = gr.Textbox(label="URL")
|
| 3661 |
with gr.Row():
|
| 3662 |
model_location_input = gr.Dropdown(
|
| 3663 |
MODEL_LOCATION_CHOICES,
|
|
|
|
| 3668 |
False,
|
| 3669 |
label="Anima model",
|
| 3670 |
)
|
| 3671 |
+
model_upload_button = gr.Button("Add")
|
| 3672 |
model_upload_status = gr.Textbox(
|
| 3673 |
label="Status",
|
| 3674 |
interactive=False,
|
|
|
|
| 4036 |
upload_bucket_assets,
|
| 4037 |
[
|
| 4038 |
model_files_input,
|
| 4039 |
+
model_url_input,
|
| 4040 |
model_location_input,
|
| 4041 |
anima_model_input,
|
| 4042 |
pass_input,
|