diff --git a/.gitattributes b/.gitattributes
index a599a901f4c9b1f561b97b72318356286e1406ca..a314bc9c5d811100bc2f8b2966ffb2d39ddccc69 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -36,3 +36,4 @@ Auto-Photoshop-StableDiffusion-Plugin/docs/heal_brush.gif filter=lfs diff=lfs me
Auto-Photoshop-StableDiffusion-Plugin/docs/inpainting.gif filter=lfs diff=lfs merge=lfs -text
Auto-Photoshop-StableDiffusion-Plugin/docs/outpainting.gif filter=lfs diff=lfs merge=lfs -text
Auto-Photoshop-StableDiffusion-Plugin/docs/prompt_shortcut.gif filter=lfs diff=lfs merge=lfs -text
+Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png filter=lfs diff=lfs merge=lfs -text
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png b/Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png
new file mode 100644
index 0000000000000000000000000000000000000000..7db1d22b4c770224d6619813cdf975fce52eb21e
--- /dev/null
+++ b/Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bedc58968db1a83c84e57d94327c4e27f5cd6fbd93c45a59627a5257b06c8a47
+size 1104345
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1d05e4490b2ff7a3a82edee277a7130aee149d55
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version_output.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version_output.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..47761f788c50549355c1d2f58cffc3032cda3ebd
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version_output.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/download_model.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/download_model.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f6c052ccfca850008f7a87e7dfd8b45d2ec17209
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/download_model.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/extension_tab.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/extension_tab.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..a9e0dbe1d1164117cd11064ebbf220377a495c2e
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/extension_tab.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/extra_network.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/extra_network.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..25b2acbdb4afefb348f058a9d45fd8a090eda8ca
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/extra_network.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/get_one_model_info.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/get_one_model_info.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9de82e031c5320d45fef054026ada12f3026e776
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/get_one_model_info.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/model_card.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/model_card.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6ceb33f68f91f3bf3eef625bc18ee427e1002456
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/model_card.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/model_info_file.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/model_info_file.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..d9e36c8e69a3feb90034a2c205a4d4c27aa43bd7
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/model_info_file.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/other_setting.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/other_setting.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9b9a342410e65bcf616978530c79a636ab173f4e
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/other_setting.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/refresh_ch.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/refresh_ch.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..93b4ab70f94ff1718cebc3880f34c4dce45ddfc8
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/refresh_ch.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/thumb_mode.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/thumb_mode.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1044e68cf89c9f0faa1e1f74a8a7cbb5d97809d6
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/thumb_mode.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..59ccb37188850686ef48248d2f565098403a0d99
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..76acd20220c8f3c22619e5d5f4541ce2aeefbeb4
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..0f231f6f8b9fb981fd9b52ff04aa44f26abf4ddb
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..fc032c496932463b958444048200dd43068984ca
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6ffd6197fe4889e946dd2c24d2192b8940c1bddc
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part1.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..26567730c1a3253244d9a8230b90456add779b69
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part1.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part2.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..54b9a2cd28a2f57e43a3f2f0b92c68dbd1c52123
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part2.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part3.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part3.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..8c0d4ce269af46779bdf4981a7bebf40a4768559
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part3.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part4.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part4.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9ddad8e72b21e5148b14a1e4170a6a774a8d2c4c
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part4.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/did_not_relaunch_sdwebui.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/did_not_relaunch_sdwebui.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..984228d4cffa6367e5191bb756350f7611b42091
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/did_not_relaunch_sdwebui.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/do_not_even_use_this_ext.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/do_not_even_use_this_ext.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e4be133af1c95e21fce1e6b9b4a26f312722ccb5
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/do_not_even_use_this_ext.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_scan_model.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_scan_model.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..fc20a79fd6c096d1a2df8d612d06f89a323cdbbd
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_scan_model.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_update_sdwebui.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_update_sdwebui.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..3fb704c1f4ce3ef9ea423d2bab3eeff9eab7fe4f
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_update_sdwebui.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/request_a_feature_it_already_has.jpg b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/request_a_feature_it_already_has.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2e1eab1df869661eab9475344d28eed4c59129e0
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/request_a_feature_it_already_has.jpg differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/javascript/civitai_helper.js b/Stable-Diffusion-Webui-Civitai-Helper/javascript/civitai_helper.js
new file mode 100644
index 0000000000000000000000000000000000000000..4454d0d4d4b73ec0436beb570e65f943aebfe97f
--- /dev/null
+++ b/Stable-Diffusion-Webui-Civitai-Helper/javascript/civitai_helper.js
@@ -0,0 +1,728 @@
+"use strict";
+
+
+function ch_convert_file_path_to_url(path){
+ let prefix = "file=";
+ let path_to_url = path.replaceAll('\\', '/');
+ return prefix+path_to_url;
+}
+
+function ch_img_node_str(path){
+ return ``;
+}
+
+
+function ch_gradio_version(){
+ let foot = gradioApp().getElementById("footer");
+ if (!foot){return null;}
+
+ let versions = foot.querySelector(".versions");
+ if (!versions){return null;}
+
+ if (versions.innerHTML.indexOf("gradio: 3.16.2")>0) {
+ return "3.16.2";
+ } else {
+ return "3.23.0";
+ }
+
+}
+
+
+// send msg to python side by filling a hidden text box
+// then will click a button to trigger an action
+// msg is an object, not a string, will be stringify in this function
+function send_ch_py_msg(msg){
+ console.log("run send_ch_py_msg")
+ let js_msg_txtbox = gradioApp().querySelector("#ch_js_msg_txtbox textarea");
+ if (js_msg_txtbox && msg) {
+ // fill to msg box
+ js_msg_txtbox.value = JSON.stringify(msg);
+ js_msg_txtbox.dispatchEvent(new Event("input"));
+ }
+
+}
+
+// get msg from python side from a hidden textbox
+// normally this is an old msg, need to wait for a new msg
+function get_ch_py_msg(){
+ console.log("run get_ch_py_msg")
+ const py_msg_txtbox = gradioApp().querySelector("#ch_py_msg_txtbox textarea");
+ if (py_msg_txtbox && py_msg_txtbox.value) {
+ console.log("find py_msg_txtbox");
+ console.log("py_msg_txtbox value: ");
+ console.log(py_msg_txtbox.value)
+ return py_msg_txtbox.value
+ } else {
+ return ""
+ }
+}
+
+
+// get msg from python side from a hidden textbox
+// it will try once in every sencond, until it reach the max try times
+const get_new_ch_py_msg = (max_count=3) => new Promise((resolve, reject) => {
+ console.log("run get_new_ch_py_msg")
+
+ let count = 0;
+ let new_msg = "";
+ let find_msg = false;
+ const interval = setInterval(() => {
+ const py_msg_txtbox = gradioApp().querySelector("#ch_py_msg_txtbox textarea");
+ count++;
+
+ if (py_msg_txtbox && py_msg_txtbox.value) {
+ console.log("find py_msg_txtbox");
+ console.log("py_msg_txtbox value: ");
+ console.log(py_msg_txtbox.value)
+
+ new_msg = py_msg_txtbox.value
+ if (new_msg != "") {
+ find_msg=true
+ }
+ }
+
+ if (find_msg) {
+ //clear msg in both sides
+ py_msg_txtbox.value = "";
+ py_msg_txtbox.dispatchEvent(new Event("input"));
+
+ resolve(new_msg);
+ clearInterval(interval);
+ } else if (count > max_count) {
+ //clear msg in both sides
+ py_msg_txtbox.value = "";
+ py_msg_txtbox.dispatchEvent(new Event("input"));
+
+ reject('');
+ clearInterval(interval);
+ }
+
+ }, 1000);
+})
+
+
+function getActiveTabType() {
+ const currentTab = get_uiCurrentTabContent();
+ switch (currentTab.id) {
+ case "tab_txt2img":
+ return "txt2img";
+ case "tab_img2img":
+ return "img2img";
+ }
+ return null;
+}
+
+
+
+function getActivePrompt() {
+ const currentTab = get_uiCurrentTabContent();
+ switch (currentTab.id) {
+ case "tab_txt2img":
+ return currentTab.querySelector("#txt2img_prompt textarea");
+ case "tab_img2img":
+ return currentTab.querySelector("#img2img_prompt textarea");
+ }
+ return null;
+}
+
+function getActiveNegativePrompt() {
+ const currentTab = get_uiCurrentTabContent();
+ switch (currentTab.id) {
+ case "tab_txt2img":
+ return currentTab.querySelector("#txt2img_neg_prompt textarea");
+ case "tab_img2img":
+ return currentTab.querySelector("#img2img_neg_prompt textarea");
+ }
+ return null;
+}
+
+
+//button's click function
+async function open_model_url(event, model_type, search_term){
+ console.log("start open_model_url");
+
+ //get hidden components of extension
+ let js_open_url_btn = gradioApp().getElementById("ch_js_open_url_btn");
+ if (!js_open_url_btn) {
+ return
+ }
+
+
+ //msg to python side
+ let msg = {
+ "action": "",
+ "model_type": "",
+ "search_term": "",
+ "prompt": "",
+ "neg_prompt": "",
+ }
+
+
+ msg["action"] = "open_url";
+ msg["model_type"] = model_type;
+ msg["search_term"] = search_term;
+ msg["prompt"] = "";
+ msg["neg_prompt"] = "";
+
+ // fill to msg box
+ send_ch_py_msg(msg)
+
+ //click hidden button
+ js_open_url_btn.click();
+
+ // stop parent event
+ event.stopPropagation()
+ event.preventDefault()
+
+ //check response msg from python
+ let new_py_msg = await get_new_ch_py_msg();
+ console.log("new_py_msg:");
+ console.log(new_py_msg);
+
+ //check msg
+ if (new_py_msg) {
+ let py_msg_json = JSON.parse(new_py_msg);
+ //check for url
+ if (py_msg_json && py_msg_json.content) {
+ if (py_msg_json.content.url) {
+ window.open(py_msg_json.content.url, "_blank");
+ }
+
+ }
+
+
+ }
+
+
+ console.log("end open_model_url");
+
+
+}
+
+function add_trigger_words(event, model_type, search_term){
+ console.log("start add_trigger_words");
+
+ //get hidden components of extension
+ let js_add_trigger_words_btn = gradioApp().getElementById("ch_js_add_trigger_words_btn");
+ if (!js_add_trigger_words_btn) {
+ return
+ }
+
+
+ //msg to python side
+ let msg = {
+ "action": "",
+ "model_type": "",
+ "search_term": "",
+ "prompt": "",
+ "neg_prompt": "",
+ }
+
+ msg["action"] = "add_trigger_words";
+ msg["model_type"] = model_type;
+ msg["search_term"] = search_term;
+ msg["neg_prompt"] = "";
+
+ // get active prompt
+ let act_prompt = getActivePrompt();
+ msg["prompt"] = act_prompt.value;
+
+ // fill to msg box
+ send_ch_py_msg(msg)
+
+ //click hidden button
+ js_add_trigger_words_btn.click();
+
+ console.log("end add_trigger_words");
+
+ event.stopPropagation()
+ event.preventDefault()
+
+
+}
+
+function use_preview_prompt(event, model_type, search_term){
+ console.log("start use_preview_prompt");
+
+ //get hidden components of extension
+ let js_use_preview_prompt_btn = gradioApp().getElementById("ch_js_use_preview_prompt_btn");
+ if (!js_use_preview_prompt_btn) {
+ return
+ }
+
+ //msg to python side
+ let msg = {
+ "action": "",
+ "model_type": "",
+ "search_term": "",
+ "prompt": "",
+ "neg_prompt": "",
+ }
+
+ msg["action"] = "use_preview_prompt";
+ msg["model_type"] = model_type;
+ msg["search_term"] = search_term;
+
+ // get active prompt
+ let act_prompt = getActivePrompt();
+ msg["prompt"] = act_prompt.value;
+
+ // get active neg prompt
+ let neg_prompt = getActiveNegativePrompt();
+ msg["neg_prompt"] = neg_prompt.value;
+
+ // fill to msg box
+ send_ch_py_msg(msg)
+
+ //click hidden button
+ js_use_preview_prompt_btn.click();
+
+ console.log("end use_preview_prompt");
+
+ event.stopPropagation()
+ event.preventDefault()
+
+}
+
+
+
+// download model's new version into SD at python side
+function ch_dl_model_new_version(event, model_path, version_id, download_url){
+ console.log("start ch_dl_model_new_version");
+
+ // must confirm before downloading
+ let dl_confirm = "\nConfirm to download.\n\nCheck Download Model Section's log and console log for detail.";
+ if (!confirm(dl_confirm)) {
+ return
+ }
+
+ //get hidden components of extension
+ let js_dl_model_new_version_btn = gradioApp().getElementById("ch_js_dl_model_new_version_btn");
+ if (!js_dl_model_new_version_btn) {
+ return
+ }
+
+ //msg to python side
+ let msg = {
+ "action": "",
+ "model_path": "",
+ "version_id": "",
+ "download_url": "",
+ }
+
+ msg["action"] = "dl_model_new_version";
+ msg["model_path"] = model_path;
+ msg["version_id"] = version_id;
+ msg["download_url"] = download_url;
+
+ // fill to msg box
+ send_ch_py_msg(msg)
+
+ //click hidden button
+ js_dl_model_new_version_btn.click();
+
+ console.log("end dl_model_new_version");
+
+ event.stopPropagation()
+ event.preventDefault()
+
+
+}
+
+
+onUiLoaded(() => {
+
+ //get gradio version
+ let gradio_ver = ch_gradio_version();
+ console.log("gradio_ver:" + gradio_ver);
+
+ // get all extra network tabs
+ let tab_prefix_list = ["txt2img", "img2img"];
+ let model_type_list = ["textual_inversion", "hypernetworks", "checkpoints", "lora"];
+ let cardid_suffix = "cards";
+
+ //get init py msg
+ // let init_py_msg_str = get_ch_py_msg();
+ // let extension_path = "";
+ // if (!init_py_msg_str) {
+ // console.log("Can not get init_py_msg");
+ // } else {
+ // init_py_msg = JSON.parse(init_py_msg_str);
+ // if (init_py_msg) {
+ // extension_path = init_py_msg.extension_path;
+ // console.log("get extension path: " + extension_path);
+ // }
+ // }
+
+ // //icon image node as string
+ // function icon(icon_name){
+ // let icon_path = extension_path+"/icon/"+icon_name;
+ // return ch_img_node_str(icon_path);
+ // }
+
+
+ // update extra network tab pages' cards
+ // * replace "replace preview" text button into an icon
+ // * add 3 button to each card:
+ // - open model url 🌐
+ // - add trigger words 💡
+ // - use preview image's prompt 🏷️
+ // notice: javascript can not get response from python side
+ // so, these buttons just sent request to python
+ // then, python side gonna open url and update prompt text box, without telling js side.
+ function update_card_for_civitai(){
+
+ //css
+ let btn_margin = "0px 5px";
+ let btn_fontSize = "200%";
+ let btn_thumb_fontSize = "100%";
+ let btn_thumb_display = "inline";
+ let btn_thumb_pos = "static";
+ let btn_thumb_backgroundImage = "none";
+ let btn_thumb_background = "rgba(0, 0, 0, 0.8)";
+
+ let ch_btn_txts = ['🌐', '💡', '🏷️'];
+ let replace_preview_text = getTranslation("replace preview");
+ if (!replace_preview_text) {
+ replace_preview_text = "replace preview";
+ }
+
+
+
+ // get component
+ let ch_always_display_ckb = gradioApp().querySelector("#ch_always_display_ckb input");
+ let ch_show_btn_on_thumb_ckb = gradioApp().querySelector("#ch_show_btn_on_thumb_ckb input");
+ let ch_always_display = false;
+ let ch_show_btn_on_thumb = false;
+ if (ch_always_display_ckb) {
+ ch_always_display = ch_always_display_ckb.checked;
+ }
+ if (ch_show_btn_on_thumb_ckb) {
+ ch_show_btn_on_thumb = ch_show_btn_on_thumb_ckb.checked;
+ }
+
+
+ //change all "replace preview" into an icon
+ let extra_network_id = "";
+ let extra_network_node = null;
+ let metadata_button = null;
+ let additional_node = null;
+ let replace_preview_btn = null;
+ let ul_node = null;
+ let search_term_node = null;
+ let search_term = "";
+ let model_type = "";
+ let cards = null;
+ let need_to_add_buttons = false;
+ let is_thumb_mode = false;
+
+ //get current tab
+ let active_tab_type = getActiveTabType();
+ if (!active_tab_type){active_tab_type = "txt2img";}
+
+ for (const tab_prefix of tab_prefix_list) {
+ if (tab_prefix != active_tab_type) {continue;}
+
+
+ //find out current selected model type tab
+ let active_extra_tab_type = "";
+ let extra_tabs = gradioApp().getElementById(tab_prefix+"_extra_tabs");
+ if (!extra_tabs) {console.log("can not find extra_tabs: " + tab_prefix+"_extra_tabs");}
+
+ //get active extratab
+ const active_extra_tab = Array.from(get_uiCurrentTabContent().querySelectorAll('.extra-network-cards,.extra-network-thumbs'))
+ .find(el => el.closest('.tabitem').style.display === 'block')
+ ?.id.match(/^(txt2img|img2img)_(.+)_cards$/)[2]
+
+
+ console.log("found active tab: " + active_extra_tab);
+
+ switch (active_extra_tab) {
+ case "textual_inversion":
+ active_extra_tab_type = "ti";
+ break;
+ case "hypernetworks":
+ active_extra_tab_type = "hyper";
+ break;
+ case "checkpoints":
+ active_extra_tab_type = "ckp";
+ break;
+ case "lora":
+ active_extra_tab_type = "lora";
+ break;
+ }
+
+
+ for (const js_model_type of model_type_list) {
+ //get model_type for python side
+ switch (js_model_type) {
+ case "textual_inversion":
+ model_type = "ti";
+ break;
+ case "hypernetworks":
+ model_type = "hyper";
+ break;
+ case "checkpoints":
+ model_type = "ckp";
+ break;
+ case "lora":
+ model_type = "lora";
+ break;
+ }
+
+ if (!model_type) {
+ console.log("can not get model_type from: " + js_model_type);
+ continue;
+ }
+
+
+ //only handle current sub-tab
+ if (model_type != active_extra_tab_type) {
+ continue;
+ }
+
+ console.log("handle active extra tab");
+
+
+ extra_network_id = tab_prefix+"_"+js_model_type+"_"+cardid_suffix;
+ // console.log("searching extra_network_node: " + extra_network_id);
+ extra_network_node = gradioApp().getElementById(extra_network_id);
+ // check if extr network is under thumbnail mode
+ is_thumb_mode = false
+ if (extra_network_node) {
+ if (extra_network_node.className == "extra-network-thumbs") {
+ console.log(extra_network_id + " is in thumbnail mode");
+ is_thumb_mode = true;
+ // if (!ch_show_btn_on_thumb) {continue;}
+ }
+ } else {
+ console.log("can not find extra_network_node: " + extra_network_id);
+ continue;
+ }
+ // console.log("find extra_network_node: " + extra_network_id);
+
+ // get all card nodes
+ cards = extra_network_node.querySelectorAll(".card");
+ for (let card of cards) {
+ //metadata_buttoncard
+ metadata_button = card.querySelector(".metadata-button");
+ //additional node
+ additional_node = card.querySelector(".actions .additional");
+ //get ul node, which is the parent of all buttons
+ ul_node = card.querySelector(".actions .additional ul");
+ // replace preview text button
+ replace_preview_btn = card.querySelector(".actions .additional a");
+
+ // check thumb mode
+ if (is_thumb_mode) {
+ additional_node.style.display = null;
+
+ if (ch_show_btn_on_thumb) {
+ ul_node.style.background = btn_thumb_background;
+ } else {
+ //reset
+ ul_node.style.background = null;
+ // console.log("remove existed buttons");
+ // remove existed buttons
+ if (ul_node) {
+ // find all .a child nodes
+ let atags = ul_node.querySelectorAll("a");
+
+ for (let atag of atags) {
+ //reset display
+ atag.style.display = null;
+ //remove extension's button
+ if (ch_btn_txts.indexOf(atag.innerHTML)>=0) {
+ //need to remove
+ ul_node.removeChild(atag);
+ } else {
+ //do not remove, just reset
+ atag.innerHTML = replace_preview_text;
+ atag.style.display = null;
+ atag.style.fontSize = null;
+ atag.style.position = null;
+ atag.style.backgroundImage = null;
+ }
+ }
+
+ //also remove br tag in ul
+ let brtag = ul_node.querySelector("br");
+ if (brtag) {
+ ul_node.removeChild(brtag);
+ }
+
+ }
+ //just reset and remove nodes, do nothing else
+ continue;
+
+ }
+
+ } else {
+ // full preview mode
+ if (ch_always_display) {
+ additional_node.style.display = "block";
+ } else {
+ additional_node.style.display = null;
+ }
+
+ // remove br tag
+ let brtag = ul_node.querySelector("br");
+ if (brtag) {
+ ul_node.removeChild(brtag);
+ }
+
+ }
+
+ // change replace preview text button into icon
+ if (replace_preview_btn) {
+ if (replace_preview_btn.innerHTML !== "🖼️") {
+ need_to_add_buttons = true;
+ replace_preview_btn.innerHTML = "🖼️";
+ if (!is_thumb_mode) {
+ replace_preview_btn.style.fontSize = btn_fontSize;
+ replace_preview_btn.style.margin = btn_margin;
+ } else {
+ replace_preview_btn.style.display = btn_thumb_display;
+ replace_preview_btn.style.fontSize = btn_thumb_fontSize;
+ replace_preview_btn.style.position = btn_thumb_pos;
+ replace_preview_btn.style.backgroundImage = btn_thumb_backgroundImage;
+ }
+
+ }
+ }
+
+ if (!need_to_add_buttons) {
+ continue;
+ }
+
+
+ // search_term node
+ // search_term = subfolder path + model name + ext
+ search_term_node = card.querySelector(".actions .additional .search_term");
+ if (!search_term_node){
+ console.log("can not find search_term node for cards in " + extra_network_id);
+ continue;
+ }
+
+ // get search_term
+ search_term = search_term_node.innerHTML;
+ if (!search_term) {
+ console.log("search_term is empty for cards in " + extra_network_id);
+ continue;
+ }
+
+
+
+ // if (is_thumb_mode) {
+ // ul_node.style.background = btn_thumb_background;
+ // }
+
+ // then we need to add 3 buttons to each ul node:
+ let open_url_node = document.createElement("a");
+ open_url_node.href = "#";
+ open_url_node.innerHTML = "🌐";
+ if (!is_thumb_mode) {
+ open_url_node.style.fontSize = btn_fontSize;
+ open_url_node.style.margin = btn_margin;
+ } else {
+ open_url_node.style.display = btn_thumb_display;
+ open_url_node.style.fontSize = btn_thumb_fontSize;
+ open_url_node.style.position = btn_thumb_pos;
+ open_url_node.style.backgroundImage = btn_thumb_backgroundImage;
+ }
+ open_url_node.title = "Open this model's civitai url";
+ open_url_node.setAttribute("onclick","open_model_url(event, '"+model_type+"', '"+search_term+"')");
+
+ let add_trigger_words_node = document.createElement("a");
+ add_trigger_words_node.href = "#";
+ add_trigger_words_node.innerHTML = "💡";
+ if (!is_thumb_mode) {
+ add_trigger_words_node.style.fontSize = btn_fontSize;
+ add_trigger_words_node.style.margin = btn_margin;
+ } else {
+ add_trigger_words_node.style.display = btn_thumb_display;
+ add_trigger_words_node.style.fontSize = btn_thumb_fontSize;
+ add_trigger_words_node.style.position = btn_thumb_pos;
+ add_trigger_words_node.style.backgroundImage = btn_thumb_backgroundImage;
+ }
+
+ add_trigger_words_node.title = "Add trigger words to prompt";
+ add_trigger_words_node.setAttribute("onclick","add_trigger_words(event, '"+model_type+"', '"+search_term+"')");
+
+ let use_preview_prompt_node = document.createElement("a");
+ use_preview_prompt_node.href = "#";
+ use_preview_prompt_node.innerHTML = "🏷️";
+ if (!is_thumb_mode) {
+ use_preview_prompt_node.style.fontSize = btn_fontSize;
+ use_preview_prompt_node.style.margin = btn_margin;
+ } else {
+ use_preview_prompt_node.style.display = btn_thumb_display;
+ use_preview_prompt_node.style.fontSize = btn_thumb_fontSize;
+ use_preview_prompt_node.style.position = btn_thumb_pos;
+ use_preview_prompt_node.style.backgroundImage = btn_thumb_backgroundImage;
+ }
+ use_preview_prompt_node.title = "Use prompt from preview image";
+ use_preview_prompt_node.setAttribute("onclick","use_preview_prompt(event, '"+model_type+"', '"+search_term+"')");
+
+ //add to card
+ ul_node.appendChild(open_url_node);
+ //add br if metadata_button exists
+ if (is_thumb_mode && metadata_button) {
+ ul_node.appendChild(document.createElement("br"));
+ }
+ ul_node.appendChild(add_trigger_words_node);
+ ul_node.appendChild(use_preview_prompt_node);
+
+
+
+
+ }
+
+
+ }
+ }
+
+
+ }
+
+
+ let tab_id = ""
+ let extra_tab = null;
+ let extra_toolbar = null;
+ let extra_network_refresh_btn = null;
+ //add refresh button to extra network's toolbar
+ for (let prefix of tab_prefix_list) {
+ tab_id = prefix + "_extra_tabs";
+ extra_tab = gradioApp().getElementById(tab_id);
+
+ //get toolbar
+ //get Refresh button
+ extra_network_refresh_btn = gradioApp().getElementById(prefix+"_extra_refresh");
+
+
+ if (!extra_network_refresh_btn){
+ console.log("can not get extra network refresh button for " + tab_id);
+ continue;
+ }
+
+ // add refresh button to toolbar
+ let ch_refresh = document.createElement("button");
+ ch_refresh.innerHTML = "🔁";
+ ch_refresh.title = "Refresh Civitai Helper's additional buttons";
+ ch_refresh.className = "lg secondary gradio-button";
+ ch_refresh.style.fontSize = "200%";
+ ch_refresh.onclick = update_card_for_civitai;
+
+ extra_network_refresh_btn.parentNode.appendChild(ch_refresh);
+
+ }
+
+
+ //run it once
+ update_card_for_civitai();
+
+
+});
+
+
+
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/__pycache__/civitai_helper.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/__pycache__/civitai_helper.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1b8e7d60ca2ca6e4ad63551b283ac6bee4d287cd
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/__pycache__/civitai_helper.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__init__.py b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/__init__.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7cbebd2239960418da2f273fd0d37b51f6deabb6
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/__init__.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/civitai.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/civitai.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a608bf672f340c81bdf172b29eb2bfbafa68c398
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/civitai.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/downloader.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/downloader.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..953146531930bda033b23b935aec5c5ed27a1f03
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/downloader.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/js_action_civitai.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/js_action_civitai.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5eb5eb6c17cba5041e477c7151f2d78ff9bd5b07
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/js_action_civitai.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b8ec10c4c27327c3ba33c29fed1ec391d8bfc704
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model_action_civitai.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model_action_civitai.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..60ffad49461a9ad7c499d77bd0586087363d3482
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model_action_civitai.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/msg_handler.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/msg_handler.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..eaf80646e4ea16c98a5acd90b33303dcf009966e
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/msg_handler.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/setting.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/setting.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..96a07b10fcca5049da041f6da08516eef0754960
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/setting.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/util.cpython-310.pyc b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/util.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9ca40074c6ee1392499159fbf2cd3816d1f91dae
Binary files /dev/null and b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/util.cpython-310.pyc differ
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/civitai.py b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/civitai.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a15ae745d6d98b75a177d8a3deb1c897fa2a2c3
--- /dev/null
+++ b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/civitai.py
@@ -0,0 +1,612 @@
+# -*- coding: UTF-8 -*-
+# handle msg between js and python side
+import os
+import time
+import json
+import re
+import requests
+from . import util
+from . import model
+from . import setting
+
+suffix = ".civitai"
+
+url_dict = {
+ "modelPage":"https://civitai.com/models/",
+ "modelId": "https://civitai.com/api/v1/models/",
+ "modelVersionId": "https://civitai.com/api/v1/model-versions/",
+ "hash": "https://civitai.com/api/v1/model-versions/by-hash/"
+}
+
+model_type_dict = {
+ "Checkpoint": "ckp",
+ "TextualInversion": "ti",
+ "Hypernetwork": "hyper",
+ "LORA": "lora",
+ "LoCon": "lora",
+}
+
+
+
+# get image with full size
+# width is in number, not string
+# return: url str
+def get_full_size_image_url(image_url, width):
+ return re.sub('/width=\d+/', '/width=' + str(width) + '/', image_url)
+
+
+# use this sha256 to get model info from civitai
+# return: model info dict
+def get_model_info_by_hash(hash:str):
+ util.printD("Request model info from civitai")
+
+ if not hash:
+ util.printD("hash is empty")
+ return
+
+ r = requests.get(url_dict["hash"]+hash, headers=util.def_headers, proxies=util.proxies)
+ if not r.ok:
+ if r.status_code == 404:
+ # this is not a civitai model
+ util.printD("Civitai does not have this model")
+ return {}
+ else:
+ util.printD("Get error code: " + str(r.status_code))
+ util.printD(r.text)
+ return
+
+ # try to get content
+ content = None
+ try:
+ content = r.json()
+ except Exception as e:
+ util.printD("Parse response json failed")
+ util.printD(str(e))
+ util.printD("response:")
+ util.printD(r.text)
+ return
+
+ if not content:
+ util.printD("error, content from civitai is None")
+ return
+
+ return content
+
+
+
+def get_model_info_by_id(id:str) -> dict:
+ util.printD("Request model info from civitai")
+
+ if not id:
+ util.printD("id is empty")
+ return
+
+ r = requests.get(url_dict["modelId"]+str(id), headers=util.def_headers, proxies=util.proxies)
+ if not r.ok:
+ if r.status_code == 404:
+ # this is not a civitai model
+ util.printD("Civitai does not have this model")
+ return {}
+ else:
+ util.printD("Get error code: " + str(r.status_code))
+ util.printD(r.text)
+ return
+
+ # try to get content
+ content = None
+ try:
+ content = r.json()
+ except Exception as e:
+ util.printD("Parse response json failed")
+ util.printD(str(e))
+ util.printD("response:")
+ util.printD(r.text)
+ return
+
+ if not content:
+ util.printD("error, content from civitai is None")
+ return
+
+ return content
+
+
+def get_version_info_by_version_id(id:str) -> dict:
+ util.printD("Request version info from civitai")
+
+ if not id:
+ util.printD("id is empty")
+ return
+
+ r = requests.get(url_dict["modelVersionId"]+str(id), headers=util.def_headers, proxies=util.proxies)
+ if not r.ok:
+ if r.status_code == 404:
+ # this is not a civitai model
+ util.printD("Civitai does not have this model version")
+ return {}
+ else:
+ util.printD("Get error code: " + str(r.status_code))
+ util.printD(r.text)
+ return
+
+ # try to get content
+ content = None
+ try:
+ content = r.json()
+ except Exception as e:
+ util.printD("Parse response json failed")
+ util.printD(str(e))
+ util.printD("response:")
+ util.printD(r.text)
+ return
+
+ if not content:
+ util.printD("error, content from civitai is None")
+ return
+
+ return content
+
+
+def get_version_info_by_model_id(id:str) -> dict:
+
+ model_info = get_model_info_by_id(id)
+ if not model_info:
+ util.printD(f"Failed to get model info by id: {id}")
+ return
+
+ # check content to get version id
+ if "modelVersions" not in model_info.keys():
+ util.printD("There is no modelVersions in this model_info")
+ return
+
+ if not model_info["modelVersions"]:
+ util.printD("modelVersions is None")
+ return
+
+ if len(model_info["modelVersions"])==0:
+ util.printD("modelVersions is Empty")
+ return
+
+ def_version = model_info["modelVersions"][0]
+ if not def_version:
+ util.printD("default version is None")
+ return
+
+ if "id" not in def_version.keys():
+ util.printD("default version has no id")
+ return
+
+ version_id = def_version["id"]
+
+ if not version_id:
+ util.printD("default version's id is None")
+ return
+
+ # get version info
+ version_info = get_version_info_by_version_id(str(version_id))
+ if not version_info:
+ util.printD(f"Failed to get version info by version_id: {version_id}")
+ return
+
+ return version_info
+
+
+
+
+# get model info file's content by model type and search_term
+# parameter: model_type, search_term
+# return: model_info
+def load_model_info_by_search_term(model_type, search_term):
+ util.printD(f"Load model info of {search_term} in {model_type}")
+ if model_type not in model.folders.keys():
+ util.printD("unknow model type: " + model_type)
+ return
+
+ # search_term = subfolderpath + model name + ext. And it always start with a / even there is no sub folder
+ base, ext = os.path.splitext(search_term)
+ model_info_base = base
+ if base[:1] == "/":
+ model_info_base = base[1:]
+
+ model_folder = model.folders[model_type]
+ model_info_filename = model_info_base + suffix + model.info_ext
+ model_info_filepath = os.path.join(model_folder, model_info_filename)
+
+ if not os.path.isfile(model_info_filepath):
+ util.printD("Can not find model info file: " + model_info_filepath)
+ return
+
+ return model.load_model_info(model_info_filepath)
+
+
+
+
+
+# get model file names by model type
+# parameter: model_type - string
+# parameter: filter - dict, which kind of model you need
+# return: model name list
+def get_model_names_by_type_and_filter(model_type:str, filter:dict) -> list:
+
+ model_folder = model.folders[model_type]
+
+ # set filter
+ # only get models don't have a civitai info file
+ no_info_only = False
+ empty_info_only = False
+
+ if filter:
+ if "no_info_only" in filter.keys():
+ no_info_only = filter["no_info_only"]
+ if "empty_info_only" in filter.keys():
+ empty_info_only = filter["empty_info_only"]
+
+
+
+ # get information from filter
+ # only get those model names don't have a civitai model info file
+ model_names = []
+ for root, dirs, files in os.walk(model_folder, followlinks=True):
+ for filename in files:
+ item = os.path.join(root, filename)
+ # check extension
+ base, ext = os.path.splitext(item)
+ if ext in model.exts:
+ # find a model
+
+ # check filter
+ if no_info_only:
+ # check model info file
+ info_file = base + suffix + model.info_ext
+ if os.path.isfile(info_file):
+ continue
+
+ if empty_info_only:
+ # check model info file
+ info_file = base + suffix + model.info_ext
+ if os.path.isfile(info_file):
+ # load model info
+ model_info = model.load_model_info(info_file)
+ # check content
+ if model_info:
+ if "id" in model_info.keys():
+ # find a non-empty model info file
+ continue
+
+ model_names.append(filename)
+
+
+ return model_names
+
+def get_model_names_by_input(model_type, empty_info_only):
+ return get_model_names_by_type_and_filter(model_type, {"empty_info_only":empty_info_only})
+
+
+# get id from url
+def get_model_id_from_url(url:str) -> str:
+ util.printD("Run get_model_id_from_url")
+ id = ""
+
+ if not url:
+ util.printD("url or model id can not be empty")
+ return ""
+
+ if url.isnumeric():
+ # is already an id
+ id = str(url)
+ return id
+
+ s = re.sub("\\?.+$", "", url).split("/")
+ if len(s) < 2:
+ util.printD("url is not valid")
+ return ""
+
+ if s[-2].isnumeric():
+ id = s[-2]
+ elif s[-1].isnumeric():
+ id = s[-1]
+ else:
+ util.printD("There is no model id in this url")
+ return ""
+
+ return id
+
+
+# get preview image by model path
+# image will be saved to file, so no return
+def get_preview_image_by_model_path(model_path:str, max_size_preview, skip_nsfw_preview):
+ if not model_path:
+ util.printD("model_path is empty")
+ return
+
+ if not os.path.isfile(model_path):
+ util.printD("model_path is not a file: "+model_path)
+ return
+
+ base, ext = os.path.splitext(model_path)
+ first_preview = base+".png"
+ sec_preview = base+".preview.png"
+ info_file = base + suffix + model.info_ext
+
+ # check preview image
+ if not os.path.isfile(sec_preview):
+ # need to download preview image
+ util.printD("Checking preview image for model: " + model_path)
+ # load model_info file
+ if os.path.isfile(info_file):
+ model_info = model.load_model_info(info_file)
+ if not model_info:
+ util.printD("Model Info is empty")
+ return
+
+ if "images" in model_info.keys():
+ if model_info["images"]:
+ for img_dict in model_info["images"]:
+ if "nsfw" in img_dict.keys():
+ if img_dict["nsfw"]:
+ util.printD("This image is NSFW")
+ if skip_nsfw_preview:
+ util.printD("Skip NSFW image")
+ continue
+
+ if "url" in img_dict.keys():
+ img_url = img_dict["url"]
+ if max_size_preview:
+ # use max width
+ if "width" in img_dict.keys():
+ if img_dict["width"]:
+ img_url = get_full_size_image_url(img_url, img_dict["width"])
+
+ util.download_file(img_url, sec_preview)
+ # we only need 1 preview image
+ break
+
+
+
+# search local model by version id in 1 folder, no subfolder
+# return - model_info
+def search_local_model_info_by_version_id(folder:str, version_id:int) -> dict:
+ util.printD("Searching local model by version id")
+ util.printD("folder: " + folder)
+ util.printD("version_id: " + str(version_id))
+
+ if not folder:
+ util.printD("folder is none")
+ return
+
+ if not os.path.isdir(folder):
+ util.printD("folder is not a dir")
+ return
+
+ if not version_id:
+ util.printD("version_id is none")
+ return
+
+ # search civitai model info file
+ for filename in os.listdir(folder):
+ # check ext
+ base, ext = os.path.splitext(filename)
+ if ext == model.info_ext:
+ # find info file
+ if len(base) < 9:
+ # not a civitai info file
+ continue
+
+ if base[-8:] == suffix:
+ # find a civitai info file
+ path = os.path.join(folder, filename)
+ model_info = model.load_model_info(path)
+ if not model_info:
+ continue
+
+ if "id" not in model_info.keys():
+ continue
+
+ id = model_info["id"]
+ if not id:
+ continue
+
+ # util.printD(f"Compare version id, src: {id}, target:{version_id}")
+ if str(id) == str(version_id):
+ # find the one
+ return model_info
+
+
+ return
+
+
+
+
+
+# check new version for a model by model path
+# return (model_path, model_id, model_name, new_verion_id, new_version_name, description, download_url, img_url)
+def check_model_new_version_by_path(model_path:str, delay:float=1) -> tuple:
+ if not model_path:
+ util.printD("model_path is empty")
+ return
+
+ if not os.path.isfile(model_path):
+ util.printD("model_path is not a file: "+model_path)
+ return
+
+ # get model info file name
+ base, ext = os.path.splitext(model_path)
+ info_file = base + suffix + model.info_ext
+
+ if not os.path.isfile(info_file):
+ return
+
+ # get model info
+ model_info_file = model.load_model_info(info_file)
+ if not model_info_file:
+ return
+
+ if "id" not in model_info_file.keys():
+ return
+
+ local_version_id = model_info_file["id"]
+ if not local_version_id:
+ return
+
+ if "modelId" not in model_info_file.keys():
+ return
+
+ model_id = model_info_file["modelId"]
+ if not model_id:
+ return
+
+ # get model info by id from civitai
+ model_info = get_model_info_by_id(model_id)
+ # delay before next request, to prevent to be treat as DDoS
+ util.printD(f"delay:{delay} second")
+ time.sleep(delay)
+
+ if not model_info:
+ return
+
+ if "modelVersions" not in model_info.keys():
+ return
+
+ modelVersions = model_info["modelVersions"]
+ if not modelVersions:
+ return
+
+ if not len(modelVersions):
+ return
+
+ current_version = modelVersions[0]
+ if not current_version:
+ return
+
+ if "id" not in current_version.keys():
+ return
+
+ current_version_id = current_version["id"]
+ if not current_version_id:
+ return
+
+ util.printD(f"Compare version id, local: {local_version_id}, remote: {current_version_id} ")
+ if current_version_id == local_version_id:
+ return
+
+ model_name = ""
+ if "name" in model_info.keys():
+ model_name = model_info["name"]
+
+ if not model_name:
+ model_name = ""
+
+
+ new_version_name = ""
+ if "name" in current_version.keys():
+ new_version_name = current_version["name"]
+
+ if not new_version_name:
+ new_version_name = ""
+
+ description = ""
+ if "description" in current_version.keys():
+ description = current_version["description"]
+
+ if not description:
+ description = ""
+
+ downloadUrl = ""
+ if "downloadUrl" in current_version.keys():
+ downloadUrl = current_version["downloadUrl"]
+
+ if not downloadUrl:
+ downloadUrl = ""
+
+ # get 1 preview image
+ img_url = ""
+ if "images" in current_version.keys():
+ if current_version["images"]:
+ if current_version["images"][0]:
+ if "url" in current_version["images"][0].keys():
+ img_url = current_version["images"][0]["url"]
+ if not img_url:
+ img_url = ""
+
+
+
+ return (model_path, model_id, model_name, current_version_id, new_version_name, description, downloadUrl, img_url)
+
+
+
+
+# check model's new version
+# parameter: delay - float, how many seconds to delay between each request to civitai
+# return: new_versions - a list for all new versions, each one is (model_path, model_id, model_name, new_verion_id, new_version_name, description, download_url, img_url)
+def check_models_new_version_by_model_types(model_types:list, delay:float=1) -> list:
+ util.printD("Checking models' new version")
+
+ if not model_types:
+ return []
+
+ # check model types, which cloud be a string as 1 type
+ mts = []
+ if type(model_types) == str:
+ mts.append(model_types)
+ elif type(model_types) == list:
+ mts = model_types
+ else:
+ util.printD("Unknow model types:")
+ util.printD(model_types)
+ return []
+
+ # output is a markdown document string to show a list of new versions on UI
+ output = ""
+ # new version list
+ new_versions = []
+
+ # walk all models
+ for model_type, model_folder in model.folders.items():
+ if model_type not in mts:
+ continue
+
+ util.printD("Scanning path: " + model_folder)
+ for root, dirs, files in os.walk(model_folder, followlinks=True):
+ for filename in files:
+ # check ext
+ item = os.path.join(root, filename)
+ base, ext = os.path.splitext(item)
+ if ext in model.exts:
+ # find a model
+ r = check_model_new_version_by_path(item, delay)
+
+ if not r:
+ continue
+
+ model_path, model_id, model_name, current_version_id, new_version_name, description, downloadUrl, img_url = r
+ # check exist
+ if not current_version_id:
+ continue
+
+ # check this version id in list
+ is_already_in_list = False
+ for new_version in new_versions:
+ if current_version_id == new_version[3]:
+ # already in list
+ is_already_in_list = True
+ break
+
+ if is_already_in_list:
+ util.printD("New version is already in list")
+ continue
+
+ # search this new version id to check if this model is already downloaded
+ target_model_info = search_local_model_info_by_version_id(root, current_version_id)
+ if target_model_info:
+ util.printD("New version is already existed")
+ continue
+
+ # add to list
+ new_versions.append(r)
+
+
+
+
+ return new_versions
+
+
+
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/downloader.py b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/downloader.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b14ebc2cde6650337b4b3e1520337a66d6ea61a
--- /dev/null
+++ b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/downloader.py
@@ -0,0 +1,115 @@
+# -*- coding: UTF-8 -*-
+import sys
+import requests
+import os
+from . import util
+
+
+dl_ext = ".downloading"
+
+# disable ssl warning info
+requests.packages.urllib3.disable_warnings()
+
+# output is downloaded file path
+def dl(url, folder, filename, filepath):
+ util.printD("Start downloading from: " + url)
+ # get file_path
+ file_path = ""
+ if filepath:
+ file_path = filepath
+ else:
+ # if file_path is not in parameter, then folder must be in parameter
+ if not folder:
+ util.printD("folder is none")
+ return
+
+ if not os.path.isdir(folder):
+ util.printD("folder does not exist: "+folder)
+ return
+
+ if filename:
+ file_path = os.path.join(folder, filename)
+
+ # first request for header
+ rh = requests.get(url, stream=True, verify=False, headers=util.def_headers, proxies=util.proxies)
+ # get file size
+ total_size = 0
+ total_size = int(rh.headers['Content-Length'])
+ util.printD(f"File size: {total_size}")
+
+ # if file_path is empty, need to get file name from download url's header
+ if not file_path:
+ filename = ""
+ if "Content-Disposition" in rh.headers.keys():
+ cd = rh.headers["Content-Disposition"]
+ # Extract the filename from the header
+ # content of a CD: "attachment;filename=FileName.txt"
+ # in case "" is in CD filename's start and end, need to strip them out
+ filename = cd.split("=")[1].strip('"')
+ if not filename:
+ util.printD("Fail to get file name from Content-Disposition: " + cd)
+ return
+
+ if not filename:
+ util.printD("Can not get file name from download url's header")
+ return
+
+ # with folder and filename, now we have the full file path
+ file_path = os.path.join(folder, filename)
+
+
+ util.printD("Target file path: " + file_path)
+ base, ext = os.path.splitext(file_path)
+
+ # check if file is already exist
+ count = 2
+ new_base = base
+ while os.path.isfile(file_path):
+ util.printD("Target file already exist.")
+ # re-name
+ new_base = base + "_" + str(count)
+ file_path = new_base + ext
+ count += 1
+
+ # use a temp file for downloading
+ dl_file_path = new_base+dl_ext
+
+
+ util.printD(f"Downloading to temp file: {dl_file_path}")
+
+ # check if downloading file is exsited
+ downloaded_size = 0
+ if os.path.exists(dl_file_path):
+ downloaded_size = os.path.getsize(dl_file_path)
+
+ util.printD(f"Downloaded size: {downloaded_size}")
+
+ # create header range
+ headers = {'Range': 'bytes=%d-' % downloaded_size}
+ headers['User-Agent'] = util.def_headers['User-Agent']
+
+ # download with header
+ r = requests.get(url, stream=True, verify=False, headers=headers, proxies=util.proxies)
+
+ # write to file
+ with open(dl_file_path, "ab") as f:
+ for chunk in r.iter_content(chunk_size=1024):
+ if chunk:
+ downloaded_size += len(chunk)
+ f.write(chunk)
+ # force to write to disk
+ f.flush()
+
+ # progress
+ progress = int(50 * downloaded_size / total_size)
+ sys.stdout.reconfigure(encoding='utf-8')
+ sys.stdout.write("\r[%s%s] %d%%" % ('-' * progress, ' ' * (50 - progress), 100 * downloaded_size / total_size))
+ sys.stdout.flush()
+
+ print()
+
+ # rename file
+ os.rename(dl_file_path, file_path)
+ util.printD(f"File Downloaded to: {file_path}")
+ return file_path
+
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/js_action_civitai.py b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/js_action_civitai.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c818539e421329ed35a903ab08294a3553804c1
--- /dev/null
+++ b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/js_action_civitai.py
@@ -0,0 +1,256 @@
+# -*- coding: UTF-8 -*-
+# handle msg between js and python side
+import os
+import json
+import requests
+import webbrowser
+from . import util
+from . import model
+from . import civitai
+from . import msg_handler
+from . import downloader
+
+
+
+# get civitai's model url and open it in browser
+# parameter: model_type, search_term
+# output: python msg - will be sent to hidden textbox then picked by js side
+def open_model_url(msg, open_url_with_js):
+ util.printD("Start open_model_url")
+
+ output = ""
+ result = msg_handler.parse_js_msg(msg)
+ if not result:
+ util.printD("Parsing js ms failed")
+ return
+
+ model_type = result["model_type"]
+ search_term = result["search_term"]
+
+ model_info = civitai.load_model_info_by_search_term(model_type, search_term)
+ if not model_info:
+ util.printD(f"Failed to get model info for {model_type} {search_term}")
+ return ""
+
+ if "modelId" not in model_info.keys():
+ util.printD(f"Failed to get model id from info file for {model_type} {search_term}")
+ return ""
+
+ model_id = model_info["modelId"]
+ if not model_id:
+ util.printD(f"model id from info file of {model_type} {search_term} is None")
+ return ""
+
+ url = civitai.url_dict["modelPage"]+str(model_id)
+
+
+ # msg content for js
+ content = {
+ "url":""
+ }
+
+ if not open_url_with_js:
+ util.printD("Open Url: " + url)
+ # open url
+ webbrowser.open_new_tab(url)
+ else:
+ util.printD("Send Url to js")
+ content["url"] = url
+ output = msg_handler.build_py_msg("open_url", content)
+
+ util.printD("End open_model_url")
+ return output
+
+
+
+# add trigger words to prompt
+# parameter: model_type, search_term, prompt
+# return: [new_prompt, new_prompt] - new prompt with trigger words, return twice for txt2img and img2img
+def add_trigger_words(msg):
+ util.printD("Start add_trigger_words")
+
+ result = msg_handler.parse_js_msg(msg)
+ if not result:
+ util.printD("Parsing js ms failed")
+ return
+
+ model_type = result["model_type"]
+ search_term = result["search_term"]
+ prompt = result["prompt"]
+
+
+ model_info = civitai.load_model_info_by_search_term(model_type, search_term)
+ if not model_info:
+ util.printD(f"Failed to get model info for {model_type} {search_term}")
+ return [prompt, prompt]
+
+ if "trainedWords" not in model_info.keys():
+ util.printD(f"Failed to get trainedWords from info file for {model_type} {search_term}")
+ return [prompt, prompt]
+
+ trainedWords = model_info["trainedWords"]
+ if not trainedWords:
+ util.printD(f"No trainedWords from info file for {model_type} {search_term}")
+ return [prompt, prompt]
+
+ if len(trainedWords) == 0:
+ util.printD(f"trainedWords from info file for {model_type} {search_term} is empty")
+ return [prompt, prompt]
+
+ # get ful trigger words
+ trigger_words = ""
+ for word in trainedWords:
+ trigger_words = trigger_words + word + ", "
+
+ new_prompt = prompt + " " + trigger_words
+ util.printD("trigger_words: " + trigger_words)
+ util.printD("prompt: " + prompt)
+ util.printD("new_prompt: " + new_prompt)
+
+ util.printD("End add_trigger_words")
+
+ # add to prompt
+ return [new_prompt, new_prompt]
+
+
+
+# use preview image's prompt as prompt
+# parameter: model_type, model_name, prompt, neg_prompt
+# return: [new_prompt, new_neg_prompt, new_prompt, new_neg_prompt,] - return twice for txt2img and img2img
+def use_preview_image_prompt(msg):
+ util.printD("Start use_preview_image_prompt")
+
+ result = msg_handler.parse_js_msg(msg)
+ if not result:
+ util.printD("Parsing js ms failed")
+ return
+
+ model_type = result["model_type"]
+ search_term = result["search_term"]
+ prompt = result["prompt"]
+ neg_prompt = result["neg_prompt"]
+
+
+ model_info = civitai.load_model_info_by_search_term(model_type, search_term)
+ if not model_info:
+ util.printD(f"Failed to get model info for {model_type} {search_term}")
+ return [prompt, neg_prompt, prompt, neg_prompt]
+
+ if "images" not in model_info.keys():
+ util.printD(f"Failed to get images from info file for {model_type} {search_term}")
+ return [prompt, neg_prompt, prompt, neg_prompt]
+
+ images = model_info["images"]
+ if not images:
+ util.printD(f"No images from info file for {model_type} {search_term}")
+ return [prompt, neg_prompt, prompt, neg_prompt]
+
+ if len(images) == 0:
+ util.printD(f"images from info file for {model_type} {search_term} is empty")
+ return [prompt, neg_prompt, prompt, neg_prompt]
+
+ # get prompt from preview images' meta data
+ preview_prompt = ""
+ preview_neg_prompt = ""
+ for img in images:
+ if "meta" in img.keys():
+ if img["meta"]:
+ if "prompt" in img["meta"].keys():
+ if img["meta"]["prompt"]:
+ preview_prompt = img["meta"]["prompt"]
+
+ if "negativePrompt" in img["meta"].keys():
+ if img["meta"]["negativePrompt"]:
+ preview_neg_prompt = img["meta"]["negativePrompt"]
+
+ # we only need 1 prompt
+ if preview_prompt:
+ break
+
+ if not preview_prompt:
+ util.printD(f"There is no prompt of {model_type} {search_term} in its preview image")
+ return [prompt, neg_prompt, prompt, neg_prompt]
+
+ util.printD("End use_preview_image_prompt")
+
+ return [preview_prompt, preview_neg_prompt, preview_prompt, preview_neg_prompt]
+
+
+# download model's new verson by model path, version id and download url
+# output is a md log
+def dl_model_new_version(msg, max_size_preview, skip_nsfw_preview):
+ util.printD("Start dl_model_new_version")
+
+ output = ""
+
+ result = msg_handler.parse_js_msg(msg)
+ if not result:
+ output = "Parsing js ms failed"
+ util.printD(output)
+ return output
+
+ model_path = result["model_path"]
+ version_id = result["version_id"]
+ download_url = result["download_url"]
+
+ util.printD("model_path: " + model_path)
+ util.printD("version_id: " + str(version_id))
+ util.printD("download_url: " + download_url)
+
+ # check data
+ if not model_path:
+ output = "model_path is empty"
+ util.printD(output)
+ return output
+
+ if not version_id:
+ output = "version_id is empty"
+ util.printD(output)
+ return output
+
+ if not download_url:
+ output = "download_url is empty"
+ util.printD(output)
+ return output
+
+ if not os.path.isfile(model_path):
+ output = "model_path is not a file: "+ model_path
+ util.printD(output)
+ return output
+
+ # get model folder from model path
+ model_folder = os.path.dirname(model_path)
+
+ # no need to check when downloading new version, since checking new version is already checked
+ # check if this model is already existed
+ # r = civitai.search_local_model_info_by_version_id(model_folder, version_id)
+ # if r:
+ # output = "This model version is already existed"
+ # util.printD(output)
+ # return output
+
+ # download file
+ new_model_path = downloader.dl(download_url, model_folder, None, None)
+ if not new_model_path:
+ output = "Download failed, check console log for detail. Download url: " + download_url
+ util.printD(output)
+ return output
+
+ # get version info
+ version_info = civitai.get_version_info_by_version_id(version_id)
+ if not version_info:
+ output = "Model downloaded, but failed to get version info, check console log for detail. Model saved to: " + new_model_path
+ util.printD(output)
+ return output
+
+ # now write version info to file
+ base, ext = os.path.splitext(new_model_path)
+ info_file = base + civitai.suffix + model.info_ext
+ model.write_model_info(info_file, version_info)
+
+ # then, get preview image
+ civitai.get_preview_image_by_model_path(new_model_path, max_size_preview, skip_nsfw_preview)
+
+ output = "Done. Model downloaded to: " + new_model_path
+ util.printD(output)
+ return output
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model.py b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model.py
new file mode 100644
index 0000000000000000000000000000000000000000..729012d742738cab559a8836727fc8289c21f18e
--- /dev/null
+++ b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model.py
@@ -0,0 +1,120 @@
+# -*- coding: UTF-8 -*-
+# handle msg between js and python side
+import os
+import json
+from . import util
+from modules import shared
+
+
+# this is the default root path
+root_path = os.getcwd()
+
+# if command line arguement is used to change model folder,
+# then model folder is in absolute path, not based on this root path anymore.
+# so to make extension work with those absolute model folder paths, model folder also need to be in absolute path
+folders = {
+ "ti": os.path.join(root_path, "embeddings"),
+ "hyper": os.path.join(root_path, "models", "hypernetworks"),
+ "ckp": os.path.join(root_path, "models", "Stable-diffusion"),
+ "lora": os.path.join(root_path, "models", "Lora"),
+}
+
+exts = (".bin", ".pt", ".safetensors", ".ckpt")
+info_ext = ".info"
+vae_suffix = ".vae"
+
+
+# get cusomter model path
+def get_custom_model_folder():
+ util.printD("Get Custom Model Folder")
+
+ global folders
+
+ if shared.cmd_opts.embeddings_dir and os.path.isdir(shared.cmd_opts.embeddings_dir):
+ folders["ti"] = shared.cmd_opts.embeddings_dir
+
+ if shared.cmd_opts.hypernetwork_dir and os.path.isdir(shared.cmd_opts.hypernetwork_dir):
+ folders["hyper"] = shared.cmd_opts.hypernetwork_dir
+
+ if shared.cmd_opts.ckpt_dir and os.path.isdir(shared.cmd_opts.ckpt_dir):
+ folders["ckp"] = shared.cmd_opts.ckpt_dir
+
+ if shared.cmd_opts.lora_dir and os.path.isdir(shared.cmd_opts.lora_dir):
+ folders["lora"] = shared.cmd_opts.lora_dir
+
+
+
+
+
+# write model info to file
+def write_model_info(path, model_info):
+ util.printD("Write model info to file: " + path)
+ with open(os.path.realpath(path), 'w') as f:
+ f.write(json.dumps(model_info, indent=4))
+
+
+def load_model_info(path):
+ # util.printD("Load model info from file: " + path)
+ model_info = None
+ with open(os.path.realpath(path), 'r') as f:
+ try:
+ model_info = json.load(f)
+ except Exception as e:
+ util.printD("Selected file is not json: " + path)
+ util.printD(e)
+ return
+
+ return model_info
+
+
+# get model file names by model type
+# parameter: model_type - string
+# return: model name list
+def get_model_names_by_type(model_type:str) -> list:
+
+ model_folder = folders[model_type]
+
+ # get information from filter
+ # only get those model names don't have a civitai model info file
+ model_names = []
+ for root, dirs, files in os.walk(model_folder, followlinks=True):
+ for filename in files:
+ item = os.path.join(root, filename)
+ # check extension
+ base, ext = os.path.splitext(item)
+ if ext in exts:
+ # find a model
+ model_names.append(filename)
+
+
+ return model_names
+
+
+# return 2 values: (model_root, model_path)
+def get_model_path_by_type_and_name(model_type:str, model_name:str) -> str:
+ util.printD("Run get_model_path_by_type_and_name")
+ if model_type not in folders.keys():
+ util.printD("unknown model_type: " + model_type)
+ return
+
+ if not model_name:
+ util.printD("model name can not be empty")
+ return
+
+ folder = folders[model_type]
+
+ # model could be in subfolder, need to walk.
+ model_root = ""
+ model_path = ""
+ for root, dirs, files in os.walk(folder, followlinks=True):
+ for filename in files:
+ if filename == model_name:
+ # find model
+ model_root = root
+ model_path = os.path.join(root, filename)
+ return (model_root, model_path)
+
+ return
+
+
+
diff --git a/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model_action_civitai.py b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model_action_civitai.py
new file mode 100644
index 0000000000000000000000000000000000000000..f09f0752a406e772dcaa95b1ccec20ba9de39506
--- /dev/null
+++ b/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model_action_civitai.py
@@ -0,0 +1,511 @@
+# -*- coding: UTF-8 -*-
+# handle msg between js and python side
+import os
+import time
+from . import util
+from . import model
+from . import civitai
+from . import downloader
+
+
+# scan model to generate SHA256, then use this SHA256 to get model info from civitai
+# return output msg
+def scan_model(scan_model_types, max_size_preview, skip_nsfw_preview):
+ util.printD("Start scan_model")
+ output = ""
+
+ # check model types
+ if not scan_model_types:
+ output = "Model Types is None, can not scan."
+ util.printD(output)
+ return output
+
+ model_types = []
+ # check type if it is a string
+ if type(scan_model_types) == str:
+ model_types.append(scan_model_types)
+ else:
+ model_types = scan_model_types
+
+ model_count = 0
+ image_count = 0
+ # scan_log = ""
+ for model_type, model_folder in model.folders.items():
+ if model_type not in model_types:
+ continue
+
+ util.printD("Scanning path: " + model_folder)
+ for root, dirs, files in os.walk(model_folder, followlinks=True):
+ for filename in files:
+ # check ext
+ item = os.path.join(root, filename)
+ base, ext = os.path.splitext(item)
+ if ext in model.exts:
+ # ignore vae file
+ if len(base) > 4:
+ if base[-4:] == model.vae_suffix:
+ # find .vae
+ util.printD("This is a vae file: " + filename)
+ continue
+
+ # find a model
+ # get info file
+ info_file = base + civitai.suffix + model.info_ext
+ # check info file
+ if not os.path.isfile(info_file):
+ util.printD("Creating model info for: " + filename)
+ # get model's sha256
+ hash = util.gen_file_sha256(item)
+
+ if not hash:
+ output = "failed generating SHA256 for model:" + filename
+ util.printD(output)
+ return output
+
+ # use this sha256 to get model info from civitai
+ model_info = civitai.get_model_info_by_hash(hash)
+ # delay 1 second for ti
+ if model_type == "ti":
+ util.printD("Delay 1 second for TI")
+ time.sleep(1)
+
+ if model_info is None:
+ output = "Connect to Civitai API service failed. Wait a while and try again"
+ util.printD(output)
+ return output+", check console log for detail"
+
+ # write model info to file
+ model.write_model_info(info_file, model_info)
+
+ # set model_count
+ model_count = model_count+1
+
+ # check preview image
+ civitai.get_preview_image_by_model_path(item, max_size_preview, skip_nsfw_preview)
+ image_count = image_count+1
+
+
+ # scan_log = "Done"
+
+ output = f"Done. Scanned {model_count} models, checked {image_count} images"
+
+ util.printD(output)
+
+ return output
+
+
+
+# Get model info by model type, name and url
+# output is log info to display on markdown component
+def get_model_info_by_input(model_type, model_name, model_url_or_id, max_size_preview, skip_nsfw_preview):
+ output = ""
+ # parse model id
+ model_id = civitai.get_model_id_from_url(model_url_or_id)
+ if not model_id:
+ output = "failed to parse model id from url: " + model_url_or_id
+ util.printD(output)
+ return output
+
+ # get model file path
+ # model could be in subfolder
+ result = model.get_model_path_by_type_and_name(model_type, model_name)
+ if not result:
+ output = "failed to get model file path"
+ util.printD(output)
+ return output
+
+ model_root, model_path = result
+ if not model_path:
+ output = "model path is empty"
+ util.printD(output)
+ return output
+
+ # get info file path
+ base, ext = os.path.splitext(model_path)
+ info_file = base + civitai.suffix + model.info_ext
+
+ # get model info
+ #we call it model_info, but in civitai, it is actually version info
+ model_info = civitai.get_version_info_by_model_id(model_id)
+
+ if not model_info:
+ output = "failed to get model info from url: " + model_url_or_id
+ util.printD(output)
+ return output
+
+ # write model info to file
+ model.write_model_info(info_file, model_info)
+
+ util.printD("Saved model info to: "+ info_file)
+
+ # check preview image
+ civitai.get_preview_image_by_model_path(model_path, max_size_preview, skip_nsfw_preview)
+
+ output = "Model Info saved to: " + info_file
+ return output
+
+
+
+# check models' new version and output to UI as markdown doc
+def check_models_new_version_to_md(model_types:list) -> str:
+ new_versions = civitai.check_models_new_version_by_model_types(model_types, 1)
+
+ count = 0
+ output = ""
+ if not new_versions:
+ output = "No model has new version"
+ else:
+ output = "Found new version for following models:
"
+ for new_version in new_versions:
+ count = count+1
+ model_path, model_id, model_name, new_verion_id, new_version_name, description, download_url, img_url = new_version
+ # in md, each part is something like this:
+ # [model_name](model_url)
+ # [version_name](download_url)
+ # version description
+ url = civitai.url_dict["modelPage"]+str(model_id)
+
+ part = f'
'+ description + '