sd-civitai-browser-plus/scripts/civitai_api.py

886 lines
43 KiB
Python

import requests
import json
import gradio as gr
import urllib.request
import urllib.parse
import urllib.error
import os
import re
from collections import defaultdict
from modules.shared import cmd_opts, opts
from modules.paths import models_path, extensions_dir
from html import escape
import scripts.civitai_global as gl
import scripts.civitai_download as _download
gl.init()
def update_dl_url(trained_tags, model_id=None, model_name=None, model_version=None):
if model_version and "[Installed]" in model_version:
model_version = model_version.replace(" [Installed]", "")
if model_id:
dl_url = None
for item in gl.json_data['items']:
if item['name'] == model_name:
for model in item['modelVersions']:
if model['name'] == model_version:
for file in model['files']:
if int(file['id']) == int(model_id):
dl_url = file['downloadUrl']
gl.json_info = model
return (
gr.Textbox.update(value=dl_url), # Download URL
gr.Button.update(interactive=True if trained_tags else False), # Save Tags Button
gr.Button.update(interactive=True if model_version else False), # Save Images Button
gr.Button.update(interactive=True if model_version else False) # Download Button
)
else:
return (
gr.Textbox.update(value=None), # Download URL
gr.Button.update(interactive=True if trained_tags else False), # Save Tags Button
gr.Button.update(interactive=True if model_version else False), # Save Images Button
gr.Button.update(interactive=True if model_version else False) # Download Button
)
def contenttype_folder(content_type, desc=None, fromCheck=False):
use_LORA = getattr(opts, "use_LORA", False)
folder = None
if desc:
desc = desc.upper()
if content_type == "modelFolder":
folder = os.path.join(models_path)
if content_type == "Checkpoint":
if cmd_opts.ckpt_dir:
folder = cmd_opts.ckpt_dir
else:
folder = os.path.join(models_path,"Stable-diffusion")
elif content_type == "Hypernetwork":
folder = cmd_opts.hypernetwork_dir
elif content_type == "TextualInversion":
folder = cmd_opts.embeddings_dir
elif content_type == "AestheticGradient":
folder = os.path.join(extensions_dir, "stable-diffusion-webui-aesthetic-gradients", "aesthetic_embeddings")
elif content_type == "LORA":
folder = cmd_opts.lora_dir
elif content_type == "LoCon":
if "lyco_dir" in cmd_opts:
folder = f"{cmd_opts.lyco_dir}"
elif "lyco_dir_backcompat" in cmd_opts:
folder = f"{cmd_opts.lyco_dir_backcompat}"
else:
folder = os.path.join(models_path,"LyCORIS")
if use_LORA and not fromCheck:
folder = cmd_opts.lora_dir
elif content_type == "VAE":
if cmd_opts.vae_dir:
folder = cmd_opts.vae_dir
else:
folder = os.path.join(models_path,"VAE")
elif content_type == "Controlnet":
if cmd_opts.ckpt_dir:
folder = os.path.join(os.path.join(cmd_opts.ckpt_dir, os.pardir), "ControlNet")
else:
folder = os.path.join(models_path,"ControlNet")
elif content_type == "Poses":
if cmd_opts.ckpt_dir:
folder = os.path.join(os.path.join(cmd_opts.ckpt_dir, os.pardir), "Poses")
else:
folder = os.path.join(models_path,"Poses")
elif content_type == "Upscaler":
if "REALESRGAN" in desc:
folder = os.path.join(models_path,"RealESRGAN")
elif "SWINIR" in desc:
folder = os.path.join(models_path,"SwinIR")
else:
folder = os.path.join(models_path,"ESRGAN")
elif content_type == "MotionModule":
folder = os.path.join(extensions_dir, "sd-webui-animatediff", "model")
elif content_type == "Workflows":
folder = os.path.join(models_path,"Workflows")
elif content_type == "Other":
if "ADETAILER" in desc:
folder = os.path.join(models_path,"adetailer")
else:
folder = os.path.join(models_path,"Other")
elif content_type == "Wildcards":
folder = os.path.join(extensions_dir, "UnivAICharGen", "wildcards")
if not os.path.exists(folder):
folder = os.path.join(extensions_dir, "sd-dynamic-prompts", "wildcards")
return folder
def api_to_data(content_type, sort_type, period_type, use_search_term, current_page, base_filter, search_term=None, timeOut=None, isNext=None):
if current_page in [0, None, ""]:
current_page = 1
if search_term != gl.previous_search_term or gl.tile_count != gl.previous_tile_count or gl.inputs_changed or gl.contentChange:
gl.previous_search_term = search_term
gl.previous_tile_count = gl.tile_count
gl.file_scan = False
api_url = f"https://civitai.com/api/v1/models?limit={gl.tile_count}&page=1"
else:
api_url = f"https://civitai.com/api/v1/models?limit={gl.tile_count}&page={current_page}"
if timeOut:
if isNext:
next_page = str(int(current_page) + 1)
else:
if current_page not in [1, 0, None, ""]:
next_page = str(int(current_page) - 1)
api_url = f"https://civitai.com/api/v1/models?limit={gl.tile_count}&page={next_page}"
period_type = period_type.replace(" ", "")
query = {'sort': sort_type, 'period': period_type}
types_query_str = ""
if content_type:
types_query_str = "".join([f"&types={type}" for type in content_type])
query_str = urllib.parse.urlencode(query, quote_via=urllib.parse.quote)
if types_query_str:
query_str += types_query_str
if use_search_term != "None" and search_term:
if "civitai.com" in search_term:
match = re.search(r'models/(\d+)', search_term)
model_number = match.group(1)
query_str = f"&ids={urllib.parse.quote(model_number)}"
elif use_search_term == "User name":
query_str += f"&username={urllib.parse.quote(search_term)}"
elif use_search_term == "Tag":
query_str += f"&tag={urllib.parse.quote(search_term)}"
else:
query_str += f"&query={urllib.parse.quote(search_term)}"
if base_filter:
for base in base_filter:
query_str += f"&baseModels={urllib.parse.quote(base)}"
full_url = f"{api_url}&{query_str}"
if gl.file_scan:
highest_number = max(gl.url_list_with_numbers.keys())
full_url = gl.url_list_with_numbers.get(int(current_page))
nextPage = int(current_page) + 1
prevPage = int(current_page) - 1
data = request_civit_api(full_url)
data["metadata"]["currentPage"] = current_page
data["metadata"]["totalPages"] = highest_number
if not nextPage > highest_number:
data["metadata"]["nextPage"] = gl.url_list_with_numbers.get(nextPage)
if not prevPage == 0:
data["metadata"]["prevPage"] = gl.url_list_with_numbers.get(prevPage)
else:
data = request_civit_api(full_url)
return data
def model_list_html(json_data, model_dict):
gl.contentChange = False
HTML = '<div class="column civmodellist">'
sorted_models = {}
for item in json_data['items']:
for k, model in model_dict.items():
if model_dict[k].lower() == item['name'].lower():
model_name = escape(item["name"].replace("'", "\\'"), quote=True)
if model_name:
selected_content_type = item['type']
desc = item['description']
if not selected_content_type:
return
nsfw = ""
installstatus = ""
baseModel = ""
try:
if 'baseModel' in item['modelVersions'][0]:
baseModel = item['modelVersions'][0]['baseModel']
except:
baseModel = "Not Found"
try:
if 'updatedAt' in item['modelVersions'][0]:
date = item['modelVersions'][0]['updatedAt'].split('T')[0]
except:
baseModel = "Not Found"
if gl.sortNewest:
if date not in sorted_models:
sorted_models[date] = []
if any(item['modelVersions']):
if len(item['modelVersions'][0]['images']) > 0:
if item["modelVersions"][0]["images"][0]['nsfw'] not in ["None", "Soft"]:
nsfw = "civcardnsfw"
media_type = item["modelVersions"][0]["images"][0]["type"]
image = item["modelVersions"][0]["images"][0]["url"]
if media_type == "video":
image = image.replace("width=", "transcode=true,width=")
imgtag = f'<video class="video-bg" autoplay loop muted playsinline><source src="{image}" type="video/mp4"></video>'
else:
imgtag = f'<img src="{image}"></img>'
else:
imgtag = f'<img src="./file=html/card-no-preview.png"></img>'
model_folder = os.path.join(contenttype_folder(selected_content_type, desc))
existing_files = []
existing_files_sha256 = []
for root, dirs, files in os.walk(model_folder):
for file in files:
existing_files.append(file)
if file.endswith('.json'):
json_path = os.path.join(root, file)
with open(json_path, 'r') as f:
try:
json_data = json.load(f)
if isinstance(json_data, dict):
sha256 = json_data.get('sha256')
if sha256:
existing_files_sha256.append(sha256.upper())
else:
print(f"Invalid JSON data in {json_path}. Expected a dictionary.")
except Exception as e:
print(f"Error decoding JSON in {json_path}: {e}")
installstatus = None
for version in reversed(item['modelVersions']):
for file in version.get('files', []):
file_name = file['name']
file_sha256 = file.get('hashes', {}).get('SHA256', "").upper()
name_match = file_name in existing_files
sha256_match = file_sha256 in existing_files_sha256
if name_match or sha256_match:
if version == item['modelVersions'][0]:
installstatus = "civmodelcardinstalled"
else:
installstatus = "civmodelcardoutdated"
model_card = f'<figure class="civmodelcard {nsfw} {installstatus}" base-model="{baseModel}" date="{date}" onclick="select_model(\'{model_name}\')">' \
+ imgtag \
+ f'<figcaption>{item["name"]}</figcaption></figure>'
if gl.sortNewest:
sorted_models[date].append(model_card)
else:
HTML += model_card
if gl.sortNewest:
for date, cards in sorted(sorted_models.items(), reverse=True):
HTML += f'<div class="date-section"><h4>{date}</h4><hr style="margin-bottom: 5px; margin-top: 5px;">'
HTML += '<div class="card-row">'
for card in cards:
HTML += card
HTML += '</div></div>'
HTML += '</div>'
return HTML
def update_prev_page(content_type, sort_type, period_type, use_search_term, search_term, current_page, base_filter):
return update_next_page(content_type, sort_type, period_type, use_search_term, search_term, current_page, base_filter, isNext=False)
def update_next_page(content_type, sort_type, period_type, use_search_term, search_term, current_page, base_filter, isNext=True):
use_LORA = getattr(opts, "use_LORA", False)
if content_type:
if use_LORA and 'LORA & LoCon' in content_type:
content_type.remove('LORA & LoCon')
if 'LORA' not in content_type:
content_type.append('LORA')
if 'LoCon' not in content_type:
content_type.append('LoCon')
if gl.json_data is None or gl.json_data == "timeout":
timeOut = True
return_values = update_model_list(content_type, sort_type, period_type, use_search_term, search_term, current_page, base_filter, timeOut, isNext)
timeOut = False
return return_values
gl.pageChange = True
current_inputs = (content_type, sort_type, period_type, use_search_term, search_term, gl.tile_count, base_filter)
if gl.previous_inputs and current_inputs != gl.previous_inputs:
gl.inputs_changed = True
else:
gl.inputs_changed = False
gl.previous_inputs = current_inputs
if not gl.file_scan:
if gl.inputs_changed or gl.contentChange:
return_values = update_model_list(content_type, sort_type, period_type, use_search_term, search_term, current_page, base_filter)
return return_values
if isNext:
if gl.json_data['metadata']['nextPage'] is not None:
gl.json_data = request_civit_api(gl.json_data['metadata']['nextPage'])
else:
gl.json_data = None
else:
if gl.json_data['metadata']['prevPage'] is not None:
gl.json_data = request_civit_api(gl.json_data['metadata']['prevPage'])
else:
gl.json_data = None
else:
highest_number = max(gl.url_list_with_numbers.keys())
if isNext:
if gl.json_data['metadata']['nextPage'] is not None:
currentPage = int(gl.json_data['metadata']['currentPage'])
nextPage = currentPage + 2
prevPage = currentPage
pageCount = currentPage + 1
gl.json_data = request_civit_api(gl.json_data['metadata']['nextPage'])
gl.json_data["metadata"]["totalPages"] = highest_number
if not nextPage > highest_number:
gl.json_data["metadata"]["nextPage"] = gl.url_list_with_numbers.get(nextPage)
if not prevPage == 0:
gl.json_data["metadata"]["prevPage"] = gl.url_list_with_numbers.get(prevPage)
gl.json_data["metadata"]["currentPage"] = pageCount
else:
gl.json_data = None
else:
if gl.json_data['metadata']['prevPage'] is not None:
currentPage = int(gl.json_data['metadata']['currentPage'])
nextPage = currentPage
prevPage = currentPage - 2
pageCount = currentPage -1
gl.json_data = request_civit_api(gl.json_data['metadata']['prevPage'])
gl.json_data["metadata"]["totalPages"] = highest_number
if not nextPage > highest_number:
gl.json_data["metadata"]["nextPage"] = gl.url_list_with_numbers.get(nextPage)
if not prevPage == 0:
gl.json_data["metadata"]["prevPage"] = gl.url_list_with_numbers.get(prevPage)
gl.json_data["metadata"]["currentPage"] = pageCount
else:
gl.json_data = None
if gl.json_data is None:
return
if gl.json_data == "timeout":
HTML = '<div style="font-size: 24px; text-align: center; margin: 50px !important;">The Civit-API has timed out, please try again.<br>The servers might be too busy or down if the issue persists.</div>'
hasPrev = current_page not in [0, 1]
hasNext = current_page == 1 or hasPrev
model_dict = {}
if gl.json_data != None and gl.json_data != "timeout":
(hasPrev, hasNext, current_page, total_pages) = pagecontrol(gl.json_data)
model_dict = {}
try:
gl.json_data['items']
except TypeError:
return gr.Dropdown.update(choices=[], value=None)
for item in gl.json_data['items']:
model_dict[item['name']] = item['name']
HTML = model_list_html(gl.json_data, model_dict)
page_string = f"Page: {current_page}/{total_pages}"
return (
gr.Dropdown.update(choices=[v for k, v in model_dict.items()], value="", interactive=True), # Model List
gr.Dropdown.update(choices=[], value=""), # Version List
gr.HTML.update(value=HTML), # HTML Tiles
gr.Button.update(interactive=hasPrev), # Prev Page Button
gr.Button.update(interactive=hasNext), # Next Page Button
gr.Slider.update(value=current_page, maximum=total_pages, label=page_string), # Page Count
gr.Button.update(interactive=False), # Save Tags
gr.Button.update(interactive=False), # Save Images
gr.Button.update(interactive=False), # Download Button
gr.Textbox.update(interactive=False, value=None), # Install Path
gr.Dropdown.update(choices=[], value="", interactive=False), # Sub Folder List
gr.Dropdown.update(choices=[], value="", interactive=False), # File List
gr.Button.update(visible=False)
)
def pagecontrol(json_data):
current_page = f"{json_data['metadata']['currentPage']}"
total_pages = f"{json_data['metadata']['totalPages']}"
hasNext = False
hasPrev = False
if 'nextPage' in json_data['metadata']:
hasNext = True
if 'prevPage' in json_data['metadata']:
hasPrev = True
return hasPrev, hasNext, current_page, total_pages
def update_model_list(content_type, sort_type, period_type, use_search_term, search_term, current_page, base_filter, timeOut=None, isNext=None, from_ver=False, from_installed=False):
use_LORA = getattr(opts, "use_LORA", False)
if content_type:
if use_LORA and 'LORA & LoCon' in content_type:
content_type.remove('LORA & LoCon')
if 'LORA' not in content_type:
content_type.append('LORA')
if 'LoCon' not in content_type:
content_type.append('LoCon')
if not from_ver and not from_installed:
gl.ver_json = None
if not gl.pageChange and not gl.file_scan:
current_inputs = (content_type, sort_type, period_type, use_search_term, search_term, gl.tile_count, base_filter)
if gl.previous_inputs and current_inputs != gl.previous_inputs:
gl.inputs_changed = True
else:
gl.inputs_changed = False
gl.previous_inputs = current_inputs
gl.json_data = api_to_data(content_type, sort_type, period_type, use_search_term, current_page, base_filter, search_term, timeOut, isNext)
if gl.json_data == "timeout":
HTML = '<div style="font-size: 24px; text-align: center; margin: 50px !important;">The Civit-API has timed out, please try again.<br>The servers might be too busy or down if the issue persists.</div>'
hasPrev = current_page not in [0, 1]
hasNext = current_page == 1 or hasPrev
model_dict = {}
if gl.json_data is None:
return
if gl.pageChange:
gl.pageChange = False
gl.contentChange = False
if from_installed or from_ver:
gl.json_data = gl.ver_json
if gl.json_data != None and gl.json_data != "timeout":
if not from_ver:
(hasPrev, hasNext, current_page, total_pages) = pagecontrol(gl.json_data)
else:
current_page = 1
total_pages = 1
hasPrev = False
hasNext = False
model_dict = {}
for item in gl.json_data['items']:
model_dict[item['name']] = item['name']
HTML = model_list_html(gl.json_data, model_dict)
else:
current_page = 1
total_pages = 1
page_string = f"Page: {current_page}/{total_pages}"
return (
gr.Dropdown.update(choices=[v for k, v in model_dict.items()], value="", interactive=True), # Model List
gr.Dropdown.update(choices=[], value=""), # Version List
gr.HTML.update(value=HTML), # HTML Tiles
gr.Button.update(interactive=hasPrev), # Prev Page Button
gr.Button.update(interactive=hasNext), # Next Page Button
gr.Slider.update(value=current_page, maximum=total_pages, label=page_string), # Page Count
gr.Button.update(interactive=False), # Save Tags
gr.Button.update(interactive=False), # Save Images
gr.Button.update(interactive=False), # Download Button
gr.Textbox.update(interactive=False, value=None), # Install Path
gr.Dropdown.update(choices=[], value="", interactive=False), # Sub Folder List
gr.Dropdown.update(choices=[], value="", interactive=False), # File List
gr.Button.update(visible=False)
)
def update_model_versions(model_name):
if model_name is not None:
selected_content_type = None
for item in gl.json_data['items']:
if item['name'] == model_name:
selected_content_type = item['type']
desc = item['description']
break
if selected_content_type is None:
print("Model name not found in json_data. (update_model_versions)")
return
versions_dict = defaultdict(list)
installed_versions = []
model_folder = os.path.join(contenttype_folder(selected_content_type, desc))
gl.main_folder = model_folder
for item in gl.json_data['items']:
if item['name'] == model_name:
for version in item['modelVersions']:
versions_dict[version['name']].append(item["name"])
for version_file in version['files']:
file_sha256 = version_file.get('hashes', {}).get('SHA256', "").upper()
version_filename = version_file['name']
for root, _, files in os.walk(model_folder):
for file in files:
if file.endswith('.json'):
try:
json_path = os.path.join(root, file)
with open(json_path, 'r') as f:
json_data = json.load(f)
if isinstance(json_data, dict):
sha256 = json_data.get('sha256', "").upper()
if sha256 == file_sha256:
installed_versions.append(version['name'])
except Exception as e:
print(f"failed to read: \"{file}\": {e}")
if version_filename == file:
installed_versions.append(version['name'])
version_names = list(versions_dict.keys())
display_version_names = [f"{v} [Installed]" if v in installed_versions else v for v in version_names]
default_installed = next((f"{v} [Installed]" for v in installed_versions), None)
default_value = default_installed or next(iter(version_names), None)
return (
gr.Dropdown.update(choices=display_version_names, value=default_value, interactive=True), # Version List
gr.Button.update(visible=True) # Back to top
)
else:
return (
gr.Dropdown.update(choices=[], value=None, interactive=False), # Version List
gr.Button.update(visible=True) # Back to top
)
def update_model_info(model_name=None, model_version=None):
BtnDown = True
BtnDel = False
file_checked = False
if model_version and "[Installed]" in model_version:
model_version = model_version.replace(" [Installed]", "")
if gl.isDownloading:
BtnDown = False
BtnDel = False
file_checked = True
if model_name and model_version:
output_html = ""
output_training = ""
output_basemodel = ""
img_html = ""
model_desc = ""
dl_dict = {}
allow = {}
file_list = []
model_filename = None
file_id_value = None
sha256_value = None
for item in gl.json_data['items']:
if item['name'] == model_name:
content_type = item['type']
desc = item['description']
model_folder = os.path.join(contenttype_folder(content_type, desc))
model_uploader = item['creator']['username']
uploader_avatar = item['creator']['image']
if uploader_avatar is None:
uploader_avatar = ''
else:
uploader_avatar = f'<div class="avatar"><img src={uploader_avatar}></div>'
tags = item['tags']
if item['description']:
model_desc = item['description']
if item['allowNoCredit']:
allow['allowNoCredit'] = item['allowNoCredit']
if item['allowCommercialUse']:
allow['allowCommercialUse'] = item['allowCommercialUse']
if item['allowDerivatives']:
allow['allowDerivatives'] = item['allowDerivatives']
if item['allowDifferentLicense']:
allow['allowDifferentLicense'] = item['allowDifferentLicense']
for model in item['modelVersions']:
if model['name'] == model_version:
if model['trainedWords']:
output_training = ",".join(model['trainedWords'])
output_training = re.sub(r'<[^>]*:[^>]*>', '', output_training)
output_training = re.sub(r', ?', ', ', output_training)
output_training = output_training.strip(', ')
if model['baseModel']:
output_basemodel = model['baseModel']
for file in model['files']:
dl_dict[file['name']] = file['downloadUrl']
if not model_filename:
model_filename = file['name']
file_id_value = file.get('id', 'Unknown')
sha256_value = file['hashes'].get('SHA256', 'Unknown')
size = file['metadata'].get('size', 'Unknown')
format = file['metadata'].get('format', 'Unknown')
fp = file['metadata'].get('fp', 'Unknown')
sizeKB = file.get('sizeKB', 0) * 1024
filesize = _download.convert_size(sizeKB)
unique_file_name = f"{size} {format} {fp} ({filesize})"
file_list.append(unique_file_name)
model_url = model['downloadUrl']
model_main_url = f"https://civitai.com/models/{item['id']}"
img_html = '<div class="sampleimgs"><input type="radio" name="zoomRadio" id="resetZoom" class="zoom-radio" checked>'
first_image = True
for index, pic in enumerate(model['images']):
# Change width value in URL to original image width
image_url = re.sub(r'/width=\d+', f'/width={pic["width"]}', pic["url"])
if pic['type'] == "video":
image_url = image_url.replace("width=", "transcode=true,width=")
if first_image and pic['type'] != "video":
# Set a data attribute on the first image to designate it as preview
preview_attr = f'data-preview-img={image_url}'
first_image = False
else:
preview_attr = ''
nsfw = 'class="model-block"'
if pic['nsfw'] not in ["None", "Soft"]:
nsfw = 'class="civnsfw model-block"'
img_html += f'''
<div {nsfw} style="display:flex;align-items:flex-start;">
<input type="radio" name="zoomRadio" id="zoomRadio{index}" class="zoom-radio">
<label for="zoomRadio{index}" class="zoom-img-container">
'''
# Check if the pic is an image or video
if pic['type'] == "video":
img_html += f'<video {preview_attr} data-sampleimg="true" autoplay loop muted playsinline><source src="{image_url}" type="video/mp4"></video>'
else:
img_html += f'<img {preview_attr} data-sampleimg="true" src="{image_url}">'
img_html += '''
</label>
<label for="resetZoom" class="zoom-overlay"></label>
'''
if pic['meta']:
img_html += '<div style="margin:1em 0em 1em 1em;text-align:left;line-height: 1.5em;"><dl>'
# Define the preferred order of keys
preferred_order = ["prompt", "negativePrompt", "seed", "Size", "Model", "clipSkip", "sampler", "steps", "cfgScale"]
# Loop through the keys in the preferred order and add them to the HTML
for key in preferred_order:
if key in pic['meta']:
value = pic['meta'][key]
img_html += f'<dt>{escape(str(key))}</dt><dd>{escape(str(value))}</dd>'
# Check if there are remaining keys in pic['meta']
remaining_keys = [key for key in pic['meta'] if key not in preferred_order]
# Add the rest
if remaining_keys:
img_html += f"""
<div class="tabs">
<div class="tab">
<input type="checkbox" class="accordionCheckbox" id="chck{index}">
<label class="tab-label" for="chck{index}">More details...</label>
<div class="tab-content">
"""
for key, value in pic['meta'].items():
if key not in preferred_order:
img_html += f'<dt>{escape(str(key))}</dt><dd>{escape(str(value))}</dd>'
img_html = img_html + '</div></div></div>'
img_html += '</dl></div>'
img_html = img_html + '</div>'
img_html = img_html + '</div>'
output_html = f'''
<div class="model-block">
<h2><a href={model_main_url} target="_blank">{escape(str(model_name))}</a></h2>
<h3 class="model-uploader">Uploaded by <a href="https://civitai.com/user/{escape(str(model_uploader))}" target="_blank">{escape(str(model_uploader))}</a>{uploader_avatar}</h3>
<dl>
<dt>Version</dt>
<dd>{escape(str(model_version))}</dd>
<dt>Base Model</dt>
<dd>{escape(str(output_basemodel))}</dd>
<dt>CivitAI Tags</dt>
<dd>{escape(str(tags))}</dd>
<dt>License</dt>
<dd>{escape(str(allow))}</dd>
<dt>Download Link</dt>
<dd><a href={model_url} target="_blank">{model_url}</a></dd>
</dl>
<div class="model-description">
<h2>Description</h2>
{model_desc}
</div>
</div>
<div align=center>{img_html}</div>
'''
default_file = file_list[0] if file_list else None
folder_location = "None"
default_subfolder = "None"
sub_folders = ["None"]
for root, dirs, files in os.walk(model_folder):
for filename in files:
if filename.endswith('.json'):
json_file_path = os.path.join(root, filename)
with open(json_file_path, 'r') as f:
try:
data = json.load(f)
sha256 = data.get('sha256')
if sha256:
sha256 = sha256.upper()
if sha256 == sha256_value:
folder_location = root
BtnDown = False
BtnDel = True
break
except Exception as e:
print(f"Error decoding JSON: {str(e)}")
else:
for filename in files:
if filename == model_filename:
folder_location = root
BtnDown = False
BtnDel = True
break
if folder_location != "None":
break
for root, dirs, _ in os.walk(model_folder):
for d in dirs:
sub_folder = os.path.relpath(os.path.join(root, d), model_folder)
if sub_folder:
sub_folders.append(f'{os.sep}{sub_folder}')
if folder_location == "None":
folder_location = model_folder
relative_path = os.path.relpath(folder_location, model_folder)
default_subfolder = f'{os.sep}{relative_path}' if relative_path != "." else "None"
return (
gr.HTML.update(value=output_html), # Model Preview
gr.Textbox.update(value=output_training, interactive=True), # Trained Tags
gr.Textbox.update(value=output_basemodel), # Base Model Number
gr.Button.update(visible=BtnDown, interactive=BtnDown), # Download Button
gr.Button.update(visible=BtnDel, interactive=BtnDel), # Delete Button
gr.Dropdown.update(choices=file_list, value=default_file, interactive=True), # File List
gr.Textbox.update(value=model_filename), # Model File Name
gr.Textbox.update(value=file_id_value), # Model ID
gr.Textbox.update(value=sha256_value), # SHA256
gr.Textbox.update(interactive=True, value=folder_location if model_name else None), # Install Path
gr.Dropdown.update(choices=sub_folders, value=default_subfolder, interactive=True) # Sub Folder List
)
else:
return (
gr.HTML.update(value=None), # Model Preview
gr.Textbox.update(value=None, interactive=False), # Trained Tags
gr.Textbox.update(value=''), # Base Model Number
gr.Button.update(visible=BtnDown), # Download Button
gr.Button.update(visible=BtnDel, interactive=BtnDel), # Delete Button
gr.Dropdown.update(choices=None, value=None, interactive=False), # File List
gr.Textbox.update(value=None), # Model File Name
gr.Textbox.update(value=None), # Model ID
gr.Textbox.update(value=None), # SHA256
gr.Textbox.update(interactive=False, value=None), # Install Path
gr.Dropdown.update(choices=None, value=None, interactive=False) # Sub Folder List
)
def update_file_info(model_name, model_version, file_metadata):
if model_version and "[Installed]" in model_version:
model_version = model_version.replace(" [Installed]", "")
if model_name and model_version:
for item in gl.json_data['items']:
if item['name'] == model_name:
content_type = item['type']
desc = item['description']
for model in item['modelVersions']:
if model['name'] == model_version:
for file in model['files']:
file_id = file.get('id', 'Unknown')
file_name = file.get('name', 'Unknown')
sha256 = file['hashes'].get('SHA256', 'Unknown')
metadata = file.get('metadata', {})
file_size = metadata.get('size', 'Unknown')
file_format = metadata.get('format', 'Unknown')
file_fp = metadata.get('fp', 'Unknown')
sizeKB = file.get('sizeKB', 0) * 1024
filesize = _download.convert_size(sizeKB)
if f"{file_size} {file_format} {file_fp} ({filesize})" == file_metadata:
installed = False
folder_location = "None"
model_folder = os.path.join(contenttype_folder(content_type, desc))
for root, _, files in os.walk(model_folder):
if file_name in files:
installed = True
folder_location = root
break
if not installed:
for root, _, files in os.walk(model_folder):
for filename in files:
if filename.endswith('.json'):
with open(os.path.join(root, filename), 'r') as f:
try:
data = json.load(f)
if data.get('sha256').upper() == sha256:
folder_location = root
installed = True
break
except Exception as e:
print(f"Error decoding JSON: {str(e)}")
if folder_location == "None":
folder_location = model_folder
relative_path = os.path.relpath(folder_location, model_folder)
default_subfolder = f'{os.sep}{relative_path}' if relative_path != "." else "None"
return (
gr.Textbox.update(value=file['name']), # Update model_filename Textbox
gr.Textbox.update(value=file_id), # Update ID Textbox
gr.Textbox.update(value=sha256), # sha256 textbox
gr.Button.update(interactive=False if installed else True, visible=False if installed else True), # Download Button
gr.Button.update(interactive=True if installed else False, visible=True if installed else False), # Delete Button
gr.Textbox.update(interactive=True, value=folder_location if model_name else None), # Install Path
gr.Dropdown.update(value=default_subfolder, interactive=True) # Sub Folder List
)
return (
gr.Textbox.update(value=None), # Update model_filename Textbox
gr.Textbox.update(value=None), # Update ID Textbox
gr.Textbox.update(value=None), # sha256 textbox
gr.Button.update(interactive=False, visible=True), # Download Button
gr.Button.update(interactive=False, visible=False), # Delete Button
gr.Textbox.update(interactive=False, value=None), # Install Path
gr.Dropdown.update(choices=None, value=None, interactive=False) # Sub Folder List
)
def request_civit_api(api_url=None):
try:
response = requests.get(api_url, timeout=(10,30))
response.raise_for_status()
except Exception as e:
print(f"Error: {e}")
return "timeout"
else:
response.encoding = "utf-8"
data = json.loads(response.text)
return data