diff --git a/.gitattributes b/.gitattributes
index a6344aac8c09253b3b630fb776ae94478aa0275b..eab0d1c429978bd448fc1ea679bff51a0ad95e43 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
+extensions/Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png filter=lfs diff=lfs merge=lfs -text
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/claim_wall.md b/extensions/Stable-Diffusion-Webui-Civitai-Helper/claim_wall.md
new file mode 100644
index 0000000000000000000000000000000000000000..e707ed1606d57ffef4a8bc94c9599791fa08712f
--- /dev/null
+++ b/extensions/Stable-Diffusion-Webui-Civitai-Helper/claim_wall.md
@@ -0,0 +1,91 @@
+# Claim Wall
+
+Since this extension got a little hot, some users come to **claim many other issues to this extension**.
+
+Following is a wall, to show a few examples how they claim this extension doesn't work, because they don't read document or forget what they did before.
+
+If you are looking for guideline, go to section [What you should do](#what-you-should-do)
+
+# Wall
+
+### Didn't even update SD Webui and claim "tried everything"
+
+![](img/user_claim_wall/have_not_update_sdwebui.jpg)
+
+
+### Havn't even scanned model
+After 4 replies, find that out, and modified his comment.
+
+![](img/user_claim_wall/have_not_scan_model.jpg)
+
+### Claim "pretty sure" this extension breaks his UI, takes 2days to find out it is not
+Then removed his comment from civitai, but his post on reddit is still there, so you can know what's really going on there.
+
+1. Claim "pretty sure" this extension breaks his UI
+
+![](img/user_claim_wall/css_issue_part1.jpg)
+
+![](img/user_claim_wall/css_issue_part2.jpg)
+
+2. Find out it is not, after 2 days
+
+![](img/user_claim_wall/css_issue_part3.jpg)
+
+3. Still don't remember what he did with other extensions, until another user tells him, about 4 days later.
+
+![](img/user_claim_wall/css_issue_part4.jpg)
+
+### **Blame SD Webui's modification to this extension**
+Latest SD webui removed a button from UI, they claim this extension did that, and want it back by this extension
+
+![](img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg)
+
+### Claim other extension's error to this extension
+Just because both extensions have "Civitai" in extension's name
+
+![](img/user_claim_wall/do_not_even_use_this_ext.jpg)
+
+
+### **Didn't even use this extension and request a feature it already has**
+
+![](img/user_claim_wall/request_a_feature_it_already_has.jpg)
+
+### **Renamed model folder's name carelessly and forgot that**
+Takes about 8 hours to find out why this extension doesn't work on his SDwebui and ready to re-install SD webui from beginning.
+
+1. claim this extension can not open civitai url on checkpoint models
+
+![](img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg)
+
+2. I reply that model he mentioned works well in my SDwebui
+
+![](img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg)
+
+3. After 6 hours' trying, find out his model folder's name is modified.
+
+![](img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg)
+
+![](img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg)
+
+
+# What you should do
+Above are just a very small piece of this kind of claims. Those claims won't help you. If you have an issue, following is the guidline:
+
+* If you want to make your extension work, read the document.
+
+* If your SD webui is broken, before you claim it is caused by this extension, you can disable it and try again.
+
+* If you followed document, but it still doesn't work well, you can check console log's msg to find out the reason. If you can not understand those msg, you can come and ask for help, with console log's msg or screenshot.
+
+* If you are using colab, and get an error from colab, then search that error msg in google. Because it's a colab's issue or limitation.
+
+* If you checked console log window's msg and understand what it means, you are welcome to submit your issue.
+
+
+
+
+
+
+
+
+
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/icon/.keep b/extensions/Stable-Diffusion-Webui-Civitai-Helper/icon/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png
new file mode 100644
index 0000000000000000000000000000000000000000..7db1d22b4c770224d6619813cdf975fce52eb21e
--- /dev/null
+++ b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1d05e4490b2ff7a3a82edee277a7130aee149d55
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version_output.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/check_model_new_version_output.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/download_model.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/download_model.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f6c052ccfca850008f7a87e7dfd8b45d2ec17209
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/download_model.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/extension_tab.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/extension_tab.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..a9e0dbe1d1164117cd11064ebbf220377a495c2e
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/extension_tab.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/extra_network.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/extra_network.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..25b2acbdb4afefb348f058a9d45fd8a090eda8ca
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/extra_network.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/get_one_model_info.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/get_one_model_info.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9de82e031c5320d45fef054026ada12f3026e776
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/get_one_model_info.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/model_card.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/model_card.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6ceb33f68f91f3bf3eef625bc18ee427e1002456
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/model_card.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/model_info_file.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/model_info_file.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..d9e36c8e69a3feb90034a2c205a4d4c27aa43bd7
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/model_info_file.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/other_setting.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/other_setting.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9b9a342410e65bcf616978530c79a636ab173f4e
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/other_setting.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/refresh_ch.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/refresh_ch.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..93b4ab70f94ff1718cebc3880f34c4dce45ddfc8
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/refresh_ch.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/thumb_mode.jpg b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/thumb_mode.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1044e68cf89c9f0faa1e1f74a8a7cbb5d97809d6
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/thumb_mode.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part1.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part1.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part2.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part2.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part3.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part3.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part4.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/css_issue_part4.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/did_not_relaunch_sdwebui.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/did_not_relaunch_sdwebui.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/do_not_even_use_this_ext.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/do_not_even_use_this_ext.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_scan_model.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_scan_model.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_update_sdwebui.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/have_not_update_sdwebui.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/request_a_feature_it_already_has.jpg b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/img/user_claim_wall/request_a_feature_it_already_has.jpg differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/javascript/civitai_helper.js b/extensions/Stable-Diffusion-Webui-Civitai-Helper/javascript/civitai_helper.js
new file mode 100644
index 0000000000000000000000000000000000000000..4454d0d4d4b73ec0436beb570e65f943aebfe97f
--- /dev/null
+++ b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/__pycache__/civitai_helper.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/__pycache__/civitai_helper.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..baa7548dd696d881c6c171bd8ffae2bb8371902e
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/__pycache__/civitai_helper.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__init__.py b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/__init__.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2441cfb17107679fe100512e46bda73074291675
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/__init__.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/civitai.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/civitai.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dda013583b485e3570fa49fd52887d1fb50f041a
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/civitai.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/downloader.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/downloader.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b55757f3fc164cd3357112a402f641b75628f162
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/downloader.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/js_action_civitai.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/js_action_civitai.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0dc5811182e2951fb1167f06ea5887eb84045df4
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/js_action_civitai.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..60883ea6f205cb7e99974b77b37b55203bf3d4aa
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model_action_civitai.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model_action_civitai.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4c4a8d058805977da71e2c2d9c4f5d68c4418522
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/model_action_civitai.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/msg_handler.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/msg_handler.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f4ad2e486a94d1090cfd6f2eeaf33d5185cabdcb
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/msg_handler.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/setting.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/setting.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..14c0d2f3f6f537e547366b60023394bd720a0b3a
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/setting.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/util.cpython-310.pyc b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/util.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d4c22fc0e4c66efe967900207b7893d58f637723
Binary files /dev/null and b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/__pycache__/util.cpython-310.pyc differ
diff --git a/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/civitai.py b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/civitai.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a15ae745d6d98b75a177d8a3deb1c897fa2a2c3
--- /dev/null
+++ b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/downloader.py b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/downloader.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b14ebc2cde6650337b4b3e1520337a66d6ea61a
--- /dev/null
+++ b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/js_action_civitai.py b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/js_action_civitai.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c818539e421329ed35a903ab08294a3553804c1
--- /dev/null
+++ b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model.py b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model.py
new file mode 100644
index 0000000000000000000000000000000000000000..729012d742738cab559a8836727fc8289c21f18e
--- /dev/null
+++ b/extensions/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/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model_action_civitai.py b/extensions/Stable-Diffusion-Webui-Civitai-Helper/scripts/ch_lib/model_action_civitai.py
new file mode 100644
index 0000000000000000000000000000000000000000..f09f0752a406e772dcaa95b1ccec20ba9de39506
--- /dev/null
+++ b/extensions/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 + '