diff --git a/README.md b/README.md index 1510758..9ccea27 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,13 @@ https://github.com/BlafKing/sd-civitai-browser-plus/assets/9644716/44c5c7a0-4854 # Changelog 📋 +

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)) + +---

v3.4.0

* Feature: (BETA) Download queue! rearrange download order and remove models from queue diff --git a/javascript/civitai-html.js b/javascript/civitai-html.js index c9fc2ce..7096cdc 100644 --- a/javascript/civitai-html.js +++ b/javascript/civitai-html.js @@ -620,14 +620,14 @@ function metaToTxt2Img(type, element) { let is_positive = false let is_negative = false switch(type) { - case 'prompt': + case 'Prompt': is_positive = true break; - case 'negativePrompt': + case 'Negative prompt': inf = 'Negative prompt: ' + inf; is_negative = true break; - case 'seed': + case 'Seed': inf = 'Seed: ' + inf; inf = inf + inf + inf; break; @@ -639,19 +639,19 @@ function metaToTxt2Img(type, element) { inf = 'Model: ' + inf; inf = inf + inf + inf; break; - case 'clipSkip': + case 'Clip skip': inf = 'Clip skip: ' + inf; inf = inf + inf + inf; break; - case 'sampler': + case 'Sampler': inf = 'Sampler: ' + inf; inf = inf + inf + inf; break; - case 'steps': + case 'Steps': inf = 'Steps: ' + inf; inf = inf + inf + inf; break; - case 'cfgScale': + case 'CFG scale': inf = 'CFG scale: ' + inf; inf = inf + inf + inf; break; @@ -1027,7 +1027,6 @@ function onPageLoad() { } observer.observe(civitaiDiv); - queueObserver.observe(queue_list, queueObserverOptions); adjustFilterBoxAndButtons(); setupClickOutsideListener(); createLink(infoElement); diff --git a/scripts/civitai_api.py b/scripts/civitai_api.py index f8e86e2..deff796 100644 --- a/scripts/civitai_api.py +++ b/scripts/civitai_api.py @@ -8,10 +8,12 @@ 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 +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 from html import escape @@ -623,17 +625,44 @@ def cleaned_name(file_name): return f"{clean_name}{extension}" def fetch_and_process_image(image_url): - use_local = getattr(opts, "local_path_in_html", False) - if use_local: - image = Image.open(image_url) - geninfo, _ = read_info_from_image(image) - return geninfo - + try: + parsed_url = urllib.parse.urlparse(image_url) + if parsed_url.scheme and parsed_url.netloc: + response = requests.get(image_url) + if response.status_code == 200: + image = Image.open(BytesIO(response.content)) + geninfo, _ = read_info_from_image(image) + return geninfo + else: + image = Image.open(image_url) + geninfo, _ = read_info_from_image(image) + return geninfo + 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)) - geninfo, _ = read_info_from_image(image) - return geninfo + + prompt, _ = read_info_from_image(image) + 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, prompt_dict return None def extract_model_info(input_string): @@ -762,17 +791,21 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in model_main_url = f"https://civitai.com/models/{item['id']}" img_html = '
' for index, pic in enumerate(selected_version['images']): - meta_button = False - meta = pic['metadata'] - if meta and meta.get('prompt'): - meta_button = True - BtnImage = True # 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, prompt_dict = image_url_to_promptInfo(image_url) + nsfw = 'class="model-block"' + meta_button = False + if prompt_dict and prompt_dict.get('Prompt'): + meta_button = True + BtnImage = True + if pic['nsfw'] not in ["None", "Soft"]: nsfw = 'class="civnsfw model-block"' @@ -804,21 +837,20 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in else: img_html += '
' - if meta: + if prompt_dict: img_html += '
' # Define the preferred order of keys and convert them to lowercase - preferred_order = ["prompt", "negativePrompt", "seed", "Size", "Model", "clipSkip", "sampler", "steps", "cfgScale"] - preferred_order_lower = [key.lower() for key in preferred_order] + preferred_order = ["Prompt", "Negative prompt", "Seed", "Size", "Model", "Clip skip", "Sampler", "Steps", "CFG scale"] # Loop through the keys in the preferred order and add them to the HTML for key in preferred_order: - if key in meta: - value = meta[key] + if key in prompt_dict: + value = prompt_dict[key] if meta_btn: img_html += f'
{escape(str(key).capitalize())}
{escape(str(value))}
' else: img_html += f'
{escape(str(key).capitalize())}
{escape(str(value))}
' # Check if there are remaining keys in meta - remaining_keys = [key for key in meta if key.lower() not in preferred_order_lower] + remaining_keys = [key for key in prompt_dict if key not in preferred_order] # Add the rest if remaining_keys: @@ -830,7 +862,7 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in
""" for key in remaining_keys: - value = meta[key] + value = prompt_dict[key] img_html += f'
{escape(str(key).capitalize())}
{escape(str(value))}
' img_html = img_html + '
' @@ -1002,7 +1034,7 @@ def update_model_info(model_string=None, model_version=None, only_html=False, in if any(key in default_sub for key in variable_mapping.keys()): path_components = [variable_mapping.get(component.strip(os.sep), component.strip(os.sep)) for component in default_sub.split(os.sep)] - default_sub = os.path.join(*path_components) + default_sub = os.path.join(os.sep, *path_components) if folder_location == "None": folder_location = model_folder diff --git a/scripts/civitai_file_manage.py b/scripts/civitai_file_manage.py index 72dadba..e5524fe 100644 --- a/scripts/civitai_file_manage.py +++ b/scripts/civitai_file_manage.py @@ -1,4 +1,3 @@ -import base64 import json import gradio as gr import urllib.request @@ -11,11 +10,10 @@ import time import errno import requests import hashlib +import base64 +from PIL import Image from pathlib import Path from urllib.parse import urlparse - -from PIL import Image -from sympy import preview from modules.shared import cmd_opts, opts from scripts.civitai_global import print import scripts.civitai_global as gl @@ -375,7 +373,6 @@ def model_from_sent(model_name, content_type, tile_count, path_input): 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." - if path_input == "Not Found": model_name = re.sub(r'\.\d{3}$', '', model_name) diff --git a/scripts/civitai_gui.py b/scripts/civitai_gui.py index b55dfee..39376ec 100644 --- a/scripts/civitai_gui.py +++ b/scripts/civitai_gui.py @@ -1176,7 +1176,7 @@ def on_ui_settings(): "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( @@ -1260,4 +1260,4 @@ def on_ui_settings(): shared.opts.add_option(f"{setting_name}_subfolder", shared.OptionInfo("None", folder_name, gr.Dropdown, make_lambda(folder, desc), section=download, **({'category_id': cat_id} if ver_bool else {}))) script_callbacks.on_ui_tabs(on_ui_tabs) -script_callbacks.on_ui_settings(on_ui_settings) +script_callbacks.on_ui_settings(on_ui_settings) \ No newline at end of file diff --git a/style.css b/style.css index 86b5fba..3c7667b 100644 --- a/style.css +++ b/style.css @@ -688,7 +688,7 @@ } #civitai_preview_html .accordionCheckbox:checked ~ .tab-content { - max-height: 200vh; + max-height: unset; padding: 1em; } /*-----------------------------------------*/