+
+* 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)
+
+---
v3.4.1
-* 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.
---
v3.4.0
diff --git a/install.py b/install.py
index e81b40f..ffb0b0e 100644
--- a/install.py
+++ b/install.py
@@ -16,4 +16,5 @@ install_req("send2trash")
install_req("zip_unicode", "ZipUnicode")
install_req("bs4", "beautifulsoup4")
install_req("fake_useragent")
-install_req("packaging")
\ No newline at end of file
+install_req("packaging")
+install_req("pysocks")
\ No newline at end of file
diff --git a/javascript/civitai-html.js b/javascript/civitai-html.js
index 8ba714c..2cd9819 100644
--- a/javascript/civitai-html.js
+++ b/javascript/civitai-html.js
@@ -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 = `
@@ -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 = 'Open on CivitAI';
- }
- 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);
diff --git a/scripts/civitai_api.py b/scripts/civitai_api.py
index 9c8eb0c..cf43b51 100644
--- a/scripts/civitai_api.py
+++ b/scripts/civitai_api.py
@@ -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 = '
The Civit-API has failed to return due to an error. Check the logs for more details.
'
+ 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 = '
The Civit-API has failed to return due to an error. Check the logs for more details.
'
+ 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 = '
'
+
+ 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 += '
'
- # 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'
{escape(str(key).capitalize())}
{escape(str(value))}
'
+ img_html += f'
{escape(str(key))}
{escape(str(value))}
'
else:
- img_html += f'
{escape(str(key).capitalize())}
{escape(str(value))}
'
+ img_html += f'
{escape(str(key))}
{escape(str(value))}
'
# 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
-
+
+
Description
{model_desc}
+
{img_html}
'''
@@ -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:
diff --git a/scripts/civitai_download.py b/scripts/civitai_download.py
index 71bfcea..c41461f 100644
--- a/scripts/civitai_download.py
+++ b/scripts/civitai_download.py
@@ -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()
diff --git a/scripts/civitai_file_manage.py b/scripts/civitai_file_manage.py
index 2f96f9b..ac2258e 100644
--- a/scripts/civitai_file_manage.py
+++ b/scripts/civitai_file_manage.py
@@ -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. Maybe the model doesn\'t exist on CivitAI?"
path_not_found = div + "Model ID not found. Could not locate the model path."
offline = div + "CivitAI failed to respond. The servers are likely offline."
+ error = div + "CivitAI failed to respond due to an error. 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:
diff --git a/scripts/civitai_global.py b/scripts/civitai_global.py
index d322db6..71567dd 100644
--- a/scripts/civitai_global.py
+++ b/scripts/civitai_global.py
@@ -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
diff --git a/scripts/civitai_gui.py b/scripts/civitai_gui.py
index 365938e..1a9e53c 100644
--- a/scripts/civitai_gui.py
+++ b/scripts/civitai_gui.py
@@ -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='
Click the search icon to load models. Use the filter icon to filter results.
') # 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",
diff --git a/style.css b/style.css
index 3c7667b..c7269e1 100644
--- a/style.css
+++ b/style.css
@@ -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 {
diff --git a/style_html.css b/style_html.css
index 92568e7..59a5af1 100644
--- a/style_html.css
+++ b/style_html.css
@@ -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 {