diff --git a/javascript/image_browser.js b/javascript/image_browser.js index 4844a9a..c8aaab4 100644 --- a/javascript/image_browser.js +++ b/javascript/image_browser.js @@ -1,21 +1,26 @@ let image_browser_state = "free" let image_browser_webui_ready = false let image_browser_started = false +let image_browser_console_log = "" +let image_browser_debug = false function image_browser_delay(ms){return new Promise(resolve => setTimeout(resolve, ms))} onUiLoaded(image_browser_start_it_up) async function image_browser_wait_for_webui() { + if (image_browser_debug) console.log("image_browser_wait_for_webui:start") await image_browser_delay(100) while (gradioApp().getElementById("setting_sd_model_checkpoint").querySelector(".eta-bar")) { await image_browser_delay(200) } image_browser_webui_ready = true image_browser_start() + if (image_browser_debug) console.log("image_browser_wait_for_webui:end") } async function image_browser_start_it_up() { + if (image_browser_debug) console.log("image_browser_start_it_up:start") container = gradioApp().getElementById("image_browser_tabs_container") let controls = container.querySelectorAll('[id*="_control_"]') controls.forEach(function(control) { @@ -29,9 +34,11 @@ async function image_browser_start_it_up() { }) image_browser_wait_for_webui() + if (image_browser_debug) console.log("image_browser_start_it_up:end") } async function image_browser_lock(reason) { + if (image_browser_debug) console.log("image_browser_lock:start") // Wait until lock removed let i = 0 while (image_browser_state != "free") { @@ -43,13 +50,17 @@ async function image_browser_lock(reason) { } // Lock image_browser_state = reason + if (image_browser_debug) console.log("image_browser_lock:end") } async function image_browser_unlock() { + if (image_browser_debug) console.log("image_browser_unlock:start") image_browser_state = "free" + if (image_browser_debug) console.log("image_browser_unlock:end") } const image_browser_click_image = async function() { + if (image_browser_debug) console.log("image_browser_click_image:start") await image_browser_lock("image_browser_click_image") const tab_base_tag = image_browser_current_tab() const container = gradioApp().getElementById(tab_base_tag + "_image_browser_container") @@ -70,13 +81,16 @@ const image_browser_click_image = async function() { } await image_browser_unlock() set_btn.click() + if (image_browser_debug) console.log("image_browser_click_image:end") } async function image_browser_get_current_img(tab_base_tag, img_index, page_index, filenames, turn_page_switch, image_gallery) { + if (image_browser_debug) console.log("image_browser_get_current_img:start") await image_browser_lock("image_browser_get_current_img") img_index = gradioApp().getElementById(tab_base_tag + '_image_browser_set_index').getAttribute("img_index") gradioApp().dispatchEvent(new Event("image_browser_get_current_img")) await image_browser_unlock() + if (image_browser_debug) console.log("image_browser_get_current_img:end") return [ tab_base_tag, img_index, @@ -88,9 +102,13 @@ async function image_browser_get_current_img(tab_base_tag, img_index, page_index } async function image_browser_refresh_current_page_preview() { + if (image_browser_debug) console.log("image_browser_refresh_current_page_preview:start") await image_browser_delay(200) const preview_div = gradioApp().querySelector('.preview') - if (preview_div === null) return + if (preview_div === null) { + if (image_browser_debug) console.log("image_browser_refresh_current_page_preview:end") + return + } const tab_base_tag = image_browser_current_tab() const gallery = gradioApp().querySelector(`#${tab_base_tag}_image_browser`) const set_btn = gallery.querySelector(".image_browser_set_index") @@ -99,9 +117,11 @@ async function image_browser_refresh_current_page_preview() { const gallery_items = gallery.querySelectorAll(".thumbnail-item") const curr_image = gallery_items[curr_idx] curr_image.click() + if (image_browser_debug) console.log("image_browser_refresh_current_page_preview:end") } async function image_browser_turnpage(tab_base_tag) { + if (image_browser_debug) console.log("image_browser_turnpage:start") while (!image_browser_started) { await image_browser_delay(200) } @@ -115,16 +135,20 @@ async function image_browser_turnpage(tab_base_tag) { } catch (e) { console.error(e) } + if (image_browser_debug) console.log("image_browser_turnpage:end") } const image_browser_get_current_img_handler = (del_img_btn) => { + if (image_browser_debug) console.log("image_browser_get_current_img_handler:start") // Prevent delete button spam del_img_btn.style.pointerEvents = "auto" del_img_btn.style.cursor = "default" del_img_btn.style.opacity = "1" + if (image_browser_debug) console.log("image_browser_get_current_img_handler:end") } async function image_browser_select_image(tab_base_tag, img_index, select_image) { + if (image_browser_debug) console.log("image_browser_select_image:start") if (select_image) { await image_browser_lock("image_browser_select_image") const del_img_btn = gradioApp().getElementById(tab_base_tag + "_image_browser_del_img_btn") @@ -148,9 +172,11 @@ async function image_browser_select_image(tab_base_tag, img_index, select_image) gradioApp().removeEventListener("image_browser_get_current_img", () => image_browser_get_current_img_handler(del_img_btn)) gradioApp().addEventListener("image_browser_get_current_img", () => image_browser_get_current_img_handler(del_img_btn)) } + if (image_browser_debug) console.log("image_browser_select_image:end") } async function image_browser_gototab(tabname) { + if (image_browser_debug) console.log("image_browser_gototab:start") await image_browser_lock("image_browser_gototab") tabNav = gradioApp().querySelector(".tab-nav") @@ -180,9 +206,11 @@ async function image_browser_gototab(tabname) { } await image_browser_unlock() + if (image_browser_debug) console.log("image_browser_gototab:end") } async function image_browser_get_image_for_ext(tab_base_tag, image_index) { + if (image_browser_debug) console.log("image_browser_get_image_for_ext:start") const image_browser_image = gradioApp().querySelectorAll(`#${tab_base_tag}_image_browser_gallery .thumbnail-item`)[image_index] const canvas = document.createElement("canvas") @@ -196,10 +224,12 @@ async function image_browser_get_image_for_ext(tab_base_tag, image_index) { canvas.getContext("2d").drawImage(image, 0, 0) + if (image_browser_debug) console.log("image_browser_get_image_for_ext:end") return canvas.toDataURL() } function image_browser_openoutpaint_send(tab_base_tag, image_index, image_browser_prompt, image_browser_neg_prompt, name = "WebUI Resource") { + if (image_browser_debug) console.log("image_browser_openoutpaint_send:start") image_browser_get_image_for_ext(tab_base_tag, image_index) .then((dataURL) => { // Send to openOutpaint @@ -220,9 +250,11 @@ function image_browser_openoutpaint_send(tab_base_tag, image_index, image_browse // Change Tab image_browser_gototab("openOutpaint") }) + if (image_browser_debug) console.log("image_browser_openoutpaint_send:end") } async function image_browser_controlnet_send(toTab, tab_base_tag, image_index, controlnetNum, controlnetType) { + if (image_browser_debug) console.log("image_browser_controlnet_send:start") // Logic originally based on github.com/fkunn1326/openpose-editor const dataURL = await image_browser_get_image_for_ext(tab_base_tag, image_index) const blob = await (await fetch(dataURL)).blob() @@ -310,6 +342,7 @@ async function image_browser_controlnet_send(toTab, tab_base_tag, image_index, c input.files = list const event = new Event("change", { "bubbles": true, "composed": true }) input.dispatchEvent(event) + if (image_browser_debug) console.log("image_browser_controlnet_send:end") } function image_browser_controlnet_send_txt2img(tab_base_tag, image_index, controlnetNum, controlnetType) { @@ -328,14 +361,17 @@ function image_browser_class_add(tab_base_tag) { } function btnClickHandler(tab_base_tag, btn) { + if (image_browser_debug) console.log("btnClickHandler:start") const tabs_box = gradioApp().getElementById("image_browser_tabs_container") if (!tabs_box.classList.contains(tab_base_tag)) { gradioApp().getElementById(tab_base_tag + "_image_browser_renew_page").click() tabs_box.classList.add(tab_base_tag) } + if (image_browser_debug) console.log("btnClickHandler:end") } function image_browser_init() { + if (image_browser_debug) console.log("image_browser_init:start") const tab_base_tags = gradioApp().getElementById("image_browser_tab_base_tags_list") if (tab_base_tags) { const image_browser_tab_base_tags_list = tab_base_tags.querySelector("textarea").value.split(",") @@ -353,20 +389,38 @@ function image_browser_init() { } image_browser_keydown() image_browser_touch() + if (image_browser_debug) console.log("image_browser_init:end") } async function image_browser_wait_for_gallery_btn(tab_base_tag){ + if (image_browser_debug) console.log("image_browser_wait_for_gallery_btn:start") await image_browser_delay(100) while (!gradioApp().getElementById(image_browser_current_tab() + "_image_browser_gallery").getElementsByClassName("thumbnail-item")) { await image_browser_delay(200) } + if (image_browser_debug) console.log("image_browser_wait_for_gallery_btn:end") } -function image_browser_renew_page(tab_base_tag) { - gradioApp().getElementById(tab_base_tag + '_image_browser_renew_page').click() +function image_browser_hijack_console_log() { + (function () { + const oldLog = console.log + console.log = function (message) { + const formattedTime = new Date().toISOString().slice(0, -5).replace(/[TZ]/g, ' ').trim().replace(/\s+/g, '-').replace(/:/g, '-') + image_browser_console_log = image_browser_console_log + formattedTime + " " + "image_browser.js: " + message + "\n" + oldLog.apply(console, arguments) + } + })() + image_browser_debug = true +} + +function get_js_logs() { + log_to_py = image_browser_console_log + image_browser_console_log = "" + return log_to_py } function image_browser_start() { + if (image_browser_debug) console.log("image_browser_start:start") image_browser_init() const mutationObserver = new MutationObserver(function(mutationsList) { const tab_base_tags = gradioApp().getElementById("image_browser_tab_base_tags_list") @@ -380,6 +434,7 @@ function image_browser_start() { gallery_item.addEventListener('click', image_browser_click_image, true) document.onkeyup = async function(e) { if (!image_browser_active()) { + if (image_browser_debug) console.log("image_browser_start:end") return } const current_tab = image_browser_current_tab() @@ -400,14 +455,20 @@ function image_browser_start() { cls_btn.addEventListener('click', () => image_browser_renew_page(tab_base_tag), false) } }) + const debug_level_option = gradioApp().getElementById("image_browser_debug_level_option").querySelector("textarea").value + if (debug_level_option == 'javascript' && !image_browser_debug) { + image_browser_hijack_console_log() + } } }) mutationObserver.observe(gradioApp(), { childList:true, subtree:true }) image_browser_started = true image_browser_activate_controls() + if (image_browser_debug) console.log("image_browser_start:end") } async function image_browser_activate_controls() { + if (image_browser_debug) console.log("image_browser_activate_controls:start") await image_browser_delay(500) container = gradioApp().getElementById("image_browser_tabs_container") let controls = container.querySelectorAll('[id*="_control_"]') @@ -420,9 +481,17 @@ async function image_browser_activate_controls() { warnings.forEach(function(warning) { warning.innerHTML = "

 " }) + if (image_browser_debug) console.log("image_browser_activate_controls:end") +} + +function image_browser_renew_page(tab_base_tag) { + if (image_browser_debug) console.log("image_browser_renew_page:start") + gradioApp().getElementById(tab_base_tag + '_image_browser_renew_page').click() + if (image_browser_debug) console.log("image_browser_renew_page:end") } function image_browser_current_tab() { + if (image_browser_debug) console.log("image_browser_current_tab:start") const tabs = gradioApp().getElementById("image_browser_tabs_container").querySelectorAll('[id$="_image_browser_container"]') const tab_base_tags = gradioApp().getElementById("image_browser_tab_base_tags_list") const image_browser_tab_base_tags_list = tab_base_tags.querySelector("textarea").value.split(",").sort((a, b) => b.length - a.length) @@ -430,20 +499,26 @@ function image_browser_current_tab() { if (element.style.display === "block") { const id = element.id const tab_base_tag = image_browser_tab_base_tags_list.find(element => id.startsWith(element)) || null + if (image_browser_debug) console.log("image_browser_current_tab:end") return tab_base_tag } } + if (image_browser_debug) console.log("image_browser_current_tab:end") } function image_browser_active() { + if (image_browser_debug) console.log("image_browser_active:start") const ext_active = gradioApp().getElementById("tab_image_browser") + if (image_browser_debug) console.log("image_browser_active:end") return ext_active && ext_active.style.display !== "none" } function image_browser_keydown() { + if (image_browser_debug) console.log("image_browser_keydown:start") gradioApp().addEventListener("keydown", function(event) { // If we are not on the Image Browser Extension, dont listen for keypresses if (!image_browser_active()) { + if (image_browser_debug) console.log("image_browser_keydown:end") return } @@ -455,7 +530,8 @@ function image_browser_keydown() { target = event.composedPath()[0] } if (!target || target.nodeName === "INPUT" || target.nodeName === "TEXTAREA") { - return + if (image_browser_debug) console.log("image_browser_keydown:end") + return } const tab_base_tag = image_browser_current_tab() @@ -496,6 +572,7 @@ function image_browser_keydown() { if (event.code == "KeyF" && modifiers_none) { if (tab_base_tag == "image_browser_tab_favorites") { + if (image_browser_debug) console.log("image_browser_keydown:end") return } const favoriteBtn = gradioApp().getElementById(tab_base_tag + "_image_browser_favorites_btn") @@ -538,19 +615,23 @@ function image_browser_keydown() { image_browser_refresh_current_page_preview() } }) + if (image_browser_debug) console.log("image_browser_keydown:end") } function image_browser_touch() { + if (image_browser_debug) console.log("image_browser_touch:start") let touchStartX = 0 let touchEndX = 0 gradioApp().addEventListener("touchstart", function(event) { if (!image_browser_active()) { + if (image_browser_debug) console.log("image_browser_touch:end") return } touchStartX = event.touches[0].clientX; }) gradioApp().addEventListener("touchend", function(event) { if (!image_browser_active()) { + if (image_browser_debug) console.log("image_browser_touch:end") return } touchEndX = event.changedTouches[0].clientX @@ -576,4 +657,5 @@ function image_browser_touch() { } } }) + if (image_browser_debug) console.log("image_browser_touch:end") } diff --git a/scripts/image_browser.py b/scripts/image_browser.py index d29037c..294c500 100644 --- a/scripts/image_browser.py +++ b/scripts/image_browser.py @@ -19,6 +19,7 @@ import hashlib import modules.extras import modules.images import modules.ui +from datetime import datetime from modules import paths, shared, script_callbacks, scripts, images from modules.shared import opts, cmd_opts from modules.ui_common import plaintext_to_html @@ -131,21 +132,47 @@ class ImageBrowserTab(): tabs_list = [ImageBrowserTab(tab) for tab in tabs_list] +debug_level_types = ["none", "warning log", "debug log", "javascript log"] #, "capture logs for saving"] + +debug_levels_list = [] +for i in range(len(debug_level_types)): + level = debug_level_types[i].split(" ")[0] + text = str(i) + " - " + debug_level_types[i] + debug_levels_list.append((level, text)) + +def debug_levels(arg_value=None, arg_level=None, arg_text=None): + if arg_value is not None: + return arg_value, debug_levels_list[arg_value] + elif arg_level is not None: + for i, (level, text) in enumerate(debug_levels_list): + if level == arg_level: + return i, debug_levels_list[i] + elif arg_text is not None: + for i, (level, text) in enumerate(debug_levels_list): + if text == arg_text: + return i, debug_levels_list[i] + # Logging logger = logging.getLogger(__name__) logger_mode = logging.ERROR -if hasattr(opts, "image_browser_logger_warning"): - if opts.image_browser_logger_warning: - logger_mode = logging.WARNING -if hasattr(opts, "image_browser_logger_debug"): - if opts.image_browser_logger_debug: +level_value = 0 +if hasattr(opts, "image_browser_debug_level"): + warning_level_value, (warning_level, warning_level_text) = debug_levels(arg_level="warning") + debug_level_value, (debug_level, debug_level_text) = debug_levels(arg_level="debug") + level_value, (level, level_text) = debug_levels(arg_text=opts.image_browser_debug_level) + if level_value >= debug_level_value: logger_mode = logging.DEBUG + elif level_value >= warning_level_value: + logger_mode = logging.WARNING logger.setLevel(logger_mode) if (logger.hasHandlers()): logger.handlers.clear() console_handler = logging.StreamHandler() console_handler.setLevel(logger_mode) +formatter = logging.Formatter(f'%(asctime)s image_browser.py: %(message)s', datefmt='%Y-%m-%d-%H:%M:%S') +console_handler.setFormatter(formatter) logger.addHandler(console_handler) +logger.warning(f"debug_level: {level_value}") # Debug logging if logger.isEnabledFor(logging.DEBUG): logger.debug(f"{sys.executable} {sys.version}") @@ -245,6 +272,10 @@ def tab_select(): path_recorder, path_recorder_formatted, path_recorder_unformatted = read_path_recorder() return path_recorder, gr.update(choices=path_recorder_unformatted) +def js_logs_output(js_log): + print(js_log) + return js_log + def reduplicative_file_move(src, dst): def same_name_file(basename, path): name, ext = os.path.splitext(basename) @@ -302,6 +333,7 @@ def save_image(file_name, filenames, page_index, turn_page_switch, dest_path): return message, filenames, page_index, turn_page_switch def delete_image(tab_base_tag_box, delete_num, name, filenames, image_index, visible_num, delete_confirm, turn_page_switch, image_page_list): + logger.debug("delete_image") refresh = False delete_num = int(delete_num) image_index = int(image_index) @@ -641,6 +673,7 @@ def exif_search(needle, haystack, use_regex, case_sensitive): def get_all_images(dir_name, sort_by, sort_order, keyword, tab_base_tag_box, img_path_depth, ranking_filter, aes_filter_min, aes_filter_max, exif_keyword, negative_prompt_search, use_regex, case_sensitive): global current_depth + logger.debug("get_all_images") current_depth = 0 fileinfos = traverse_all_files(dir_name, [], tab_base_tag_box, img_path_depth) keyword = keyword.strip(" ") @@ -762,6 +795,7 @@ def get_all_images(dir_name, sort_by, sort_order, keyword, tab_base_tag_box, img return filenames def get_image_thumbnail(image_list): + logger.debug("get_image_thumbnail") optimized_cache = os.path.join(tempfile.gettempdir(),"optimized") os.makedirs(optimized_cache,exist_ok=True) thumbnail_list = [] @@ -795,6 +829,7 @@ def get_image_thumbnail(image_list): return thumbnail_list def get_image_page(img_path, page_index, filenames, keyword, sort_by, sort_order, tab_base_tag_box, img_path_depth, ranking_filter, aes_filter_min, aes_filter_max, exif_keyword, negative_prompt_search, use_regex, case_sensitive): + logger.debug("get_image_page") if img_path == "": return [], page_index, [], "", "", "", 0, "", None, "" @@ -832,7 +867,7 @@ def get_current_file(tab_base_tag_box, num, page_index, filenames): return file def show_image_info(tab_base_tag_box, num, page_index, filenames, turn_page_switch, image_gallery): - logger.debug(f"tab_base_tag_box, num, page_index, len(filenames), num_of_imgs_per_page: {tab_base_tag_box}, {num}, {page_index}, {len(filenames)}, {num_of_imgs_per_page}") + logger.debug(f"show_image_info: tab_base_tag_box, num, page_index, len(filenames), num_of_imgs_per_page: {tab_base_tag_box}, {num}, {page_index}, {len(filenames)}, {num_of_imgs_per_page}") if len(filenames) == 0: # This should only happen if webui was stopped and started again and the user clicks on one of the still displayed images. # The state with the filenames will be empty then. In that case we return None to prevent further errors and force a page refresh. @@ -1121,6 +1156,7 @@ def create_tab(tab: ImageBrowserTab, current_gr_tab: gr.Tab): image_browser_mod_keys = gr.Textbox(value=mod_keys, elem_id=f"{tab.base_tag}_image_browser_mod_keys") image_browser_prompt = gr.Textbox(elem_id=f"{tab.base_tag}_image_browser_prompt") image_browser_neg_prompt = gr.Textbox(elem_id=f"{tab.base_tag}_image_browser_neg_prompt") + js_logs = gr.Textbox() # Maintenance tab with gr.Row(visible=maint): @@ -1151,6 +1187,11 @@ def create_tab(tab: ImageBrowserTab, current_gr_tab: gr.Tab): maint_reapply_ranking = gr.Button(value="Reapply ranking after moving files") with gr.Column(scale=10): gr.HTML(visible=False) + with gr.Row(visible=maint): + with gr.Column(scale=1): + maint_get_js_logs = gr.Button(value="Get javascript logs") + with gr.Column(scale=10): + maint_show_logs = gr.Textbox(label="Javascript logs", lines=10, interactive=False) with gr.Row(visible=False): with gr.Column(scale=1): maint_rebuild_ranking = gr.Button(value="Rebuild ranking from exif info") @@ -1286,6 +1327,13 @@ def create_tab(tab: ImageBrowserTab, current_gr_tab: gr.Tab): inputs=[path_recorder, maint_wait], outputs=[maint_wait, maint_last_msg] ) + maint_get_js_logs.click( + fn=js_logs_output, + _js="get_js_logs", + show_progress=True, + inputs=[js_logs], + outputs=[maint_show_logs] + ) # other functions if opts.image_browser_use_thumbnail: @@ -1404,7 +1452,15 @@ def on_ui_tabs(): create_tab(tab, current_gr_tab) gr.Textbox(",".join( [tab.base_tag for tab in tabs_list] ), elem_id="image_browser_tab_base_tags_list", visible=False) - return (image_browser , "Image Browser", "image_browser"), + javascript_level_value, (javascript_level, javascript_level_text) = debug_levels(arg_level="javascript") + level_value, (level, level_text) = debug_levels(arg_text=opts.image_browser_debug_level) + if level_value >= javascript_level_value: + debug_level_option = level + else: + debug_level_option = "" + gr.Textbox(value=debug_level_option, elem_id="image_browser_debug_level_option", visible=False) + + return (image_browser, "Image Browser", "image_browser"), def move_setting(cur_setting_name, old_setting_name, option_info, section, added): try: @@ -1428,6 +1484,10 @@ def move_setting(cur_setting_name, old_setting_name, option_info, section, added def on_ui_settings(): # [current setting_name], [old setting_name], [default], [label], [component], [component_args] active_tabs_description = f"List of active tabs (separated by commas). Available options are {', '.join(default_tab_options)}. Custom folders are also supported by specifying their path." + debug_level_choices = [] + for i in range(len(debug_level_types)): + level_value, (level, level_text) = debug_levels(arg_value=i) + debug_level_choices.append(level_text) image_browser_options = [ ("image_browser_active_tabs", None, ", ".join(default_tab_options), active_tabs_description), @@ -1436,8 +1496,7 @@ def on_ui_settings(): ("image_browser_copy_image", "images_copy_image", False, "Move buttons copy instead of move"), ("image_browser_delete_message", "images_delete_message", True, "Print image deletion messages to the console"), ("image_browser_txt_files", "images_txt_files", True, "Move/Copy/Delete matching .txt files"), - ("image_browser_logger_warning", "images_logger_warning", False, "Print warning logs to the console"), - ("image_browser_logger_debug", "images_logger_debug", False, "Print debug logs to the console"), + ("image_browser_debug_level", None, debug_level_choices[0], "Debug level", gr.Dropdown, lambda: {"choices": debug_level_choices}), ("image_browser_delete_recycle", "images_delete_recycle", False, "Use recycle bin when deleting images"), ("image_browser_scan_exif", "images_scan_exif", True, "Scan Exif-/.txt-data (initially slower, but required for many features to work)"), ("image_browser_mod_shift", None, False, "Change CTRL keybindings to SHIFT"),