Update to v3.4.2

pull/245/head
BlafKing 2024-03-11 01:05:36 +01:00
parent bcf6c1872b
commit 2cfa149e3c
No known key found for this signature in database
GPG Key ID: BFD6B0BCA6280F8B
10 changed files with 484 additions and 249 deletions

View File

@ -100,12 +100,22 @@ https://github.com/BlafKing/sd-civitai-browser-plus/assets/9644716/44c5c7a0-4854
# Changelog 📋
<h3>v3.4.2</h3>
* Feature: Ability to set-up a custom proxy for API requests and downloads.
* Feature: Use image API for prompt info, should speed up loading.
* Feature: Optimized javascript code, improved webpage speed for some users.
* New setting: Proxy settings to set-up custom proxy.
* New setting: Toggle for saving description to model json file. (this displays description on the cards)
* Bug fix: Broken default sub folder option fixed [#217](https://github.com/BlafKing/sd-civitai-browser-plus/issues/217)
---
<h3>v3.4.1</h3>
* Bug fix: Fixed prompt info and model selection after CivitAI API update.
* Bug fix: Fixed "/" missing from default path/sub-folder.
* Feature: Local images now work in HTML files as preview. (credit: [mx](https://github.com/mx))
* Feature: Updated available base models.
* Bug fix: Fixed prompt info and model selection after CivitAI API update.
* Bug fix: Fixed "/" missing from default path/sub-folder.
---
<h3>v3.4.0</h3>

View File

@ -16,4 +16,5 @@ install_req("send2trash")
install_req("zip_unicode", "ZipUnicode")
install_req("bs4", "beautifulsoup4")
install_req("fake_useragent")
install_req("packaging")
install_req("packaging")
install_req("pysocks")

View File

@ -102,7 +102,7 @@ function updateCard(modelNameWithSuffix) {
}
// Enables refresh with alt+enter and ctrl+enter
document.addEventListener('keydown', function(e) {
function keydownHandler(e) {
var handled = false;
if (e.key !== undefined) {
@ -126,7 +126,8 @@ document.addEventListener('keydown', function(e) {
e.preventDefault();
}
}
});
}
document.addEventListener('keydown', keydownHandler);
// Function for the back to top button
function BackToTop() {
@ -204,14 +205,21 @@ function pressRefresh() {
setTimeout(() => {
const input = document.querySelector("#pageSlider > div:nth-child(2) > div > input");
if (document.activeElement === input) {
input.addEventListener('keydown', function(event) {
function keydownHandler(event) {
if (event.key === 'Enter' || event.keyCode === 13) {
input.blur();
input.removeEventListener('keydown', keydownHandler);
input.removeEventListener('blur', blurHandler);
}
});
input.addEventListener('blur', function() {
return;
});
}
function blurHandler() {
input.removeEventListener('keydown', keydownHandler);
input.removeEventListener('blur', blurHandler);
}
input.addEventListener('keydown', keydownHandler);
input.addEventListener('blur', blurHandler);
return;
}
@ -328,24 +336,6 @@ function updateBackToTopVisibility(entries) {
}
}
// Options for the Intersection Observer
var options = {
root: null,
rootMargin: '0px 0px -60px 0px',
threshold: 0
};
// Create an Intersection Observer instance
const observer = new IntersectionObserver(updateBackToTopVisibility, options);
function handleCivitaiDivChanges() {
var civitaiDiv = document.getElementById('civitai_preview_html');
observer.unobserve(civitaiDiv);
observer.observe(civitaiDiv);
}
document.addEventListener("scroll", handleCivitaiDivChanges)
// Create the accordion dropdown inside the settings tab
function createAccordion(containerDiv, subfolders, name) {
if (containerDiv == null || subfolders.length == 0) {
@ -371,14 +361,19 @@ function createAccordion(containerDiv, subfolders, name) {
}
// Adds a button to the cards in txt2img and img2img
function createCardButtons(event) {
const clickedElement = event.target;
function createCivitAICardButtons(clickedElement=null) {
addOnClickToButtons();
const validButtonNames = ['Textual Inversion', 'Hypernetworks', 'Checkpoints', 'Lora'];
const validParentIds = ['txt2img_textual_inversion_cards_html', 'txt2img_hypernetworks_cards_html', 'txt2img_checkpoints_cards_html', 'txt2img_lora_cards_html'];
const hasMatchingButtonName = clickedElement && clickedElement.innerText && validButtonNames.some(buttonName =>
clickedElement.innerText.trim() === buttonName
);
let hasMatchingButtonName = null;
if (clickedElement) {
hasMatchingButtonName = clickedElement && clickedElement.innerText && validButtonNames.some(buttonName =>
clickedElement.innerText.trim() === buttonName
);
} else {
hasMatchingButtonName = true;
}
const flexboxDivs = document.querySelectorAll('.layoutkit-flexbox');
let isLobeTheme = false;
@ -429,10 +424,6 @@ function createCardButtons(event) {
const newDiv = document.createElement('div');
newDiv.classList.add('goto-civitbrowser', 'card-button');
newDiv.addEventListener('click', function (event) {
event.stopPropagation();
modelInfoPopUp(modelName, content_type);
});
const svgIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
if (isLobeTheme) {
@ -451,7 +442,10 @@ function createCardButtons(event) {
svgIcon.setAttribute('viewBox', `75 ${viewBoxHeight} 500 500`);
svgIcon.setAttribute('fill', 'white');
svgIcon.setAttribute('style', `scale: ${cardScale}%;`);
newDiv.onclick = function() {
modelInfoPopUp(modelName, content_type);
};
svgIcon.innerHTML = `
<path d="M 352.79 218.85 L 319.617 162.309 L 203.704 162.479 L 146.28 259.066 L 203.434 355.786 L 319.373 355.729 L 352.773 299.386 L 411.969 299.471 L 348.861 404.911 L 174.065 404.978 L 87.368 259.217 L 174.013 113.246 L 349.147 113.19 L 411.852 218.782 L 352.79 218.85 Z"/>
<path d="M 304.771 334.364 L 213.9 334.429 L 169.607 259.146 L 214.095 183.864 L 305.132 183.907 L 330.489 227.825 L 311.786 259.115 L 330.315 290.655 Z M 278.045 290.682 L 259.294 259.18 L 278.106 227.488 L 240.603 227.366 L 221.983 259.128 L 240.451 291.026 Z"/>
@ -461,10 +455,45 @@ function createCardButtons(event) {
buttonRow.insertBefore(newDiv, buttonRow.firstChild);
});
}
}, 100);
}, 200);
setTimeout(() => {
clearInterval(checkForCardDivs);
}, 5000);
}
}
document.addEventListener('click', createCardButtons);
function addOnClickToButtons() {
const img2img_extra_tabs = document.getElementById('img2img_extra_tabs');
const txt2img_extra_tabs = document.getElementById('txt2img_extra_tabs');
const txt2img_refresh_btn = document.getElementById('txt2img_checkpoints_extra_refresh');
const img2img_refresh_btn = document.getElementById('img2img_checkpoints_extra_refresh');
txt2img_refresh_btn.onclick = function() {
createCivitAICardButtons(this);
};
img2img_refresh_btn.onclick = function() {
createCivitAICardButtons(this);
};
function addButtonClickEvent(div) {
var firstChildDiv = div.querySelector('div');
if (firstChildDiv) {
var buttons = firstChildDiv.querySelectorAll('button');
buttons.forEach(function(button, index) {
if (index !== 0) {
button.onclick = function() {
createCivitAICardButtons(this);
};
}
});
}
}
addButtonClickEvent(img2img_extra_tabs);
addButtonClickEvent(txt2img_extra_tabs);
}
function modelInfoPopUp(modelName, content_type) {
select_model(modelName, null, true, content_type);
@ -480,6 +509,7 @@ function modelInfoPopUp(modelName, content_type) {
overlay.style.backgroundColor = 'rgba(20, 20, 20, 0.95)';
overlay.style.zIndex = '1001';
overlay.style.overflowY = 'auto';
overlay.addEventListener('keydown', handleKeyPress);
// Create the close button
var closeButton = document.createElement('div');
@ -493,7 +523,6 @@ function modelInfoPopUp(modelName, content_type) {
closeButton.style.color = 'white';
closeButton.style.fontSize = '32pt';
closeButton.addEventListener('click', hidePopup);
document.addEventListener('keydown', handleKeyPress);
// Create the pop-up window
var inner = document.createElement('div');
@ -503,7 +532,7 @@ function modelInfoPopUp(modelName, content_type) {
inner.style.left = '50%';
inner.style.width = 'auto';
inner.style.transform = 'translate(-50%, -50%)';
inner.style.background = 'var(--body-background-fill)';
inner.style.background = 'var(--neutral-950)';
inner.style.padding = '2em';
inner.style.borderRadius = 'var(--block-radius)';
inner.style.borderStyle = 'solid';
@ -587,6 +616,8 @@ function inputHTMLPreviewContent(html_input) {
}
modelInfo.innerHTML = extractedText;
inner.appendChild(modelInfo);
setDescriptionToggle();
}
}
}
@ -695,64 +726,6 @@ function multi_model_select(modelName, modelType, isChecked) {
updateInput(selected_type_list);
}
// Metadata button click detector
document.addEventListener('click', function(event) {
var target = event.target;
if (target.classList.contains('edit-button') && target.classList.contains('card-button')) {
var parentDiv = target.parentElement;
var actionsDiv = parentDiv.nextElementSibling;
if (actionsDiv && actionsDiv.classList.contains('actions')) {
var nameSpan = actionsDiv.querySelector('.name');
if (nameSpan) {
var nameValue = nameSpan.textContent;
onEditButtonCardClick(nameValue);
}
}
}
}, true);
// CivitAI Link Button Creation
function onEditButtonCardClick(nameValue) {
var checkInterval = setInterval(function() {
var globalPopupInner = document.querySelector('.global-popup-inner');
var titleElement = globalPopupInner.querySelector('.extra-network-name');
if (titleElement.textContent.trim() === nameValue.trim()) {
var descriptionSpan = Array.from(globalPopupInner.querySelectorAll('span')).find(span => span.textContent.trim() === "Description");
if (descriptionSpan) {
var descriptionTextarea = descriptionSpan.nextElementSibling;
if (descriptionTextarea.value.startsWith('Model URL:')) {
var matches = descriptionTextarea.value.match(/"([^"]+)"/);
if (matches && matches[1]) {
var modelUrl = matches[1];
var grandParentDiv = descriptionTextarea.parentElement.parentElement.parentElement.parentElement;
var imageDiv = grandParentDiv.nextElementSibling
var openInCivitaiDiv = document.querySelector('.open-in-civitai');
if (!openInCivitaiDiv) {
openInCivitaiDiv = document.createElement('div');
openInCivitaiDiv.classList.add('open-in-civitai');
imageDiv.appendChild(openInCivitaiDiv);
}
openInCivitaiDiv.innerHTML = '<a href="' + modelUrl + '" target="_blank" onclick="window.open(this.href, \'_blank\'); return false;">Open on CivitAI</a>';
}
else {
var openInCivitaiDiv = document.querySelector('.open-in-civitai');
if (openInCivitaiDiv) {
openInCivitaiDiv.remove();
}
}
} else {
var openInCivitaiDiv = document.querySelector('.open-in-civitai');
if (openInCivitaiDiv) {
openInCivitaiDiv.remove();
}
}
}
clearInterval(checkInterval);
}
}, 100);
}
function sendClick(location) {
const clickEvent = new MouseEvent('click', {
view: window,
@ -961,59 +934,82 @@ function hideInstalled(toggleValue) {
});
}
function setDescriptionToggle() {
const popUp = document.querySelector(".civitai-overlay-inner");
let toggleButton = null;
let descriptionDiv = null;
if (popUp) {
descriptionDiv = popUp.querySelector(".model-description");
toggleButton = popUp.querySelector(".description-toggle-label");
} else {
descriptionDiv = document.querySelector(".model-description");
toggleButton = document.querySelector(".description-toggle-label");
}
if (descriptionDiv && descriptionDiv.scrollHeight <= 400) {
toggleButton.style.visibility = "hidden";
toggleButton.style.height = "0";
descriptionDiv.style.position = "unset";
}
}
// Runs all functions when the page is fully loaded
function onPageLoad() {
const divElement = document.getElementById('setting_custom_api_key');
let civitaiDiv = document.getElementById('civitai_preview_html');
let queue_list = document.querySelector("#queue_list");
const infoElement = divElement?.querySelector('.info');
if (!infoElement) {
return;
}
clearInterval(intervalID);
updateSVGIcons();
let subfolderDiv = document.querySelector("#settings_civitai_browser_plus > div > div");
let downloadDiv = document.querySelector("#settings_civitai_browser_download > div > div");
let upscalerDiv = document.querySelector("#settings_civitai_browser_plus > div > div > #settings-accordion > div");
let downloadDivSub = document.querySelector("#settings_civitai_browser_download > div > div > #settings-accordion > div");
let settingsDiv = document.querySelector("#settings_civitai_browser > div > div");
if (subfolderDiv || downloadDiv) {
let div = subfolderDiv || downloadDiv;
let subfolders = div.querySelectorAll("[id$='subfolder']");
createAccordion(div, subfolders, "Default sub folders");
}
let upscalerDiv = document.querySelector("#settings_civitai_browser_plus > div > div > #settings-accordion > div");
let downloadDivSub = document.querySelector("#settings_civitai_browser_download > div > div > #settings-accordion > div");
if (upscalerDiv || downloadDivSub) {
let div = upscalerDiv || downloadDivSub;
let upscalers = div.querySelectorAll("[id$='upscale_subfolder']");
createAccordion(div, upscalers, "Upscalers");
}
let settingsDiv = document.querySelector("#settings_civitai_browser > div > div");
if (subfolderDiv || settingsDiv) {
let div = subfolderDiv || settingsDiv;
let subfolders = div.querySelectorAll("[id^='setting_insert_sub']");
createAccordion(div, subfolders, "Insert sub folder options");
let proxy = div.querySelectorAll("[id$='proxy']");
createAccordion(div, proxy, "Proxy options");
}
let toggle4L = document.getElementById('toggle4L');
let toggle4 = document.getElementById('toggle4');
let hash_toggle_hover = document.querySelector('#skip_hash_toggle > label');
let hash_toggle = document.querySelector('#skip_hash_toggle');
if (toggle4L || toggle4) {
let like_toggle = toggle4L || toggle4;
let insertText = 'Requires an API Key\nConfigurable in CivitAI settings tab';
createTooltip(like_toggle, like_toggle, insertText);
}
let hash_toggle_hover = document.querySelector('#skip_hash_toggle > label');
let hash_toggle = document.querySelector('#skip_hash_toggle');
if (hash_toggle) {
let insertText = 'This option generates unique hashes for models that were not downloaded with this extension.\nA hash is required for any of the options below to work, a model with no hash will be skipped.\nInitial hash generation is a one-time process per file.';
createTooltip(hash_toggle, hash_toggle_hover, insertText);
}
observer.observe(civitaiDiv);
addOnClickToButtons();
createCivitAICardButtons();
adjustFilterBoxAndButtons();
setupClickOutsideListener();
createLink(infoElement);

View File

@ -8,15 +8,10 @@ import os
import re
import datetime
import platform
import time
from PIL import Image
from io import BytesIO
from collections import defaultdict
from modules.images import read_info_from_image
try:
from modules.generation_parameters_copypaste import parse_generation_parameters
except:
from modules.infotext_utils import parse_generation_parameters
from modules.shared import cmd_opts, opts
from modules.paths import models_path, extensions_dir, data_path
@ -372,7 +367,7 @@ def update_next_page(content_type, sort_type, period_type, use_search_term, sear
if 'LoCon' not in content_type:
content_type.append('LoCon')
if gl.json_data is None or gl.json_data == "timeout":
if gl.json_data is None or gl.json_data == "timeout" or gl.json_data == "error":
timeOut = True
return_values = update_model_list(content_type, sort_type, period_type, use_search_term, search_term, current_page, base_filter, only_liked, nsfw, timeOut=timeOut, isNext=isNext)
timeOut = False
@ -443,8 +438,14 @@ def update_next_page(content_type, sort_type, period_type, use_search_term, sear
hasPrev = current_page not in [0, 1]
hasNext = current_page == 1 or hasPrev
model_dict = {}
if gl.json_data == "error":
HTML = '<div style="font-size: 24px; text-align: center; margin: 50px !important;">The Civit-API has failed to return due to an error.<br>Check the logs for more details.</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":
if gl.json_data != None and gl.json_data != "timeout" and gl.json_data != "error":
(hasPrev, hasNext, current_page, total_pages) = pagecontrol(gl.json_data)
model_dict = {}
try:
@ -517,13 +518,19 @@ def update_model_list(content_type=None, sort_type=None, period_type=None, use_s
hasPrev = current_page not in [0, 1]
hasNext = current_page == 1 or hasPrev
if gl.json_data == "error":
HTML = '<div style="font-size: 24px; text-align: center; margin: 50px !important;">The Civit-API has failed to return due to an error.<br>Check the logs for more details.</div>'
hasPrev = current_page not in [0, 1]
hasNext = current_page == 1 or hasPrev
model_dict = {}
if gl.json_data is None:
return
if from_installed or from_ver:
gl.json_data = gl.ver_json
if gl.json_data != None and gl.json_data != "timeout":
if gl.json_data != None and gl.json_data != "timeout" and gl.json_data != "error":
if not from_ver:
(hasPrev, hasNext, current_page, total_pages) = pagecontrol(gl.json_data)
else:
@ -629,10 +636,11 @@ def cleaned_name(file_name):
return f"{clean_name}{extension}"
def fetch_and_process_image(image_url):
proxies, ssl = get_proxies()
try:
parsed_url = urllib.parse.urlparse(image_url)
if parsed_url.scheme and parsed_url.netloc:
response = requests.get(image_url)
response = requests.get(image_url, proxies=proxies, verify=ssl)
if response.status_code == 200:
image = Image.open(BytesIO(response.content))
geninfo, _ = read_info_from_image(image)
@ -644,34 +652,6 @@ def fetch_and_process_image(image_url):
except:
return None
def image_url_to_promptInfo(image_url):
response = requests.get(image_url)
if response.status_code == 200:
image = Image.open(BytesIO(response.content))
prompt, _ = read_info_from_image(image)
if prompt:
prompt_dict = parse_generation_parameters(prompt)
invalid_values = [None, 0, "", "Use same sampler", "Use same checkpoint"]
keys_to_remove = [key for key, value in prompt_dict.items() if key != "Clip skip" and value in invalid_values]
for key in keys_to_remove:
prompt_dict.pop(key, None)
if "Size-1" in prompt_dict and "Size-2" in prompt_dict:
prompt_dict["Size"] = f'{prompt_dict["Size-1"]}x{prompt_dict["Size-2"]}'
prompt_dict.pop("Size-1", None)
prompt_dict.pop("Size-2", None)
if "Hires resize-1" in prompt_dict and "Hires resize-2" in prompt_dict:
prompt_dict["Hires resize"] = f'{prompt_dict["Hires resize-1"]}x{prompt_dict["Hires resize-2"]}'
prompt_dict.pop("Hires resize-1", None)
prompt_dict.pop("Hires resize-2", None)
return prompt_dict
else:
return []
return []
def extract_model_info(input_string):
last_open_parenthesis = input_string.rfind("(")
last_close_parenthesis = input_string.rfind(")")
@ -797,19 +777,31 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in
model_url = selected_version.get('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>'
url = f"https://civitai.com/api/v1/images?modelId={item['id']}&modelVersionId={selected_version['id']}&username={model_uploader}"
model_images = request_civit_api(url)
for index, pic in enumerate(selected_version['images']):
if from_preview:
index = f"preview_{index}"
for item in model_images['items']:
if item['id'] == pic['id']:
current_image = item
# 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=")
prompt_dict = []
else:
prompt_dict = image_url_to_promptInfo(image_url)
prompt_dict = current_image['meta']
nsfw = 'class="model-block"'
meta_button = False
if prompt_dict and prompt_dict.get('Prompt'):
if prompt_dict and prompt_dict.get('prompt'):
meta_button = True
BtnImage = True
@ -846,16 +838,29 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in
if prompt_dict:
img_html += '<div style="margin:1em 0em 1em 1em;text-align:left;line-height:1.5em;" id="image_info"><dl style="gap:10px; display:grid;">'
# Define the preferred order of keys and convert them to lowercase
preferred_order = ["Prompt", "Negative prompt", "Seed", "Size", "Model", "Clip skip", "Sampler", "Steps", "CFG scale"]
# Define the preferred order of keys
preferred_order = ["prompt", "negativePrompt", "seed", "Size", "Model", "Clip skip", "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 prompt_dict:
value = prompt_dict[key]
key_map = {
"prompt": "Prompt",
"negativePrompt": "Negative prompt",
"seed": "Seed",
"Size": "Size",
"Model": "Model",
"Clip skip": "Clip skip",
"sampler": "Sampler",
"steps": "Steps",
"cfgScale": "CFG scale"
}
key = key_map.get(key, key)
if meta_btn:
img_html += f'<div class="civitai-meta-btn" onclick="metaToTxt2Img(\'{escape(str(key))}\', this)"><dt>{escape(str(key).capitalize())}</dt><dd>{escape(str(value))}</dd></div>'
img_html += f'<div class="civitai-meta-btn" onclick="metaToTxt2Img(\'{escape(str(key))}\', this)"><dt>{escape(str(key))}</dt><dd>{escape(str(value))}</dd></div>'
else:
img_html += f'<div class="civitai-meta"><dt>{escape(str(key).capitalize())}</dt><dd>{escape(str(value))}</dd></div>'
img_html += f'<div class="civitai-meta"><dt>{escape(str(key))}</dt><dd>{escape(str(value))}</dd></div>'
# Check if there are remaining keys in meta
remaining_keys = [key for key in prompt_dict if key not in preferred_order]
@ -917,10 +922,12 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in
</div>
</div>
</div>
<div class="model-description" style="overflow-wrap: break-word;">
<input type="checkbox" id="{'preview-' if from_preview else ''}civitai-description" class="description-toggle-checkbox">
<div class="model-description">
<h2>Description</h2>
{model_desc}
</div>
<label for="{'preview-' if from_preview else ''}civitai-description" class="description-toggle-label"></label>
</div>
<div align=center>{img_html}</div>
'''
@ -991,7 +998,7 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in
sub_folders.remove("None")
sub_folders = sorted(sub_folders, key=lambda x: (x.lower(), x))
sub_folders.insert(0, "None")
base = cleaned_name(model_uploader)
base = cleaned_name(output_basemodel)
author = cleaned_name(model_uploader)
name = cleaned_name(model_name)
ver = cleaned_name(model_version)
@ -1226,37 +1233,54 @@ def update_file_info(model_string, model_version, file_metadata):
gr.Dropdown.update(choices=None, value=None, interactive=False) # Sub Folder List
)
def get_headers():
def get_proxies():
custom_proxy = getattr(opts, "custom_civitai_proxy", "")
disable_ssl = getattr(opts, "disable_sll_proxy", False)
cabundle_path = getattr(opts, "cabundle_path_proxy", "")
ssl = True
proxies = {}
if custom_proxy:
if not disable_ssl:
if cabundle_path:
ssl = os.path(cabundle_path)
else:
ssl = False
proxies = {
'http': custom_proxy,
'https': custom_proxy,
}
return proxies, ssl
def get_headers(referer=None, no_api=None):
api_key = getattr(opts, "custom_api_key", "")
try:
user_agent = UserAgent().chrome
except ImportError:
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
headers = {
'User-Agent': user_agent,
'Sec-Ch-Ua': '"Brave";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Sec-Gpc': '1',
'Upgrade-Insecure-Requests': '1',
"Connection": "keep-alive",
"Sec-Ch-Ua-Platform": "Windows",
"User-Agent": user_agent,
"Content-Type": "application/json"
}
if api_key:
if referer:
headers['Referer'] = f"https://civitai.com/models/{referer}"
if api_key and not no_api:
headers['Authorization'] = f'Bearer {api_key}'
return headers
def request_civit_api(api_url=None):
headers = get_headers()
proxies, ssl = get_proxies()
try:
response = requests.get(api_url, headers=headers, timeout=(10, 30))
response = requests.get(api_url, headers=headers, timeout=(60,30), proxies=proxies, verify=ssl)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return "timeout"
return "error"
else:
response.encoding = "utf-8"
try:

View File

@ -68,7 +68,10 @@ def start_aria2_rpc():
try:
show_log = getattr(opts, "show_log", False)
aria2_flags = getattr(opts, "aria2_flags", "")
cmd = f'"{aria2}" --enable-rpc --rpc-listen-all --rpc-listen-port=24000 --rpc-secret {rpc_secret} --check-certificate=false --ca-certificate=" " --file-allocation=none {aria2_flags}'
custom_proxy = getattr(opts, "custom_proxy", "")
if custom_proxy:
custom_proxy = f"--all-proxy={custom_proxy} "
cmd = f'"{aria2}" --enable-rpc --rpc-listen-all --rpc-listen-port=24000 --rpc-secret {rpc_secret} --check-certificate=false --ca-certificate=" " {custom_proxy}--file-allocation=none {aria2_flags}'
subprocess_args = {'shell': True}
if not show_log:
subprocess_args.update({'stdout': subprocess.DEVNULL, 'stderr': subprocess.DEVNULL})
@ -323,10 +326,11 @@ def convert_size(size):
size /= 1024
return f"{size:.2f} GB"
def get_download_link(url):
headers = _api.get_headers()
response = requests.get(url, headers=headers, allow_redirects=False)
def get_download_link(url, model_id):
headers = _api.get_headers(model_id)
proxies, ssl = _api.get_proxies()
response = requests.get(url, headers=headers, allow_redirects=False, proxies=proxies, verify=ssl)
if 300 <= response.status_code <= 308:
if "login?returnUrl" in response.text and "reason=download-auth" in response.text:
@ -337,7 +341,7 @@ def get_download_link(url):
else:
return None
def download_file(url, file_path, install_path, progress=gr.Progress() if queue else None):
def download_file(url, file_path, install_path, model_id, progress=gr.Progress() if queue else None):
try:
disable_dns = getattr(opts, "disable_dns", False)
split_aria2 = getattr(opts, "split_aria2", 64)
@ -347,7 +351,7 @@ def download_file(url, file_path, install_path, progress=gr.Progress() if queue
file_name = os.path.basename(file_path)
download_link = get_download_link(url)
download_link = get_download_link(url, model_id)
if not download_link:
print(f'File: "{file_name}" not found on CivitAI servers, it looks like the file is not available for download.')
gl.download_fail = True
@ -485,7 +489,7 @@ def info_to_json(install_path, model_id, model_sha256, unpackList=None):
with open(json_file, 'w', encoding="utf-8") as f:
json.dump(data, f, indent=4)
def download_file_old(url, file_path, progress=gr.Progress() if queue else None):
def download_file_old(url, file_path, model_id, progress=gr.Progress() if queue else None):
try:
gl.download_fail = False
max_retries = 5
@ -499,7 +503,7 @@ def download_file_old(url, file_path, progress=gr.Progress() if queue else None)
last_update_time = 0
update_interval = 0.25
download_link = get_download_link(url)
download_link = get_download_link(url, model_id)
if not download_link:
print(f'File: "{file_name_display}" not found on CivitAI servers, it looks like the file is not available for download.')
if progress != None:
@ -516,6 +520,9 @@ def download_file_old(url, file_path, progress=gr.Progress() if queue else None)
time.sleep(5)
return
headers = _api.get_headers(model_id, True)
proxies, ssl = _api.get_proxies()
while True:
if gl.cancel_status:
if progress != None:
@ -523,9 +530,8 @@ def download_file_old(url, file_path, progress=gr.Progress() if queue else None)
return
if os.path.exists(file_path):
downloaded_size = os.path.getsize(file_path)
headers = {"Range": f"bytes={downloaded_size}-"}
else:
headers = {}
headers['Range'] = f"bytes={downloaded_size}-"
with open(file_path, "ab") as f:
while gl.isDownloading:
try:
@ -538,7 +544,7 @@ def download_file_old(url, file_path, progress=gr.Progress() if queue else None)
if progress != None:
progress(0, desc=f"Download cancelled.")
return
response = requests.get(download_link, headers=headers, stream=True, timeout=4)
response = requests.get(download_link, headers=headers, stream=True, timeout=10, proxies=proxies, verify=ssl)
if response.status_code == 404:
if progress != None:
progress(0, desc=f"Encountered an error during download of: {file_name_display}, file is not found on CivitAI servers.")
@ -645,9 +651,9 @@ def download_create_thread(download_finish, queue_trigger, progress=gr.Progress(
path_to_new_file = os.path.join(item['install_path'], item['model_filename'])
if use_aria2 and os_type != 'Darwin':
thread = threading.Thread(target=download_file, args=(item['dl_url'], path_to_new_file, item['install_path'], progress))
thread = threading.Thread(target=download_file, args=(item['dl_url'], path_to_new_file, item['install_path'], item['model_id'], progress))
else:
thread = threading.Thread(target=download_file_old, args=(item['dl_url'], path_to_new_file, progress))
thread = threading.Thread(target=download_file_old, args=(item['dl_url'], path_to_new_file, item['model_id'], progress))
thread.start()
thread.join()

View File

@ -161,6 +161,7 @@ def delete_associated_files(directory, base_name):
print(f"Associated file deleted: {path_to_delete}")
def save_preview(file_path, api_response, overwrite_toggle=False, sha256=None):
proxies, ssl = _api.get_proxies()
json_file = os.path.splitext(file_path)[0] + ".json"
install_path, file_name = os.path.split(file_path)
name = os.path.splitext(file_name)[0]
@ -191,7 +192,7 @@ def save_preview(file_path, api_response, overwrite_toggle=False, sha256=None):
if image["type"] == "image":
url_with_width = re.sub(r'/width=\d+', f'/width={image["width"]}', image["url"])
response = requests.get(url_with_width)
response = requests.get(url_with_width, proxies=proxies, verify=ssl)
if response.status_code == 200:
with open(image_path, 'wb') as img_file:
img_file.write(response.content)
@ -373,6 +374,7 @@ def model_from_sent(model_name, content_type, tile_count):
not_found = div + "Model ID not found.<br>Maybe the model doesn\'t exist on CivitAI?</div>"
path_not_found = div + "Model ID not found.<br>Could not locate the model path.</div>"
offline = div + "CivitAI failed to respond.<br>The servers are likely offline."
error = div + "CivitAI failed to respond due to an error.<br>Check the logs for more details."
model_name = re.sub(r'\.\d{3}$', '', model_name)
content_type = re.sub(r'\.\d{3}$', '', content_type)
@ -426,22 +428,24 @@ def model_from_sent(model_name, content_type, tile_count):
if json_data == "timeout":
output_html = offline
if json_data != None and json_data != "timeout":
if json_data == "error":
output_html = error
if json_data != None and json_data != "timeout" and json_data != "error":
model_versions = _api.update_model_versions(modelID, json_data)
output_html = _api.update_model_info(None, model_versions.get('value'), True, modelID, json_data)
output_html = _api.update_model_info(None, model_versions.get('value'), True, modelID, json_data, True)
css_path = Path(__file__).resolve().parents[1] / "style_html.css"
with open(css_path, 'r', encoding='utf-8') as css_file:
css = css_file.read()
replacements = {
'#0b0f19': 'var(--body-background-fill)',
'#0b0f19': 'var(--neutral-950)',
'#F3F4F6': 'var(--body-text-color)',
'white': 'var(--body-text-color)',
'#80a6c8': 'var(--secondary-300)',
'#60A5FA': 'var(--link-text-color-hover)',
'#1F2937': 'var(--input-background-fill)',
'#1F2937': 'var(--neutral-700)',
'#374151': 'var(--input-border-color)',
'#111827': 'var(--error-background-fill)',
'#111827': 'var(--neutral-800)',
'top: 50%;': '',
'padding-top: 0px;': 'padding-top: 475px;',
'.civitai_txt2img': '.civitai_placeholder'
@ -554,6 +558,7 @@ def save_model_info(install_path, file_name, sub_folder, sha256=None, preview_ht
def find_and_save(api_response, sha256=None, file_name=None, json_file=None, no_hash=None, overwrite_toggle=None):
save_desc = getattr(opts, "model_desc_to_json", True)
for item in api_response.get('items', []):
for model_version in item.get('modelVersions', []):
for file in model_version.get('files', []):
@ -563,15 +568,11 @@ def find_and_save(api_response, sha256=None, file_name=None, json_file=None, no_
if file_name == file_name_api if no_hash else sha256 == sha256_api:
gl.json_info = item
trained_words = model_version.get('trainedWords', [])
model_id = model_version.get('modelId', '')
if model_id:
model_url = f'Model URL: \"https://civitai.com/models/{model_id}\"\n'
description = item.get('description', '')
if description != None:
description = clean_description(description)
description = model_url + description
if save_desc:
description = item.get('description', '')
if description != None:
description = clean_description(description)
base_model = model_version.get('baseModel', '')
@ -605,22 +606,25 @@ def find_and_save(api_response, sha256=None, file_name=None, json_file=None, no_
if "activation text" not in content or not content["activation text"]:
content["activation text"] = trained_tags
changed = True
if "description" not in content or not content["description"]:
content["description"] = description
changed = True
if save_desc:
if "description" not in content or not content["description"]:
content["description"] = description
changed = True
if "sd version" not in content or not content["sd version"]:
content["sd version"] = base_model
changed = True
else:
content["activation text"] = trained_tags
content["description"] = description
if save_desc:
content["description"] = description
content["sd version"] = base_model
changed = True
with open(json_file, 'w', encoding="utf-8") as f:
json.dump(content, f, indent=4)
if changed: print(f"Model info saved to \"{json_file}\"")
if changed:
print(f"Model info saved to \"{json_file}\"")
return "found"
return "not found"
@ -651,10 +655,10 @@ def get_models(file_path, gen_hash=None):
return modelId
else:
return None
proxies, ssl = _api.get_proxies()
try:
if not modelId or modelId == "Model not found":
response = requests.get(by_hash, timeout=(10,30))
response = requests.get(by_hash, timeout=(60,30), proxies=proxies, verify=ssl)
if response.status_code == 200:
api_response = response.json()
if 'error' in api_response:
@ -761,9 +765,9 @@ def get_content_choices(scan_choices=False):
return content_list
return content_list
def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish, overwrite_toggle, tile_count, gen_hash, progress=gr.Progress() if queue else None):
def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish, overwrite_toggle, tile_count, gen_hash, create_html, progress=gr.Progress() if queue else None):
global from_ver, from_installed, no_update
update_log = getattr(opts, "update_log", True)
proxies, ssl = _api.get_proxies()
gl.scan_files = True
no_update = False
if from_ver:
@ -777,7 +781,7 @@ def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish,
if not folders:
if progress != None:
progress(0, desc=f"No folder selected.")
progress(0, desc=f"No model type selected.")
no_update = True
gl.scan_files = False
from_ver, from_installed = False, False
@ -857,13 +861,13 @@ def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish,
model_id = get_models(file_path, gen_hash)
if model_id == "offline":
print("The CivitAI servers did not respond, unable to retrieve Model ID")
elif model_id == "Model not found" and update_log:
elif model_id == "Model not found":
print(f"model: \"{file_name}\" not found on CivitAI servers.")
elif model_id != None:
all_model_ids.append(f"&ids={model_id}")
all_ids.append(model_id)
file_paths.append(file_path)
elif not model_id and update_log:
elif not model_id:
print(f"model ID not found for: \"{file_name}\"")
files_done += 1
@ -901,7 +905,7 @@ def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish,
try:
if progress is not None:
progress(url_done / url_count, desc=f"Sending API request... {url_done}/{url_count}")
response = requests.get(url, timeout=(10, 30))
response = requests.get(url, timeout=(60,30), proxies=proxies, verify=ssl)
if response.status_code == 200:
api_response_json = response.json()
@ -947,9 +951,8 @@ def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish,
all_model_ids = [model[0] for model in outdated_set]
all_model_names = [model[1] for model in outdated_set]
if update_log:
for model_name in all_model_names:
print(f'"{model_name}" is currently outdated.')
for model_name in all_model_names:
print(f'"{model_name}" is currently outdated.')
if len(all_model_ids) == 0:
no_update = True
@ -969,7 +972,7 @@ def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish,
api_url = gl.url_list_with_numbers.get(1)
if not url_error:
response = requests.get(api_url, timeout=(10,30))
response = requests.get(api_url, timeout=(60,30), proxies=proxies, verify=ssl)
try:
if response.status_code == 200:
response.encoding = "utf-8"
@ -1017,7 +1020,10 @@ def file_scan(folders, ver_finish, tag_finish, installed_finish, preview_finish,
for file_path, id_value in zip(file_paths, all_ids):
install_path, file_name = os.path.split(file_path)
model_versions = _api.update_model_versions(id_value, api_response)
preview_html = _api.update_model_info(None, model_versions.get('value'), True, id_value, api_response)
if create_html:
preview_html = _api.update_model_info(None, model_versions.get('value'), True, id_value, api_response, True)
else:
preview_html = None
sub_folder = os.path.normpath(os.path.relpath(install_path, gl.main_folder))
save_model_info(install_path, file_name, sub_folder, preview_html=preview_html, api_response=api_response, overwrite_toggle=overwrite_toggle)
if progress != None:

View File

@ -1,4 +1,8 @@
def init():
import warnings
from urllib3.exceptions import InsecureRequestWarning
warnings.simplefilter('ignore', InsecureRequestWarning)
global download_queue, last_version, cancel_status, recent_model, json_data, json_info, main_folder, previous_inputs, download_fail, sortNewest, isDownloading, old_download, scan_files, ver_json, file_scan, url_list_with_numbers, print
cancel_status = None

View File

@ -189,7 +189,7 @@ def on_ui_tabs():
base_filter = gr.Dropdown(label='Base model:', multiselect=True, choices=["SD 1.4","SD 1.5","SD 1.5 LCM","SD 2.0","SD 2.0 768","SD 2.1","SD 2.1 768","SD 2.1 Unclip","SDXL 0.9","SDXL 1.0","SDXL 1.0 LCM","SDXL Distilled","SDXL Turbo","SDXL Lightning","Stable Cascade","Pony","SVD","SVD XT","Playground v2","PixArt a","Other"], value=None, type="value", elem_id="centerText")
with gr.Row():
period_type = gr.Dropdown(label='Time period:', choices=["All Time", "Year", "Month", "Week", "Day"], value="All Time", type="value", elem_id="centerText")
sort_type = gr.Dropdown(label='Sort by:', choices=["Newest","Most Downloaded","Highest Rated","Most Liked", "Most Buzz","Most Discussed","Most Collected","Most Images"], value="Most Downloaded", type="value", elem_id="centerText")
sort_type = gr.Dropdown(label='Sort by:', choices=["Newest","Oldest","Most Downloaded","Highest Rated","Most Liked","Most Buzz","Most Discussed","Most Collected","Most Images"], value="Most Downloaded", type="value", elem_id="centerText")
with gr.Row(elem_id=component_id):
create_json = gr.Checkbox(label=f"Save info after download", value=True, elem_id=toggle1, min_width=171)
show_nsfw = gr.Checkbox(label="NSFW content", value=False, elem_id=toggle2, min_width=107)
@ -198,7 +198,7 @@ def on_ui_tabs():
hide_installed = gr.Checkbox(label="Hide installed models", value=False, elem_id=toggle5, min_width=170)
with gr.Row():
size_slider = gr.Slider(minimum=4, maximum=20, value=8, step=0.25, label='Tile size:')
tile_count_slider = gr.Slider(label="Tile count:", minimum=1, maximum=100, value=15, step=1, max_width=100)
tile_count_slider = gr.Slider(label="Tile count:", minimum=1, maximum=100, value=15, step=1)
with gr.Row(elem_id="save_set_box"):
save_settings = gr.Button(value="Save settings as default", elem_id="save_set_btn")
search_term = gr.Textbox(label="", placeholder="Search CivitAI", elem_id="searchBox")
@ -229,7 +229,7 @@ def on_ui_tabs():
with gr.Column(scale=4):
trained_tags = gr.Textbox(label='Trained tags (if any):', value=None, interactive=False, lines=1)
with gr.Column(scale=2, elem_id="spanWidth"):
base_model = gr.Textbox(label='Base model: ', value='', interactive=False, lines=1, elem_id="baseMdl")
base_model = gr.Textbox(label='Base model: ', value=None, interactive=False, lines=1, elem_id="baseMdl")
model_filename = gr.Textbox(label="Model filename:", interactive=False, value=None)
with gr.Row():
save_info = gr.Button(value="Save model info", interactive=False)
@ -248,10 +248,10 @@ def on_ui_tabs():
with gr.Tab("Update Models"):
with gr.Row():
selected_tags = gr.CheckboxGroup(elem_id="selected_tags", label="Scan for:", choices=scan_choices)
with gr.Row():
overwrite_toggle = gr.Checkbox(elem_id="overwrite_toggle", label="Overwrite any existing previews, tags or descriptions.", value=True)
with gr.Row():
skip_hash_toggle = gr.Checkbox(elem_id="skip_hash_toggle", label="One-Time Hash Generation for externally downloaded models.", value=True)
with gr.Row(elem_id="civitai_update_toggles"):
overwrite_toggle = gr.Checkbox(elem_id="overwrite_toggle", label="Overwrite any existing previews, tags or descriptions.", value=True, min_width=300)
skip_hash_toggle = gr.Checkbox(elem_id="skip_hash_toggle", label="One-Time Hash Generation for externally downloaded models.", value=True, min_width=300)
do_html_gen = gr.Checkbox(elem_id="do_html_gen", label="Save HTML file for each model when updating info & tags (increases process time).", value=False, min_width=300)
with gr.Row():
save_all_tags = gr.Button(value="Update model info & tags", interactive=True, visible=True)
cancel_all_tags = gr.Button(value="Cancel updating model info & tags", interactive=False, visible=False)
@ -349,6 +349,7 @@ def on_ui_tabs():
list_models.select(fn=None, inputs=list_models, _js="(list_models) => select_model(list_models)")
preview_html.change(fn=None, _js="() => adjustFilterBoxAndButtons()")
preview_html.change(fn=None, _js="() => setDescriptionToggle()")
back_to_top.click(fn=None, _js="() => BackToTop()")
@ -421,13 +422,36 @@ def on_ui_tabs():
list_html.change(fn=all_visible,inputs=list_html,outputs=select_all)
def update_models_dropdown(input):
if not gl.json_data:
return (
gr.Dropdown.update(value=None, choices=[], interactive=False), # List models
gr.Dropdown.update(value=None, choices=[], interactive=False), # List version
gr.HTML.update(value=None), # Preview HTML
gr.Textbox.update(value=None, interactive=False), # Trained Tags
gr.Textbox.update(value=None, interactive=False), # Base Model
gr.Textbox.update(value=None, interactive=False), # Model filename
gr.Textbox.update(value=None, interactive=False), # Install path
gr.Dropdown.update(value=None, choices=[], interactive=False), # Sub folder
gr.Button.update(interactive=False), # Download model btn
gr.Button.update(interactive=False), # Save image btn
gr.Button.update(interactive=False, visible=False), # Delete model btn
gr.Dropdown.update(value=None, choices=[], interactive=False), # File list
gr.Textbox.update(value=None), # DL Url
gr.Textbox.update(value=None), # Model ID
gr.Textbox.update(value=None), # Current sha256
gr.Button.update(interactive=False), # Save model info
gr.HTML.update(value='<div style="font-size: 24px; text-align: center; margin: 50px;">Click the search icon to load models.<br>Use the filter icon to filter results.</div>') # Model list
)
model_string = re.sub(r'\.\d{3}$', '', input)
model_name, model_id = _api.extract_model_info(model_string)
model_versions = _api.update_model_versions(model_id)
(html, tags, base_mdl, DwnButton, SaveImages, DelButton, filelist, filename, dl_url, id, current_sha256, install_path, sub_folder) = _api.update_model_info(model_string, model_versions.get('value'))
return (gr.Dropdown.update(value=model_string, interactive=True),
model_versions,html,tags,base_mdl,filename,install_path,sub_folder,DwnButton,SaveImages,DelButton,filelist,dl_url,id,current_sha256,
gr.Button.update(interactive=True))
gr.Button.update(interactive=True),
gr.HTML.update()
)
model_select.change(
fn=update_models_dropdown,
@ -448,7 +472,8 @@ def on_ui_tabs():
dl_url,
model_id,
current_sha256,
save_info
save_info,
list_html
]
)
@ -687,7 +712,8 @@ def on_ui_tabs():
preview_finish,
overwrite_toggle,
tile_count_slider,
skip_hash_toggle
skip_hash_toggle,
do_html_gen
]
load_to_browser_inputs = [
@ -927,7 +953,7 @@ def subfolder_list(folder, desc=None):
if insert_sub_3:
sub_folders.insert(3, f"{os.sep}Base model{os.sep}Author name{os.sep}Model name")
if insert_sub_4:
sub_folders.insert(4, f"{os.sep}Base model{os.sep}Author name{os.sep}Model name{os.sep}Version name")
sub_folders.insert(4, f"{os.sep}Base model{os.sep}Author name{os.sep}Model name{os.sep}Model version")
if insert_sub_5:
sub_folders.insert(5, f"{os.sep}Base model{os.sep}Model name")
if insert_sub_6:
@ -1108,6 +1134,16 @@ def on_ui_settings():
).info("Uses the matching local HTML file when pressing CivitAI button on model cards in txt2img and img2img")
)
shared.opts.add_option(
"local_path_in_html",
shared.OptionInfo(
False,
"Use local images in the HTML",
section=browser,
**({'category_id': cat_id} if ver_bool else {})
).info("Only works if all images of the corresponding model are downloaded")
)
shared.opts.add_option(
"page_header",
shared.OptionInfo(
@ -1137,15 +1173,15 @@ def on_ui_settings():
**({'category_id': cat_id} if ver_bool else {})
).info("Turns individual prompts from an example image into a button to send it to txt2img")
)
shared.opts.add_option(
"update_log",
"model_desc_to_json",
shared.OptionInfo(
True,
'Show console logs during update scanning',
'Save model description to json',
section=browser,
**({'category_id': cat_id} if ver_bool else {})
).info('Shows the "is currently outdated" messages in the console when scanning models for available updates')
).info('This saves the models description to the description field on model cards')
)
shared.opts.add_option(
@ -1168,16 +1204,6 @@ def on_ui_settings():
).info("Will append any content type and sub folders to the custom path.")
)
shared.opts.add_option(
"local_path_in_html",
shared.OptionInfo(
False,
"Use local images in the HTML",
section=browser,
**({'category_id': cat_id} if ver_bool else {})
).info("Only works if all images of the corresponding model are downloaded")
)
shared.opts.add_option(
"save_to_custom",
shared.OptionInfo(
@ -1187,6 +1213,40 @@ def on_ui_settings():
**({'category_id': cat_id} if ver_bool else {})
)
)
shared.opts.add_option(
"custom_civitai_proxy",
shared.OptionInfo(
r"",
"Proxy address",
gr.Textbox,
{"placeholder": "socks4://0.0.0.0:00000 | socks5://0.0.0.0:00000"},
section=browser,
**({'category_id': cat_id} if ver_bool else {})
).info("Only works with proxies that support HTTPS, Requires UI reload for Aria2 compatibility")
)
shared.opts.add_option(
"cabundle_path_proxy",
shared.OptionInfo(
r"",
"Path to custom CA Bundle",
gr.Textbox,
{"placeholder": "/path/to/custom/cabundle.pem"},
section=browser,
**({'category_id': cat_id} if ver_bool else {})
).info("Specify custom CA bundle for SSL certificate checks if required")
)
shared.opts.add_option(
"disable_sll_proxy",
shared.OptionInfo(
False,
"Disable SSL certificate checks",
section=browser,
**({'category_id': cat_id} if ver_bool else {})
).info("Not recommended for security, may be required if you do not have the correct CA Bundle available")
)
id_and_sub_options = {
"1" : f"{os.sep}Base model",

View File

@ -130,7 +130,18 @@
justify-content: center;
}
#toggle1L, #toggle2L, #toggle3L, #toggle4L, #toggle4L_api, #toggle5L, #overwrite_toggle, #skip_hash_toggle{
#civitai_update_toggles > div {
display: flex;
flex-direction: column;
}
#civitai_update_toggles {
margin-top: calc(-1 * var(--layout-gap));
margin-bottom: var(--layout-gap);
}
#toggle1L, #toggle2L, #toggle3L, #toggle4L, #toggle4L_api, #toggle5L,
#overwrite_toggle, #skip_hash_toggle, #do_html_gen {
display: flex;
justify-content: center;
}
@ -325,7 +336,7 @@
.civitai-tag,
.civitai-meta,
.civitai-meta-btn {
background-color: var(--error-background-fill);
background-color: var(--neutral-800);
border-radius: 8px;
padding: 4px 6px;
border: 1px solid var(--input-border-color);
@ -333,7 +344,7 @@
.civitai-meta-btn:hover {
cursor: pointer;
background-color: var(--input-background-fill);
background-color: var(--neutral-700);
}
#select_all_models_container {
@ -591,10 +602,68 @@
}
#civitai_preview_html .model-description {
border-top: 1px solid;
padding-bottom: 10px;
margin-bottom: 10px;
}
border-top: 1px solid;
overflow-wrap: break-word;
overflow: hidden;
position: relative;
max-height: 400px;
}
#civitai_preview_html .model-description::after {
content: "";
position: absolute;
bottom: 0;
width: 100%;
height: 75px;
background: linear-gradient(to bottom, rgb(255 255 255 / 0%), var(--background-fill-primary));
z-index: 1;
}
.description-toggle-label {
cursor: pointer;
}
.description-toggle-checkbox {
position: absolute;
opacity: 0;
z-index: -1;
}
.description-toggle-checkbox:checked + .model-description {
max-height: unset !important;
position: unset !important;
}
.model-description + .description-toggle-label::before {
content: "";
width: 1em;
height: 1em;
text-align: center;
transition: all 0.3s;
}
.model-description + .description-toggle-label {
display: flex;
padding: 10px 0px 0px 0px;
font-weight: bold;
cursor: pointer;
font-size: large;
}
.description-toggle-checkbox:checked + .model-description + .description-toggle-label::before {
transform: rotate(-90deg);
margin-right: 10px;
}
.description-toggle-checkbox:checked + .model-description + .description-toggle-label::after {
content: "Show Less...";
}
.description-toggle-checkbox:not(:checked) + .model-description + .description-toggle-label::after {
content: "Show All...";
}
/*------------------------------------------*/
/*End CSS accordion for toggling description*/
/*Avatar CSS mostly copied from CivitAI, but 48px instead of 32px*/
#civitai_preview_html .avatar {

View File

@ -162,6 +162,65 @@ a:hover {
border-style: none;
}
.model-description {
border-top: 1px solid;
overflow-wrap: break-word;
overflow: hidden;
position: relative;
max-height: 400px;
}
.model-description::after {
content: "";
position: absolute;
bottom: 0;
width: 100%;
height: 75px;
background: linear-gradient(to bottom, rgb(255 255 255 / 0%), #0b0f19);
z-index: 1;
}
.description-toggle-label {
display: flex;
padding: 10px 0px 0px 0px;
font-weight: bold;
cursor: pointer;
font-size: large;
color: white;
}
.description-toggle-checkbox {
position: absolute;
opacity: 0;
z-index: -1;
}
.description-toggle-checkbox:checked + .model-description {
max-height: none !important;
position: unset !important;
}
.model-description + .description-toggle-label::before {
content: "";
width: 1em;
height: 1em;
text-align: center;
transition: all 0.3s;
}
.description-toggle-checkbox:checked + .model-description + .description-toggle-label::before {
transform: rotate(-90deg);
margin-right: 10px;
}
.description-toggle-checkbox:checked + .model-description + .description-toggle-label::after {
content: "Show Less...";
}
.description-toggle-checkbox:not(:checked) + .model-description + .description-toggle-label::after {
content: "Show All...";
}
/*CSS accordion for toggling extra metadata*/
/*-----------------------------------------*/
.accordionCheckbox {