Upload 46 files
Browse files- .gitattributes +1 -0
- .github/ISSUE_TEMPLATE/simple-issue-template.md +20 -0
- .gitignore +3 -0
- README.cn.md +241 -0
- README.jp.md +224 -0
- README.kr.md +206 -0
- README.md +329 -0
- claim_wall.md +91 -0
- icon/.keep +0 -0
- img/all_in_one.png +3 -0
- img/check_model_new_version.jpg +0 -0
- img/check_model_new_version_output.jpg +0 -0
- img/download_model.jpg +0 -0
- img/extension_tab.jpg +0 -0
- img/extra_network.jpg +0 -0
- img/get_one_model_info.jpg +0 -0
- img/model_card.jpg +0 -0
- img/model_info_file.jpg +0 -0
- img/other_setting.jpg +0 -0
- img/refresh_ch.jpg +0 -0
- img/thumb_mode.jpg +0 -0
- img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg +0 -0
- img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg +0 -0
- img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg +0 -0
- img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg +0 -0
- img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg +0 -0
- img/user_claim_wall/css_issue_part1.jpg +0 -0
- img/user_claim_wall/css_issue_part2.jpg +0 -0
- img/user_claim_wall/css_issue_part3.jpg +0 -0
- img/user_claim_wall/css_issue_part4.jpg +0 -0
- img/user_claim_wall/did_not_relaunch_sdwebui.jpg +0 -0
- img/user_claim_wall/do_not_even_use_this_ext.jpg +0 -0
- img/user_claim_wall/have_not_scan_model.jpg +0 -0
- img/user_claim_wall/have_not_update_sdwebui.jpg +0 -0
- img/user_claim_wall/request_a_feature_it_already_has.jpg +0 -0
- javascript/civitai_helper.js +728 -0
- scripts/ch_lib/__init__.py +0 -0
- scripts/ch_lib/civitai.py +612 -0
- scripts/ch_lib/downloader.py +115 -0
- scripts/ch_lib/js_action_civitai.py +256 -0
- scripts/ch_lib/model.py +120 -0
- scripts/ch_lib/model_action_civitai.py +511 -0
- scripts/ch_lib/msg_handler.py +64 -0
- scripts/ch_lib/setting.py +113 -0
- scripts/ch_lib/util.py +105 -0
- scripts/civitai_helper.py +218 -0
- style.css +18 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
img/all_in_one.png filter=lfs diff=lfs merge=lfs -text
|
.github/ISSUE_TEMPLATE/simple-issue-template.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: Simple Issue template
|
| 3 |
+
about: Describe this issue template's purpose here.
|
| 4 |
+
title: ''
|
| 5 |
+
labels: ''
|
| 6 |
+
assignees: ''
|
| 7 |
+
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
## Have you read document?
|
| 11 |
+
|
| 12 |
+
## Have you checked console log window's msg?
|
| 13 |
+
|
| 14 |
+
## Describe Issue
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
## Screenshot for UI issue
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
## Console log's msg or screenshot for function issue
|
.gitignore
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
scripts/__pycache__/
|
| 2 |
+
scripts/ch_lib/__pycache__/
|
| 3 |
+
setting.json
|
README.cn.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 关于Civitai Helper2: Model Info Helper
|
| 2 |
+
Civitai助手2将改名为**Model Info助手**。目前还在缓慢开发中。你可以查看它的UI演示视频,了解它会是什么样子:
|
| 3 |
+
[https://youtu.be/mPcKwQDDH8s](https://youtu.be/mPcKwQDDH8s)
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
# Civitai Helper
|
| 7 |
+
Stable Diffusion Webui 扩展Civitai助手,用于更轻松的管理和使用Civitai模型。
|
| 8 |
+
|
| 9 |
+
[Civitai Url](https://civitai.com/models/16768/civitai-helper-sd-webui-civitai-extension)
|
| 10 |
+
|
| 11 |
+
# 注意
|
| 12 |
+
**本插件现在非常稳定,很多人用得很好,如果碰到问题,先看[常见问题](#常见问题),并检查命令行窗口的详情。**
|
| 13 |
+
开issue前,请先看文档。找茬行为的用户将会被拉黑,参考:[找茬行为会被拉黑](https://github.com/butaixianran/Stable-Diffusion-Webui-Civitai-Helper/issues/96#issuecomment-1500310981)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# 功能
|
| 17 |
+
[中文介绍视频(非官方)](https://youtu.be/x4tPWPmeAgM?t=373)
|
| 18 |
+
|
| 19 |
+
* 扫描所有模型,从Civitai下载模型信息和预览图
|
| 20 |
+
* 通过civitai模型页面url,连接本地模型和civitai模型信息
|
| 21 |
+
* 通过Civitai模型页面url,下载模型(含信息和预览图)到SD目录或子目录。
|
| 22 |
+
* 下载支持断点续传
|
| 23 |
+
* 批量检查本地模型,在civitai上的新版本
|
| 24 |
+
* 直接下载新版本模型到SD模型目录内(含信息和预览图)
|
| 25 |
+
* 修改了内置的"Extra Network"模型卡片,每个卡片增加了如下功能按钮:
|
| 26 |
+
- 🖼: 修改文字"replace preview"为这个图标
|
| 27 |
+
- 🌐: 在新标签页打开这个模型的Civitai页面
|
| 28 |
+
- 💡: 一键添加这个模型的触发词到关键词输入框
|
| 29 |
+
- 🏷: 一键使用这个模型预览图所使用的关键词
|
| 30 |
+
* 以上额外功能按钮支持thumbnail模式
|
| 31 |
+
* 增加一直显示按钮的选项,以供触屏用户使用
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# 安装
|
| 35 |
+
下载本项目为zip文件,解压到`你的SD webui目录/extensions`下即可。
|
| 36 |
+
|
| 37 |
+
不管是安装还是升级本插件,都要整个关闭SD Webui,重新启动它。只是Reload UI不起作用。
|
| 38 |
+
|
| 39 |
+
(如果用SD webui的插件界面安装,请先给git配置代理。它不是通过浏览器下载,是通过git下载。)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
# 使用方法
|
| 43 |
+
|
| 44 |
+
## 更新你的SD webui
|
| 45 |
+
本扩展需要取到 Extra Network的卡片列表id。**这个是2023-02-06,才添加到SD webui里面的。**
|
| 46 |
+
|
| 47 |
+
所以,如果你用的版本比这个早,你就需要先更新你的SD Webui!
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
## 扫描模型
|
| 51 |
+
前往扩展页面"Civitai Helper",有个按钮叫:"Scan Model"
|
| 52 |
+
|
| 53 |
+

|
| 54 |
+
|
| 55 |
+
点击,就会扫描所有模型,生成SHA256码,用于从civitai获取模型信息和预览图。**扫描需要很久,耐心等待**。
|
| 56 |
+
|
| 57 |
+
每个模型,本扩展都会创建一个json文件,用来保存从civitai得到的模型信息。这个文件会保存在模型同目录下,名称为:"模型名字.civitai.info"。
|
| 58 |
+
|
| 59 |
+

|
| 60 |
+
|
| 61 |
+
如果模型信息文件已经存在,扫描时就会跳过这个模型。如果模型不是civitai的,就会创建个空信息文件,以避免以后重复扫描。
|
| 62 |
+
|
| 63 |
+
### 添加新模型
|
| 64 |
+
当你下载了新模型之后,只要再次点击扫描按钮即可。已经扫描过的文件不会重复扫描,会自动得到新模型的信息和预览图。无须重启SD webui。
|
| 65 |
+
|
| 66 |
+
## 模型卡片
|
| 67 |
+
**(先完成扫描,再使用卡片功能)**
|
| 68 |
+
打开SD webui's 内置的 "Extra Network" 页面,显示模型卡片
|
| 69 |
+
|
| 70 |
+

|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
移动鼠标到模型卡片底部,就会显示4个按钮:
|
| 74 |
+
- 🖼: 修改文字"replace preview"为这个图标
|
| 75 |
+
- 🌐: 在新标签页打开这个模型的Civitai页面
|
| 76 |
+
- 💡: 一键添加这个模型的触发词到关键词输入框
|
| 77 |
+
- 🏷: 一键使用这个模型预览图所使用的关键词
|
| 78 |
+
|
| 79 |
+

|
| 80 |
+
|
| 81 |
+
如果你没有看到这些额外的按钮,只要点击`Refresh Civitai Helper`,他们就会被重新添加到卡片上。
|
| 82 |
+
|
| 83 |
+

|
| 84 |
+
|
| 85 |
+
每次当Extra Network刷新,他都会删除掉额外的修改,我们的按钮就会消失。这时你就需要点击`Refresh Civitai Helper`把这些功能添加回去。
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
### 小图模式
|
| 89 |
+
以上功能按钮支持小图模式,但受制于SD Webui的CSS问题,目前,只能要么一直显示,要么一直不显示,不能鼠标滑过才显示。
|
| 90 |
+

|
| 91 |
+
|
| 92 |
+
## 下载
|
| 93 |
+
**(单任务,下载完一个再下另一个)**
|
| 94 |
+
通过Civitai模型页面Url下载模型,要3个步骤:
|
| 95 |
+
* 填入url,点击按钮获取模型信息
|
| 96 |
+
* 扩展会自动填入模型名称和类型,你需要选择下载的子目录和模型版本。
|
| 97 |
+
* 点击下载
|
| 98 |
+

|
| 99 |
+
|
| 100 |
+
下载过程会显示在命令行界面带个进度条。
|
| 101 |
+
支持断点续传,无畏大文件。
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
## 批量检查模型新版本
|
| 105 |
+
你可以按照模型类型,批量检查你的本地模型,在civitai上的新版本。你可以选择多个模型类型。
|
| 106 |
+

|
| 107 |
+
|
| 108 |
+
检查新版本的时候,每检查完一个模型,都会有一个1秒的延迟,所以速度有点慢。
|
| 109 |
+
|
| 110 |
+
这是为了保护Civitai避免因为本插件而短暂陷入类似DDos的局面。有些云服务商,有类似“免费用户每秒API请求不能超过1次”的保护机制。Civitai还没有这种设置。但我们还是得自觉保护它。因为如果它挂了,对大家都没有好处。
|
| 111 |
+
|
| 112 |
+
**检查完毕之后**,就会如下图,在UI上显示所有找到的新版本的信息。
|
| 113 |
+
|
| 114 |
+
每个模型新版本,都有3个链接。
|
| 115 |
+
* 第一个是这个模型的网页。
|
| 116 |
+
* 第二个是这个新版本的下载地址。
|
| 117 |
+
* 第三个是个按钮,在python端,直接下载新版本到模型目录内。
|
| 118 |
+
这种方式下载,下载详情显示在"Download Model"的区域和命令行窗口中。一次一个任务,不支持多任务。
|
| 119 |
+

|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
## 根据URL获取模型信息
|
| 124 |
+
如果无法在civitai上找到你的模型的SHA256,但你还是希望能把你的模型连接到一个civitai模型,你可以在本扩展页面,从列表中选择你的模型,并提供一个civitai模型页面的url。
|
| 125 |
+
|
| 126 |
+
点击按钮之后,扩展就会下载那个civitai模型的信息,作为你这个本地模型的信息使用。
|
| 127 |
+
|
| 128 |
+

|
| 129 |
+
|
| 130 |
+
## 代理
|
| 131 |
+
**如果你是刚更新新版本,你需要重启SD webui再来使用**
|
| 132 |
+
|
| 133 |
+
代理输入框在插件页面最下方。
|
| 134 |
+
|
| 135 |
+
**每次填入或清除代理后,都要保存,并用SDwebui设置页面的Reload UI按钮刷新UI**
|
| 136 |
+
|
| 137 |
+
然后所有发到civitai的请求就会用代理。
|
| 138 |
+
|
| 139 |
+
有些sock5代理, 需要使用socks5h开头的形式"socks5h://xxxxx"才能生效。
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
## 其他设置
|
| 144 |
+
**保存设置按钮, 会保存扫描模型区域,以及其他设置 这两个区域的选项**
|
| 145 |
+
|
| 146 |
+
* "一直显示按钮" 是为了方便触屏。
|
| 147 |
+
* "小图模式显示功能按钮" 会开关功能按钮在小图模式的显示
|
| 148 |
+

|
| 149 |
+
|
| 150 |
+
## 预览图
|
| 151 |
+
Extra Network支持两种预览图命名:`model_name.png` 和 `model_name.preview.png`。其中,`model_name.png`优先级较高。
|
| 152 |
+
|
| 153 |
+
当优先级较高的预览图不存在,他就会自动使用`model_name.preview.png`。
|
| 154 |
+
|
| 155 |
+
这样,你自己创建的预览图 和 网络下载的预览图,能够同时存在,并优先使用你自己创建的。
|
| 156 |
+
|
| 157 |
+
## 关键词
|
| 158 |
+
卡片上,添加关键词按钮,是添加从civitai预览图中得到的关键词,而不是你自己创建的图片的关键词。
|
| 159 |
+
|
| 160 |
+
civitai不是每个图片都有关键词,一个模型中,也不是所有预览图关键词都一样。所以这里是遍历所有civitai预览图信息,加载第一个有关键词的。
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
## SHA256
|
| 164 |
+
为了创建文件的SHA256,插件需要读取整个文件。对于大尺寸文件,就会很慢。
|
| 165 |
+
|
| 166 |
+
有两种情况,这个SHA256无法从civitai找到对应模型:
|
| 167 |
+
* 太老的模型,civitai没有存储SHA256.
|
| 168 |
+
* 模型作者,静静的换掉了模型文件,但没有修改描述和版本。所以,虽然网页上看不出来,但实际上civitai上的 和你本地的模型文件,已经不是同一个文件了。
|
| 169 |
+
|
| 170 |
+
这些情况下,你可以在插件上,通过提供模型页面的url,来获取模型信息文件。
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
## 新特性
|
| 175 |
+
从v1.5开始,v1.x不再接受任何新特性。所有新特性进入2.x。
|
| 176 |
+
|
| 177 |
+
2.x专注于自定义模型信息,并可能改名为"Model Info Helper"。因为不再是专注Civitai了。
|
| 178 |
+
|
| 179 |
+
从v1.5开始。v1.x进入维护阶段。
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
Enjoy!
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
## 常见问题
|
| 186 |
+
### 4个卡片按钮不显示
|
| 187 |
+
#### 汉化原因
|
| 188 |
+
下载新版,最新版已经处理汉化导致的问题。**双语汉化插件需要v1.6.1.1之后的版本才开始支持。**
|
| 189 |
+
|
| 190 |
+
#### 使用了云端汉化功能
|
| 191 |
+
如果是秋叶启动器,就关闭启动器“云端汉化”功能。如果是专门的云端汉化插件,就换用普通汉化插件。
|
| 192 |
+
|
| 193 |
+
#### 其他情况
|
| 194 |
+
首先,确保你点过了"Refresh Civitai Helper"刷新按钮。
|
| 195 |
+
|
| 196 |
+
然后,如果还有这个问题,那么唯一原因,是你没有使用最新版SD webui。
|
| 197 |
+
|
| 198 |
+
如果你修改过SD webui的文件, 你的更新操作可能会失败。你需要检查git命令行的输出信息,来确定你更新成功了。
|
| 199 |
+
|
| 200 |
+
git在很多时候,会拒绝升级,并告诉你有些冲突需要你手动先解决。如果你不看命令行输出,你就会以为你已经更新成功了,但其实并没有。
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
### Request model info from civitai
|
| 204 |
+
意思就是正在连接civitai,如果没有后面的信息,就是连不上,请挂代理。
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
### 扫描或获取模型信息失败
|
| 208 |
+
这个插件现在很稳定,所以,这个问题的原因,基本是是因为Civitai拒绝了你的连接请求。
|
| 209 |
+
|
| 210 |
+
Civitai不像那些大网站那么稳定。他网站会挂,会拒绝API连接,还会把API请求转到真人验证页面,来挡住。
|
| 211 |
+
|
| 212 |
+
Civitai还有连接池的设定。基本上,就是同时能允许的最大连接数。一旦达到这个数字,接下来的API连接请求,都会被拒绝。
|
| 213 |
+
|
| 214 |
+
所以,这种时候你只能等一下再试。
|
| 215 |
+
|
| 216 |
+
另外,对于国��用户,还有代理问题。现在国内都要用代理才能连上。
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
### 扫描之后得到了错误的预览图和模型信息
|
| 220 |
+
坏消息是,有些模型在civitai数据库中,保存的sha256完全是错的。查看下面的issue了解详情:
|
| 221 |
+
[https://github.com/civitai/civitai/issues/426](https://github.com/civitai/civitai/issues/426)
|
| 222 |
+
|
| 223 |
+
对于这种模型,那这个插件自然就无法获得正确的模型信息和预览图。
|
| 224 |
+
|
| 225 |
+
这种情况下,请删除扫描得到的模型信息和预览图,在插件界面提供正确的模型url来获取。
|
| 226 |
+
|
| 227 |
+
另外,civitai官方有个页面,专门用于回报带有错误sha256的模型:
|
| 228 |
+
[https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100](https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100)
|
| 229 |
+
|
| 230 |
+
请把这类模型反馈给civitai,好让他们进行修复。
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
### 使用colab时扫描失败
|
| 236 |
+
首先,在google中搜索你看到的错误信息。更有可能是,你碰到的是个colab的问题。
|
| 237 |
+
|
| 238 |
+
然后,如果colab连接了google drive,会有一次性访问文件数量的限制,而导致扫描失败。这是google drive的限制,请自行google搜索了解详情。
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
|
README.jp.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
### Language
|
| 2 |
+
[中文](README.cn.md)
|
| 3 |
+
[English](README.md)
|
| 4 |
+
[한국어(ChatGPT)](README.kr.md)
|
| 5 |
+
|
| 6 |
+
## About Civitai Helper2: Model Info Helper
|
| 7 |
+
Civitai Helper 2は、**ModelInfo Helper**に改名されます。現在開発中です。デモをご覧ください:
|
| 8 |
+
[YouTube](https://youtu.be/mPcKwQDDH8s)
|
| 9 |
+
|
| 10 |
+
# お知らせ
|
| 11 |
+
**この拡張機能は現在、非常に安定しています。もし問題があれば、コンソールログの詳細を確認し、[よくある質問](#よくある質問)を確認してください。**
|
| 12 |
+
|
| 13 |
+
# Civitai Helper
|
| 14 |
+
この拡張機能は、Civitaiのモデルをより簡単に扱えるようにするためのものです。
|
| 15 |
+
|
| 16 |
+
Civitai: [Civitai Url](https://civitai.com/models/16768/civitai-helper-sd-webui-civitai-extension)
|
| 17 |
+
|
| 18 |
+
# 機能
|
| 19 |
+
* 全てのモデルをスキャンし、Civitaiからモデル情報とプレビューをダウンロード
|
| 20 |
+
* CivitaiモデルページのURLを使って、ローカルモデルとCivitaiモデル情報を取得
|
| 21 |
+
* CivitaiモデルページのURLから、モデル(情報とプレビューを含む)をSDディレクトリまたはサブディレクトリにダウンロードする。
|
| 22 |
+
* ダウンロードは途中から再開可能
|
| 23 |
+
* ローカルのモデルとCivitai上の新しいバージョンを一括でチェック
|
| 24 |
+
* 新しいバージョンのモデルを直接モデルのディレクトリにダウンロード(情報とプレビュー画像を含む)
|
| 25 |
+
* 内蔵の**Extra Network**モデルカードを変更し、各カードに以下の機能ボタンを追加しました。
|
| 26 |
+
- 🖼: `replace preview`のテキストをこのアイコンに変更
|
| 27 |
+
- 🌐: このモデルのCivitaiページを新しいタブで開く
|
| 28 |
+
- 💡: このモデルのトリガーワードをキーワード入力欄に一括で追加する
|
| 29 |
+
- 🏷: このモデルのプレビュー画像で使用されているキーワードを一括で使用する
|
| 30 |
+
* 上記の追加機能ボタンは、サムネイルモードにも対応しています。
|
| 31 |
+
* タッチスクリーンデバイス向けに、常に表示されるボタンのオプションを追加しました。
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# インストール
|
| 35 |
+
SD webui's extensionタブから、`Install from url`のタブに移動。
|
| 36 |
+
このリポジトリのURLをコピーペーストし、インストールする。
|
| 37 |
+
|
| 38 |
+
または、このリポジトリをzipでダウンロードし、`./webui/extensions`へ展開してください。
|
| 39 |
+
|
| 40 |
+
この拡張機能をインストール、またはアップデートするたびに、SD Webui再起動する必要があります。
|
| 41 |
+
この拡張機能は、**UIを再読み込みする**だけでは動作しません。
|
| 42 |
+
|
| 43 |
+
# 使い方
|
| 44 |
+
|
| 45 |
+
## WebUIをアップデート
|
| 46 |
+
この拡張機能は`network cards id`を取得する必要があります。この機能は**2023-02-06**に追加されました。
|
| 47 |
+
**SD webuiがこれより前のバージョンである場合は、アップデートする必要があります!**
|
| 48 |
+
|
| 49 |
+
## モデルのスキャン
|
| 50 |
+
拡張機能タブから<kbd>Civitai Helper</kbd>へ。
|
| 51 |
+
<kbd>Scan model</kbd>というボタンがあります。
|
| 52 |
+
|
| 53 |
+

|
| 54 |
+
|
| 55 |
+
これをクリックすると、拡張機能がすべてのモデルをスキャンしてSHA256ハッシュを生成し、それを使ってCivitaiからモデル情報とプレビュー画像を取得します。
|
| 56 |
+
**スキャンには時間がかかります。 終了までお待ちください。**
|
| 57 |
+
|
| 58 |
+
各モデルに対して、Civitaiからすべてのモデル情報を保存するためのjsonファイルを作成します。このモデル情報ファイルは、modelsディレクトリ内の`Your_model_name.civitai.info`となります。
|
| 59 |
+
|
| 60 |
+

|
| 61 |
+
|
| 62 |
+
モデル情報ファイルがすでに存在する場合は、スキップされます。Civitaiでモデルが見つからない場合、空のモデル情報ファイルを作成するので、モデルが2回スキャンされることはありません。
|
| 63 |
+
|
| 64 |
+
### 新しいモデルを追加
|
| 65 |
+
新規のモデルがある場合、もう一度スキャンボタンをクリックするだけで、新しいモデルの情報とプレビューを取得できます。同じモデルを2回スキャンすることはありません。
|
| 66 |
+
|
| 67 |
+
## モデルカード
|
| 68 |
+
**(スキャン終了後に使用)**
|
| 69 |
+
SD webuiの`Extra Network`タブを開き、モデルカードを表示します。
|
| 70 |
+
|
| 71 |
+

|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
マウスをモデルカードの下部に移動すると、4つのボタンが表示されます。
|
| 75 |
+
- 🖼: プレビューを置き換えるためのテキストを`replace preview`からこのアイコンに変更します
|
| 76 |
+
- 🌐: このモデルのCivitaiページを新しいタブで開きます
|
| 77 |
+
- 💡: このモデルのトリガーワードをキーワード入力欄に一括追加します
|
| 78 |
+
- 🏷: このモデルのプレビュー画像に使用されているキーワードを一括で使用します
|
| 79 |
+
|
| 80 |
+

|
| 81 |
+
|
| 82 |
+
これらのボタンが表示���れない場合は、<kbd>Refresh Civitai Helper</kbd>をクリックすると、ボタンがカードに再追加されます。
|
| 83 |
+
|
| 84 |
+

|
| 85 |
+
|
| 86 |
+
`Extra Network`が更新されるたびに、余分な変更が削除され、ボタンが消えてしまいます。その場合は、「Refresh Civitai Helper」をクリックして、これらの機能を再度追加する必要があります。
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
### サムネイル
|
| 90 |
+
これらのボタンは、サムネイルをサポートしていますが、SD WebuiのCSSの問題により、現在は常に表示か非表示かのどちらかに制限されています。マウスをスライドして表示することはできません。
|
| 91 |
+

|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
## ダウンロード
|
| 95 |
+
**(タスクが一つ完了してから、次のタスクをダウンロードしてください)**
|
| 96 |
+
CivitaiモデルページのURLを使用してモデルをダウンロードするには、3つのステップが必要です。
|
| 97 |
+
1. URLを入力し、モデル情報を取得するためにボタンをクリック
|
| 98 |
+
2. 拡張機能が自動的にモデル名とタイプを入力します。ダウンロードするサブディレクトリとモデルバージョンを選択
|
| 99 |
+
3. ダウンロードをクリックします
|
| 100 |
+

|
| 101 |
+
|
| 102 |
+
ダウンロード状況は、CLIに進行状況バーを表示します。
|
| 103 |
+
断片的に再開することができ、大きなファイルをダウンロードする際にも心配する必要はありません。
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
## 新しいモデルのバージョンを確認する
|
| 107 |
+
モデルの種類に従って、ローカルのモデルを一括でCivitaiの新バージョンがないかをチェックすることができます。複数のモデルの種類を選択できます。
|
| 108 |
+

|
| 109 |
+
|
| 110 |
+
これを押すと、各モデルをチェックするたびに1秒の遅延が発生するため、速度がやや遅くなります。
|
| 111 |
+
|
| 112 |
+
これは、本拡張機能のユーザーの過失によるDDoSを回避し、Civitaiを保護するために行われます。
|
| 113 |
+
一部のクラウドサービスプロバイダーには、「無料ユーザーのAPIリクエストは1秒あたり1回を超えてはいけない」というような保護があります。Civitaiにはまだこのような設定がありませんが、我々はそれを自衛しなければなりません。
|
| 114 |
+
なぜなら、もしCivitaiがダウンした場合、誰にとっても良いことではないからです。
|
| 115 |
+
|
| 116 |
+
チェックが完了すると、すべての新しいバージョンがUIに表示されます。
|
| 117 |
+
|
| 118 |
+
各モデルの新しいバージョンには、3つのリンクがあります。
|
| 119 |
+
* 最初のものは、このモデルのWebページです。
|
| 120 |
+
* 2つ目は、この新しいバージョンのダウンロードアドレスです。
|
| 121 |
+
* 3つ目は、Python(拡張機能)側で新しいバージョンをモデルディレクトリに直接ダウンロードするボタンです。
|
| 122 |
+
この方法でダウンロードすると、ダウンロードの詳細が「Download Model」の領域とコマンドラインに表示されます。一度に1つのタスクしかサポートされていません。
|
| 123 |
+

|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
## URLからモデル情報を取得する
|
| 128 |
+
Civitai上で自分のモデルのSHA256が見つからない場合でも、自分のモデルをCivitaiモデルに接続したい場合は、この拡張機能のページから、モデルをリストから選択し、CivitaiモデルページのURLを提供することができます。
|
| 129 |
+
|
| 130 |
+
ボタンをクリックすると、拡張機能はCivitaiモデルの情報をダウンロードし、それをローカルモデルの情報として使用します。
|
| 131 |
+
|
| 132 |
+

|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
## その他の設定
|
| 137 |
+
**設定保存ボタンを押すと、<kbd>Scan Model</kbd>の設定とその他の設定の両方が保存されます。**
|
| 138 |
+
|
| 139 |
+
* <kbd>Always Display Button</kbd>は、タッチデバイスでの操作を容易にするためです。
|
| 140 |
+
* <kbd>Show Buttons on Thumb Mode</kbd>は、小さな画像モードでの機能ボタンの表示を切り替えます。
|
| 141 |
+

|
| 142 |
+
|
| 143 |
+
## プレビュー
|
| 144 |
+
Extra Networkは、2つのプレビュー画像の命名をサポートしています:`model_name.png`と`model_name.preview.png`。
|
| 145 |
+
デフォルトでは自動で`model_name.png`が優先的に使われます。
|
| 146 |
+
|
| 147 |
+
優先度が高いプレビュー画像が存在しない場合は、自動的に`model_name.preview.png`が使用されます。
|
| 148 |
+
|
| 149 |
+
これにより、自分で作成したプレビュー画像とネットからダウンロードしたプレビュー画像を同時に使用し、自分で作成したプレビュー画像を優先的に使用できます。
|
| 150 |
+
|
| 151 |
+
## プロンプト
|
| 152 |
+
カード上の<kbd>Use prompt from preview image</kbd>ボタンは、Civitaiプレビュー画像から取得したキーワードであり、自分で作成した画像のキーワードではありません。
|
| 153 |
+
|
| 154 |
+
Civitaiにはすべての画像にキーワードがあるわけではなく、1つのモデルに含まれるすべてのプレビュー画像のキーワードが同じであるわけでもありません。したがって、ここではすべてのCivitaiプレビュー画像情報を走査し、最初にキーワードがあるものを読み込みます。
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
## SHA256
|
| 158 |
+
ファイルのSHA256を作成するために、はファイル全体を読み取る必要があります。大きなファイルの場合、処理が遅くなります。
|
| 159 |
+
|
| 160 |
+
Civitaiで対応するモデルのSHA256が見つからない場合は、次の2つの場合が考えられます:
|
| 161 |
+
* 古すぎるモデルには、SHA256が保存されていません。
|
| 162 |
+
* モデルの作成者が静かにモデルファイルを変更しましたが、説明やバージョンを変更していないため、サイト上ではわかりませんが、実際にはCivitaiに保存されているモデルファイルとローカルのモデルファイルは異なるものとなっています。
|
| 163 |
+
|
| 164 |
+
これらの場合は、拡張機能にモデルページのURLを提供することで、モデルの情報ファイルを取得できます。
|
| 165 |
+
|
| 166 |
+
## Feature Request
|
| 167 |
+
v1.5以降のv1.xには新機能はありません。すべての新機能は2.xに移行されます。
|
| 168 |
+
2.xでは、カスタムモデル情報にフォーカスし、Civitaiだけではなく、`Model Info Helper`という名称に変更する可能性があります。
|
| 169 |
+
v1.5からv1.xはメンテナンスのフェーズに入ります。
|
| 170 |
+
|
| 171 |
+
お楽しみに!
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
## よくある質問
|
| 175 |
+
### 4つのカードボタンが表示されない
|
| 176 |
+
#### ローカライズの問題
|
| 177 |
+
新しいバージョンをダウンロードしてください。
|
| 178 |
+
最新バージョンでは、ローカライズによる問題が解決されています。
|
| 179 |
+
[バイリンガル拡張機能](https://github.com/journey-ad/sd-webui-bilingual-localization)は、v1.6.1.1以降のバージョンでサポートされるようになりました。
|
| 180 |
+
|
| 181 |
+
#### クラウドサービスベースの翻訳機能を使用した
|
| 182 |
+
クラウドサービスベースの翻訳機能を使用している場合は、通常のローカライズに変更してください。
|
| 183 |
+
|
| 184 |
+
#### その他の場合
|
| 185 |
+
まず、<kbd>Refresh Civitai Helper</kbd>をクリックして更新しましたか?
|
| 186 |
+
|
| 187 |
+
それでもこの問題が発生する場合は、おそらく最新バージョンのSD webuiを使用していないためです。
|
| 188 |
+
|
| 189 |
+
SD webuiのファイルを変更した場合、更新操作が失敗する可能性があります。更新が成功したかどうかを確認するには、gitコマンドラインの出力情報を確認する必要があります。
|
| 190 |
+
|
| 191 |
+
gitは、多くの場合、アップグレードを拒否し、手動で解決する必要があるいくつかの競合状態を示します。コマンドライン出力を見ない場合、更新が成功したと思うかもしれませんが、実際には成功していません。
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
### Request model info from civitai
|
| 195 |
+
これはcivitaiに接続しています。情報がない場合は接続できないため、プロキシを使用してください。
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
### スキャンまたはモデル情報の取得に失敗しました
|
| 199 |
+
この拡張機能は現在非常に安定しているため、この問題の原因は基本的にはCivitaiが接続要求を拒否したためです。
|
| 200 |
+
|
| 201 |
+
Civitaiは大きなウェブサイトとは異なり、安定していません。彼らのウェブサイトはダウンしたり、API接続を拒否したり、APIリクエストをCpatchaページに転送してブロックしたりすることがあります。
|
| 202 |
+
|
| 203 |
+
Civitaiには接続プールの上限もあります。基本的に、同時に許可される最大接続数です。この数字に達すると、以降のAPI接続要求はすべて拒否されます。
|
| 204 |
+
|
| 205 |
+
そのため、このような場合はしばらく待ってから再試行するしかありません。
|
| 206 |
+
|
| 207 |
+
### civitaiから誤ったモデル情報とプレビュー画像を取得する(Translated by ChatGPT)
|
| 208 |
+
悪いニュースですが、civitaiのデータベースには誤ったsha256で保存されたモデルがいくつかあります。詳細についてはこちらをご覧ください:
|
| 209 |
+
[https://github.com/civitai/civitai/issues/426](https://github.com/civitai/civitai/issues/426)
|
| 210 |
+
|
| 211 |
+
したがって、これらのモデルについては、この拡張機能では正しいモデル情報やプレビュー画像を取得できません。
|
| 212 |
+
|
| 213 |
+
この場合、モデル情報ファイルを削除し、この拡張機能のタブページでcivitaiのURLから正しいモデル情報を取得する必要があります。
|
| 214 |
+
|
| 215 |
+
また、誤ったsha256を持つこれらのモデルをcivitaiに報告することもできます。
|
| 216 |
+
[https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100](https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100)
|
| 217 |
+
|
| 218 |
+
civitaiにそのモデルを報告して修正してもらうようにしてください。
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
### colabを使用した際にスキャンに失敗する
|
| 222 |
+
まず、表示されたエラーメッセージをGoogleで検索してください。おそらくcolabの問題が発生している可能性があります。表示されたエラーメッセージを検索して、原因を特定してください。
|
| 223 |
+
|
| 224 |
+
Google Driveに接続する際には、ファイルへのアクセス数に制限があるため、スキャンが失敗することがよくあります。これはGoogle Drive側の制限です。詳細についてはインターネットで[検索](https://google.com)してください。
|
README.kr.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Civitai Helper
|
| 2 |
+
Stable Diffusion Webui는 Civitai 모델을 더 쉽게 관리하고 사용하기 위한 Civitai Assistant 확장 기능입니다.
|
| 3 |
+
|
| 4 |
+
[Civitai Url](https://civitai.com/models/16768/civitai-helper-sd-webui-civitai-extension)
|
| 5 |
+
|
| 6 |
+
# 주의사항
|
| 7 |
+
**이 플러그인은 지금 매우 안정적이며 많은 사용자들이 잘 사용하고 있습니다. 문제가 발생하면, [자주 묻는 질문](#자주-묻는-질문)을 먼저 확인하고 명령 프롬프트 창의 세부 정보를 확인하세요.**
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
# 기능
|
| 13 |
+
* 모든 모델을 스캔하여 Civitai에서 모델 정보 및 미리보기 이미지 다운로드
|
| 14 |
+
* Civitai 모델 페이지 URL을 통해 로컬 모델 및 Civitai 모델 정보 연결
|
| 15 |
+
* Civitai 모델 페이지 URL을 통해 모델(정보 및 미리보기 이미지 포함) 다운로드하여 SD 디렉토리 또는 하위 디렉토리에 저장
|
| 16 |
+
* 이어받기 지원 다운로드
|
| 17 |
+
* 로컬 모델에서 Civitai에 새 버전이 있는지 일괄 확인
|
| 18 |
+
* 새 버전 모델을 SD 모델 디렉토리에 직접 다운로드(정보 및 미리보기 이미지 포함)
|
| 19 |
+
* "Extra Network" 모델 카드 내부를 수정하여 다음과 같은 기능 버튼을 추가:
|
| 20 |
+
- 🖼: "replace preview" 텍스트를 이 아이콘으로 변경
|
| 21 |
+
- 🌐: 해당 모델의 Civitai 페이지를 새 탭에서 열기
|
| 22 |
+
- 💡: 이 모델의 트리거 단어를 키워드 입력란에 일괄 추가
|
| 23 |
+
- 🏷: 이 모델 미리보기 이미지에 사용된 키워드 사용
|
| 24 |
+
* 위의 추가 기능 버튼은 썸네일 모드를 지원합니다.
|
| 25 |
+
* 터치 스크린 사용자를 위해 항상 표시되는 버튼 옵션 추가
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# 설치
|
| 29 |
+
이 프로젝트를 zip 파일로 다운로드하고 SD webui 디렉토리/extensions에 압축 해제하면 됩니다.
|
| 30 |
+
|
| 31 |
+
이 플러그인을 설치하거나 업그레이드하려면 SD Webui를 완전히 종료하고 다시 시작해야 합니다. UI 다시로드는 작동하지 않습니다.
|
| 32 |
+
|
| 33 |
+
# 사용 방법
|
| 34 |
+
|
| 35 |
+
## SD Webui 업데이트
|
| 36 |
+
이 확장 기능은 Extra Network 카드 목록 ID를 가져와야 합니다. 이것은 2023-02-06에 SD Webui에 추가된 것입니다.
|
| 37 |
+
|
| 38 |
+
따라서, 만약 사용 중인 버전이 이보다 이전 버전이라면, 먼저 SD Webui를 업데이트해야 합니다!
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
## 모델 스캔
|
| 42 |
+
확장 페이지 "Civitai Helper"로 이동하고 "Scan Model"이라는 버튼을 클릭합니다.
|
| 43 |
+
|
| 44 |
+

|
| 45 |
+
|
| 46 |
+
클릭하면 모든 모델을 스캔하고 SHA256 코드를 생성하여 Civitai에서 모델 정보 및 미리보기 이미지를 가져옵니다. 스캔에는 시간이 걸리므로 인내심을 가지고 기다려주세요.
|
| 47 |
+
|
| 48 |
+
이 확장 기능은 각 모델마다 Civitai에서 얻은 모델 정보를 저장하는 JSON 파일을 생성합니다. 이 파일은 모델이 있는 디렉토리에 "모델 이름.civitai.info"라는 이름으로 저장됩니다.
|
| 49 |
+
|
| 50 |
+

|
| 51 |
+
|
| 52 |
+
모델 정보 파일이 이미 존재하는 경우 해당 모델은 스캔하지 않습니다. 모델이 Civitai가 아닌 경우 빈 정보 파일이 생성되어 나중에 중복 스캔을 피합니다.
|
| 53 |
+
|
| 54 |
+
### 새 모델 추가
|
| 55 |
+
새 모델을 다운로드한 후 스캔 버튼을 다시 클릭하면 됩니다. 이미 스캔된 파일은 다시 스캔하지 않으며 새 모델의 정보와 미리보기 이미지를 자동으로 얻을 수 있습니다. SD Webui를 다시 시작할 필요가 없습니다.
|
| 56 |
+
|
| 57 |
+
## 모델 카드
|
| 58 |
+
**(스캔을 완료한 후에 카드 기능을 사용하세요)**
|
| 59 |
+
SD Webui의 내장 "Extra Network" 페이지를 열어 모델 카드를 표시합니다.
|
| 60 |
+
|
| 61 |
+

|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
마우스를 모델 카드 아래쪽으로 이동하면 4개의 버튼이 표시됩니다:
|
| 65 |
+
- 🖼: "replace preview" 텍스트를이 아이콘으로 변경
|
| 66 |
+
- 🌐:이 모델의 Civitai 페이지를 새 탭에서 엽니다.
|
| 67 |
+
- 💡:이 모델의 트리거 단어를 키워드 입력 상자에 추가합니다.
|
| 68 |
+
- 🏷:이 모델 미리보기에 사용되는 키워드를 사용합니다.
|
| 69 |
+
|
| 70 |
+

|
| 71 |
+
|
| 72 |
+
이러한 추가 버튼이 표시되지 않으면 Refresh Civitai Helper를 클릭하여 다시 추가하십시오.
|
| 73 |
+
|
| 74 |
+

|
| 75 |
+
|
| 76 |
+
Extra Network가 새로 고침될 때마다이 추가 수정이 제거되므로 버튼이 사라지면 Refresh Civitai Helper를 클릭하여 기능을 다시 추가해야합니다.
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
### 작은 미리보기 모드
|
| 80 |
+
이러한 기능 버튼은 작은 미리보기 모드를 지원하지만 SD Webui의 CSS 문제로 인해 현재 항상 표시하거나 항상 표시하지 않아야합니다.
|
| 81 |
+

|
| 82 |
+
|
| 83 |
+
## 다운로드
|
| 84 |
+
**(한 번에 하나씩, 하나를 다운로드하고 다른 것을 다운로드하세요)**
|
| 85 |
+
Civitai 모델 페이지 URL을 통해 모델을 다운로드하려면 3 단계가 필요합니다:
|
| 86 |
+
* URL을 입력하고 모델 정보를 가져 오는 버튼을 클릭합니다.
|
| 87 |
+
* 확장 프로그램이 모델 이름과 유형을 자동으로 입력합니다. 다운로드 할 하위 디렉토리와 모델 버전을 선택해야합니다.
|
| 88 |
+
* 다운로드를 클릭하십시오.
|
| 89 |
+

|
| 90 |
+
|
| 91 |
+
다운로드 과정은 진행률 표시 줄이있는 명령 줄 인터페이스에서 표시됩니다.
|
| 92 |
+
일시 중지 및 다시 시작을 지원하며 대용량 파일도 문제없이 처리합니다.
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
## 일괄적으로 모델 새 버전 확인
|
| 96 |
+
Civitai에서 새 버전을 확인하기 위해 로컬 모델을 모델 유형 별로 일괄적으로 확인할 수 있습니다. 여러 모델 유형을 선택할 수 있습니다.
|
| 97 |
+

|
| 98 |
+
|
| 99 |
+
새 버전을 확인 할 때마다 모델이 모두 확인 될 때까지 1 초의 지연이 있으므로 속도가 다소 느립니다.
|
| 100 |
+
|
| 101 |
+
이것은 Civitai가 이 플러그인으로 인해 일시적으로 DDos와 유사한 상황에 빠지지 않도록 보호하기 위한 것입니다. 일부 클라우드 서비스 제공 업체는 "무료 사용자의 초당 API 요청 수는 1 회를 초과 할 수 없다"는 보호 메커니즘이 있습니다. Civitai는 이러한 설정이 없습니다. 그러나 우리는 여전히 그것을 보호해야합니다. 왜냐하면 그것이 다운되면 모두에게 좋지 않기 때문입니다.
|
| 102 |
+
|
| 103 |
+
확인이 완료되면 다음과 같이 UI에 모든 새 버전을 찾은 정보가 표시됩니다.
|
| 104 |
+
|
| 105 |
+
각 모델 새 버전에는 3 개의 링크가 있습니다.
|
| 106 |
+
* 첫 번째는 이 모델의 웹 페이지입니다.
|
| 107 |
+
* 두 번째는이 새 버전의 다운로드 주소입니다.
|
| 108 |
+
* 세 번째는 버튼입니다. Python 측에서 새 버전을 모델 디렉토리로 직접 다운로드합니다.
|
| 109 |
+
이 방식으로 다운로드하면 "모델 다운로드" 영역과 명령 줄 창에 다운로드 세부 정보가 표시됩니다. 한 번에 하나의 작업만 지원됩니다.
|
| 110 |
+

|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
## URL을 기반으로 모델 정보 가져오기
|
| 115 |
+
Civitai에서 모델의 SHA256을 찾을 수 없지만 여전히 Civitai 모델에 모델을 연결하고 싶다면 해당 확장 프로그램 페이지에서 모델을 선택하고 Civitai 모델 페이지의 URL을 제공할 수 있습니다.
|
| 116 |
+
|
| 117 |
+
버튼을 클릭하면 확장 프로그램이 해당 Civitai 모델의 정보를 다운로드하여 로컬 모델의 정보로 사용합니다.
|
| 118 |
+
|
| 119 |
+

|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
## 기타 설정
|
| 124 |
+
**설정 저장 버튼은 스캔 모델 영역 및 기타 설정 두 영역의 옵션을 저장합니다.**
|
| 125 |
+
|
| 126 |
+
* "항상 표시 버튼"은 터치 스크린에서 편리하게 사용하기 위한 것입니다.
|
| 127 |
+
* "작은 그림 모드에서 기능 버튼 표시"는 작은 그림 모드에서 기능 버튼을 표시할지 여부를 전환합니다.
|
| 128 |
+

|
| 129 |
+
|
| 130 |
+
## 미리보기 이미지
|
| 131 |
+
Extra Network는 model_name.png 및 model_name.preview.png 두 가지 미리보기 이미지 이름을 지원합니다. 여기서 model_name.png이 우선순위가 높습니다.
|
| 132 |
+
|
| 133 |
+
우선순위가 높은 미리보기 이미지가 없으면 자동으로 model_name.preview.png를 사용합니다.
|
| 134 |
+
|
| 135 |
+
이렇게 하면 직접 만든 미리보기 이미지와 인터넷에서 다운로드한 미리보기 이미지를 함께 사용할 수 있으며, 우선순위는 직접 만든 이미지가 높습니다.
|
| 136 |
+
|
| 137 |
+
## 키워드
|
| 138 |
+
카드에 키워드 추가 버튼은 civitai 미리보기 이미지에서 얻은 키워드를 추가하는 것이며, 사용자가 직접 만든 이미지의 키워드가 아닙니다.
|
| 139 |
+
|
| 140 |
+
모든 이미지에 키워드가 있는 것은 아니며, 모델에 따라 미리보기 이미지의 키워드가 모두 같지 않을 수 있습니다. 따라서 여기서는 civitai 모든 미리보기 이미지 정보를 탐색하여 첫 번째 키워드가 있는 이미지를 로드합니다.
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
## SHA256
|
| 144 |
+
파일의 SHA256을 생성하려면 플러그인에서 전체 파일을 읽어야 합니다. 대형 파일의 경우 시스템이 느려질 수 있습니다.
|
| 145 |
+
|
| 146 |
+
SHA256은 civitai에서 해당 모델을 찾을 수 없는 두 가지 경우가 있습니다.
|
| 147 |
+
* 너무 오래된 모델이므로 civitai에 SHA256이 저장되어 있지 않습니다.
|
| 148 |
+
* 모델 작성자가 모델 파일을 조용히 교체했지만 설명 및 버전을 수정하지 않았습니다. 따라서 웹 페이지에서는 확인할 수 없지만 civitai 및 로컬 모델 파일은 이미 다른 파일입니다.
|
| 149 |
+
|
| 150 |
+
이러한 경우에는 플러그인에서 모델 페이지 URL을 제공하여 모델 정보 파일을 얻을 수 있습니다
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
## 자주 묻는 질문
|
| 154 |
+
### 4개의 카드 버튼이 표시되지 않습니다.
|
| 155 |
+
#### 한국어 플러그인을 사용했습니다
|
| 156 |
+
새 버전을 다운로드하면, 최신 버전에서 한국어 번역으로 인한 문제가 해결되었습니다. 양방향 다국어 플러그인은 v1.6.1.1 이후 버전부터 지원됩니다.
|
| 157 |
+
|
| 158 |
+
#### 클라우드 기반 한국어 플러그인을 사용했습니다
|
| 159 |
+
클라우드 기반 한국어 플러그인을 사용한 경우 일반적인 한국어 플러그인으로 변경���십시오.
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
#### 다른 경우
|
| 163 |
+
먼저 "Refresh Civitai Helper" 버튼을 클릭하여 Civitai Helper를 새로고침했는지 확인하세요.
|
| 164 |
+
|
| 165 |
+
그런 다음 이 문제가 계속되는 경우, 유일한 이유는 최신 버전의 SD webui를 사용하지 않았기 때문입니다.
|
| 166 |
+
|
| 167 |
+
만약 SD webui의 파일을 수정했다면, 업데이트 작업이 실패할 수 있습니다. 업데이트가 제대로 이루어졌는지 확인하려면 git 명령 줄의 출력 정보를 확인해야 합니다.
|
| 168 |
+
|
| 169 |
+
git은 종종 업그레이드를 거부하고, 일부 충돌을 수동으로 해결해야 한다는 메시지를 보여줍니다. 명령 줄 출력을 확인하지 않으면 업그레이드가 성공했다고 잘못 생각할 수 있습니다.
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
### Request model info from civitai
|
| 173 |
+
이것은 Civitai에 연결하고 있음을 나타내며, 정보가 없으면 연결할 수 없으므로 프록시를 사용해야 합니다.
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
### 모델 정보 스캔 또는 가져오기 실패
|
| 177 |
+
이 플러그인은 이제 매우 안정적이므로, 이 문제의 원인은 대부분 Civitai가 연결 요청을 거부했기 때문입니다.
|
| 178 |
+
|
| 179 |
+
Civitai는 대형 웹사이트와 같이 안정적이지 않습니다. 웹사이트가 다운되거나 API 연결을 거부할 수 있고, API 요청을 실제 검증 페이지로 전환하여 차단할 수도 있습니다.
|
| 180 |
+
|
| 181 |
+
Civitai에는 연결 풀 설정이 있습니다. 이는 동시에 허용되는 최대 연결 수입니다. 이 수치에 도달하면 다음 API 연결 요청은 모두 거부됩니다. 이 때는 잠시 기다렸다가 다시 시도해야 합니다.
|
| 182 |
+
|
| 183 |
+
또한 국내 사용자들에게는 프록시 문제가 있습니다. 대개는 프록시를 사용해야만 연결할 수 있습니다.
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
### civitai에서 잘못된 모델 정보 및 미리보기 이미지 가져오기
|
| 187 |
+
안타깝게도, civitai의 데이터베이스에 일부 모델이 잘못된 sha256으로 저장되어 있습니다. 자세한 내용은 여기를 확인하십시오:
|
| 188 |
+
[https://github.com/civitai/civitai/issues/426](https://github.com/civitai/civitai/issues/426)
|
| 189 |
+
|
| 190 |
+
따라서 이 확장 프로그램은 해당 모델의 올바른 모델 정보나 미리보기 이미지를 가져올 수 없습니다.
|
| 191 |
+
|
| 192 |
+
이 경우 모델 정보 파일을 제거하고 이 확장 프로그램의 탭 페이지에서 civitai url로 올바른 모델 정보를 가져와야 합니다.
|
| 193 |
+
|
| 194 |
+
또한, 잘못된 sha256을 가진 해당 모델을 civitai에 신고할 수 있습니다.
|
| 195 |
+
[https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100](https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100)
|
| 196 |
+
|
| 197 |
+
civitai에 그 모델을 신고하여 수정할 수 있도록 해주시기 바랍니다.
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
### Colab 사용시 스캔 실패
|
| 201 |
+
먼저 보이는 오류 메시지를 Google에서 검색해보세요. 대개 Colab의 문제일 가능성이 높습니다.
|
| 202 |
+
|
| 203 |
+
그리고 Colab이 Google 드라이브에 연결되어 있다면, 파일에 대한 일회성 액세스 제한으로 인해 스캔이 실패할 수 있습니다. 이는 Google 드라이브의 제한 사항으로, 자세한 내용은 Google 검색을 통해 알아보세요.
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
|
README.md
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
### Language
|
| 2 |
+
[中文](README.cn.md)
|
| 3 |
+
[日本語](README.jp.md)
|
| 4 |
+
[한국어(ChatGPT)](README.kr.md)
|
| 5 |
+
|
| 6 |
+
## About Civitai Helper2: Model Info Helper
|
| 7 |
+
Civitai Helper 2 will be renamed to **ModelInfo Helper**. It is under development, you can watch its UI demo video to see how it gonna look like:
|
| 8 |
+
[YouTube](https://youtu.be/mPcKwQDDH8s)
|
| 9 |
+
|
| 10 |
+
# Notice
|
| 11 |
+
**This extension now is very stable and works well for many people. If you have an issue, check console log window's detail and read [common issue](#common-issue) part**
|
| 12 |
+
|
| 13 |
+
If you want to claim it doesn't work, check this first: [Claim Wall](claim_wall.md)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# Civitai Helper
|
| 17 |
+
Stable Diffusion Webui Extension for Civitai, to handle your models much more easily.
|
| 18 |
+
|
| 19 |
+
Civitai: [Civitai Url](https://civitai.com/models/16768/civitai-helper-sd-webui-civitai-extension)
|
| 20 |
+
|
| 21 |
+
# Features
|
| 22 |
+
* Scans all models to download model information and preview images from Civitai.
|
| 23 |
+
* Link local model to a civitai model by civitai model's url
|
| 24 |
+
* Download a model(with info+preview) by Civitai Url into SD's model folder or subfolder.
|
| 25 |
+
* Downloading can resume at break-point, which is good for large file.
|
| 26 |
+
* Checking all your local model's new version from Civitai
|
| 27 |
+
* Download a new version directly into SD model folder (with info+preview)
|
| 28 |
+
* Modified Built-in "Extra Network" cards, to add the following buttons on each card:
|
| 29 |
+
- 🖼️: Modified "replace preview" text into this icon
|
| 30 |
+
- 🌐: Open this model's Civitai url in a new tab
|
| 31 |
+
- 💡: Add this model's trigger words to prompt
|
| 32 |
+
- 🏷️: Use this model's preview image's prompt
|
| 33 |
+
* Above buttons support thumbnail mode of Extra Network
|
| 34 |
+
* Option to always show additional buttons, to work with touchscreen.
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
# Install
|
| 38 |
+
Go to SD webui's extension tab, go to `Install from url` sub-tab.
|
| 39 |
+
Copy this project's url into it, click install.
|
| 40 |
+
|
| 41 |
+
Alternatively, download this project as a zip file, and unzip it to `Your SD webui folder/extensions`.
|
| 42 |
+
|
| 43 |
+
Everytime you install or update this extension, you need to shutdown SD Webui and Relaunch it. Just "Reload UI" won't work for this extension.
|
| 44 |
+
|
| 45 |
+
Done.
|
| 46 |
+
|
| 47 |
+
# How to Use
|
| 48 |
+
|
| 49 |
+
## Update Your SD Webui
|
| 50 |
+
This extension need to get extra network's cards id. Which is added since **2023-02-06**.
|
| 51 |
+
**If your SD webui is an earlier version, you need to update it!**
|
| 52 |
+
|
| 53 |
+
## Scanning Models
|
| 54 |
+
Go to extension tab "Civitai Helper". There is a button called "Scan model".
|
| 55 |
+
|
| 56 |
+

|
| 57 |
+
|
| 58 |
+
Click it and the extension will scan all your models to generate SHA256 hashes, using them to retreive model information and preview images from Civitai.
|
| 59 |
+
|
| 60 |
+
**Scanning takes time, just wait it finish**
|
| 61 |
+
|
| 62 |
+
For each model, it will create a json file to save all model info from Civitai. This model info file will be "Your_model_name.civitai.info" in your model folder.
|
| 63 |
+
|
| 64 |
+

|
| 65 |
+
|
| 66 |
+
If a model info file already exists, it will be skipped. If a model cannot be found in Civitai, it will create an empty model info file, so the model won't be scanned twice.
|
| 67 |
+
|
| 68 |
+
### Adding New Models
|
| 69 |
+
When you have some new models, just click scan button again, to get new model's information and preview images. It won't scan the same model twice.
|
| 70 |
+
|
| 71 |
+
## Model Card
|
| 72 |
+
**(Use this only after scanning finished)**
|
| 73 |
+
Open SD webui's build-in "Extra Network" tab, to show model cards.
|
| 74 |
+
|
| 75 |
+

|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
Move your mouse on to the bottom of a model card. It will show 4 icon buttons:
|
| 79 |
+
- 🖼: Replace preview (a build-in button, modified from text to icon)
|
| 80 |
+
- 🌐: Open this model's Civitai url in a new tab
|
| 81 |
+
- 💡: Add this model's trigger words to prompt
|
| 82 |
+
- 🏷: Use this model's preview image's prompt
|
| 83 |
+
|
| 84 |
+

|
| 85 |
+
|
| 86 |
+
**If these additional buttons are not there**, click the `Refresh Civitai Helper` button to bring them back.
|
| 87 |
+
|
| 88 |
+

|
| 89 |
+
Everytime after Extra Network tab refreshed, it will remove all these additional buttons. So, you need to click `Refresh Civitai Helper` button to bring them back.
|
| 90 |
+
|
| 91 |
+
### Thumbnail Mode
|
| 92 |
+
Additional buttons work on thumbnail too, but due to SD webui's CSS issue, for now, they must be always displayed on thumbnail or don't display at all.
|
| 93 |
+

|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
## Download
|
| 97 |
+
To download a model by Civitai Model Page's Url, you need 3 steps:
|
| 98 |
+
* Fill url, click button to get model info
|
| 99 |
+
* It will show model name and type automatically. Just choose sub-folder and model version
|
| 100 |
+
* Click download.
|
| 101 |
+

|
| 102 |
+
|
| 103 |
+
Detail will be displayed on console log, with a progress bar.
|
| 104 |
+
Downloading can resume from break-point, so no fear for large file.
|
| 105 |
+
|
| 106 |
+
## Checking Model's New Version
|
| 107 |
+
You can checking your local model's new version from civitai by model types. You can select multiple model types.
|
| 108 |
+

|
| 109 |
+
|
| 110 |
+
The checking process has a "1 second delay" after each model's new version checking request. So it is a little slow.
|
| 111 |
+
|
| 112 |
+
This is to protect Civitai from issue like DDos from this extension. Some cloud service provider has a rule as "no more than 1 API request in a second for free user". Civitai doesn't have this rule yet, but we still need to protect it. There is no good for us if it is down.
|
| 113 |
+
|
| 114 |
+
**After checking process done**, it will display all new version's information on UI.
|
| 115 |
+
|
| 116 |
+
There are 3 urls for each new version.
|
| 117 |
+
* First one is model's civitai page.
|
| 118 |
+
* Second one is new version's download url.
|
| 119 |
+
* Third one is a button to download it into your SD's model folder with python.
|
| 120 |
+
With this one, output information is on "Download Model" section's log and console log. **One task at a time**.
|
| 121 |
+
|
| 122 |
+

|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
## Get Model Info By Url
|
| 126 |
+
This is used to force a local model links to a Civitai model. For example, you converted a model's format or pruned it. Then it can not be found on civitai when scanning.
|
| 127 |
+
|
| 128 |
+
In that case, if you still want to link it to a civitai model. You can use this funcion.
|
| 129 |
+
|
| 130 |
+
Choose this model from list, then offer a civitai model page's url.
|
| 131 |
+
|
| 132 |
+
After clicking button, extension will download that civitai model's info and preview image for the local file you picked.
|
| 133 |
+
|
| 134 |
+

|
| 135 |
+
|
| 136 |
+
## Proxy
|
| 137 |
+
**If you are updating to new version, you need to re-lanuch SD webui before using it.**
|
| 138 |
+
|
| 139 |
+
Proxy textbox is at the bottom of extension tab.
|
| 140 |
+
|
| 141 |
+
**Each time you fill or clear a proxy value, you need to save setting, and Re-load UI with setting tab's reload button.**
|
| 142 |
+
|
| 143 |
+
Then all requests to civitai will use the proxy.
|
| 144 |
+
|
| 145 |
+
For some sock5 proxy, need to be used as "socks5h://xxxxx".
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
## Other Setting
|
| 151 |
+
**The Save Setting button, will save both "Scan Model"'s setting and other setting.**
|
| 152 |
+
|
| 153 |
+
* "Always Display Button" is good for touch screen.
|
| 154 |
+
* "Show Buttons on Thumb Mode" will turn on/off additional Buttons on thumbnail.
|
| 155 |
+

|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
## Preview Image
|
| 161 |
+
Extra network uses both `model_file.png` and `model_file.preview.png` as preview image. But `model_file.png` has higher priority, because it is created by yourself.
|
| 162 |
+
|
| 163 |
+
When you don't have the higher priority one, it will use the other automatically.
|
| 164 |
+
|
| 165 |
+
## Prompt
|
| 166 |
+
When you click the button "Use prompt from preview image", it does not use the prompt from your own preview image. It uses the one from civitai's preview image.
|
| 167 |
+
|
| 168 |
+
On civitai, a model's preview images may not has prompt. This extension will check this model's all civitai preview images' information and use the first one has prompt in it.
|
| 169 |
+
|
| 170 |
+
## SHA256
|
| 171 |
+
To create a file SHA256, it need to read the whole file to generate a hash code. It gonna be slow for large files.
|
| 172 |
+
|
| 173 |
+
Also, extension uses Memory Optimized SHA256, which won't stuck your system and works with colab.
|
| 174 |
+
|
| 175 |
+
There are 2 cases this hash code can not find the model on civitai:
|
| 176 |
+
* Some old models, which do not have SHA256 code on civitai.
|
| 177 |
+
* The model's owner changed file on civitai, but does not change version name and description. So, the file on civitai is actually not the one on your manchine.
|
| 178 |
+
|
| 179 |
+
In these cases, you can always link a model to civitai by filling its URL in this extension.
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
## Feature Request
|
| 184 |
+
No new feature for v1.x after v1.5. All new feature will go to 2.x.
|
| 185 |
+
|
| 186 |
+
2.x will focus on custom model information and may change name to "Model Info Helper", because it is not just focus on Civitai anymore.
|
| 187 |
+
|
| 188 |
+
From v1.5, v1.x goes into maintenance phase.
|
| 189 |
+
|
| 190 |
+
Enjoy!
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
## Common Issue
|
| 194 |
+
### 4 Buttons on card didn't show
|
| 195 |
+
#### Localization
|
| 196 |
+
There was a Localization issue if you are not using English version of SD webui. This is fixed in the latest version of this extension. **Bilingual localization extension is supported by PR since v1.6.1.1.**
|
| 197 |
+
|
| 198 |
+
##### Using cloud based localization extension
|
| 199 |
+
Turn off cloud based localization extension, use normal localization extension.
|
| 200 |
+
|
| 201 |
+
#### Other case
|
| 202 |
+
First of all, make sure you clicked "Refresh Civitai Helper" button.
|
| 203 |
+
|
| 204 |
+
If issue is still there, then only reason is you are not using the latest SD webui. So, Make sure you updated it.
|
| 205 |
+
|
| 206 |
+
Your update could be failed if you have modified SD webui's file. You need to check git command's console log to make sure it is updated.
|
| 207 |
+
|
| 208 |
+
In many cases, git will just refuse to update and tell you there are some conflicts need you to handle manually. If you don't check the consloe log, you will think your SD webui is updated, but it is not.
|
| 209 |
+
|
| 210 |
+
### Request, Scan or Get model info failed
|
| 211 |
+
This extension is stable. So, the reason for this most likely is your internet connection to Civitai API service.
|
| 212 |
+
|
| 213 |
+
Civitai is not as stable as those rich websites, it can be down or refuse your API connection.
|
| 214 |
+
|
| 215 |
+
Civitai has a connection pool setting. Basicly, it's a max connection number that civitai can have at the same time. So, if there are already too manny connections on civitai, it will refuse your API connection.
|
| 216 |
+
|
| 217 |
+
In those cases, the only thing you can do is just wait a while then try again.
|
| 218 |
+
|
| 219 |
+
### Get Wrong model info and preview images from civitai
|
| 220 |
+
A bad news is, some models are saved with a wrong sha256 in civitai's database. Check here for more detail:
|
| 221 |
+
[https://github.com/civitai/civitai/issues/426](https://github.com/civitai/civitai/issues/426)
|
| 222 |
+
|
| 223 |
+
So, for those models, this extension can not get the right model info or preview images.
|
| 224 |
+
|
| 225 |
+
In this case, you have to remove the model info file and get the right model info by a civitai url on this extension's tab page.
|
| 226 |
+
|
| 227 |
+
Also, you can report those models with wrong sha256 to civitai at following page:
|
| 228 |
+
[https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100](https://discord.com/channels/1037799583784370196/1096271712959615100/1096271712959615100)
|
| 229 |
+
|
| 230 |
+
Please report that model to civitai, so they can fix it.
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
### Scanning fail when using colab
|
| 236 |
+
First of, search your error message with google. Most likely, it will be a colab issue.
|
| 237 |
+
|
| 238 |
+
If you are sure it is a out of memory issue when scanning models, and you are using this extension's latest version, then there is nothing we can do.
|
| 239 |
+
|
| 240 |
+
Since v1.5.5, we've already optimized the SHA256 function to the top. So the only 2 choices for you are:
|
| 241 |
+
* try again
|
| 242 |
+
* or use a pro account of colab.
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
# Change Log
|
| 249 |
+
## v1.6.4
|
| 250 |
+
* Add "Download All files" checkbox for downloading model section. Uncheck means only download 1 file.
|
| 251 |
+
|
| 252 |
+
## v1.6.3
|
| 253 |
+
* Support downloading multiple files, not avaiable when checking new version.
|
| 254 |
+
|
| 255 |
+
## v1.6.2.1
|
| 256 |
+
* when parsing civitai url, remove query string by PR
|
| 257 |
+
|
| 258 |
+
## v1.6.2
|
| 259 |
+
* When downloading, re-name file if file already exists
|
| 260 |
+
|
| 261 |
+
## v1.6.1.1
|
| 262 |
+
* Support bilingual localization extension by PR
|
| 263 |
+
|
| 264 |
+
## v1.6.1
|
| 265 |
+
* Fix Localization issue for 4 addtional buttons on cards. (Forgot that again...)
|
| 266 |
+
|
| 267 |
+
## v1.6.0
|
| 268 |
+
* Fix some UI issues to work with gradio 3.23.0
|
| 269 |
+
* Support Proxy when connecting to civitai. Check document for detail.
|
| 270 |
+
* check realpath when opening file, to fix error when using junction
|
| 271 |
+
* Fix multiple addtional buttons issue after switching tabs.
|
| 272 |
+
|
| 273 |
+
## v1.5.7
|
| 274 |
+
* Fix Localization issue for 4 addtional buttons on cards
|
| 275 |
+
|
| 276 |
+
## v1.5.6
|
| 277 |
+
* update error msg when can not connect to civitai API service
|
| 278 |
+
* update thumb mode for SD webui new version's metadata button
|
| 279 |
+
|
| 280 |
+
## v1.5.5
|
| 281 |
+
* update SHA256 function, now it just use the code from pip
|
| 282 |
+
|
| 283 |
+
## v1.5.4
|
| 284 |
+
* set sys.stdout to utf-8
|
| 285 |
+
* Add default header for requests to prevent from being blocked by civitai.
|
| 286 |
+
* merge other v1.5.x change log to v1.5.4
|
| 287 |
+
* When downloading a model by url, check if target model version is already existed in user selected sub-folder.
|
| 288 |
+
* Support scanning only selected model types.
|
| 289 |
+
* Force TI scanning delay 1 second to prevent from civitai treating this extension's requests as attacking.
|
| 290 |
+
|
| 291 |
+
## v1.5.0
|
| 292 |
+
* Download a model by Civitai model page's url
|
| 293 |
+
* Resume downloading from break-point
|
| 294 |
+
* Download new version into SD Webui's model folder
|
| 295 |
+
* Addtional button now works on thumbnail mode
|
| 296 |
+
* Option to always show addtion button, for touch screen.
|
| 297 |
+
|
| 298 |
+
## v1.4.2
|
| 299 |
+
* ignore .vae file in model folder when scanning
|
| 300 |
+
|
| 301 |
+
## v1.4.1
|
| 302 |
+
* When checking new versions, also searching and ignore already existed ones.
|
| 303 |
+
* Add version number to the bottom of this extension's tab
|
| 304 |
+
|
| 305 |
+
## v1.4
|
| 306 |
+
* Support checking model's new version, display the result in UI and offer download url
|
| 307 |
+
* Remove addintional sub tabs on extension tab. make ui simpler.
|
| 308 |
+
|
| 309 |
+
## v1.3
|
| 310 |
+
* Open url at client side
|
| 311 |
+
* Link selected model to civitai by url or model id
|
| 312 |
+
* Save and load extension setting to file
|
| 313 |
+
* Show button action's output to UI
|
| 314 |
+
* Code refactoring
|
| 315 |
+
|
| 316 |
+
## v1.2.1
|
| 317 |
+
* Add more error checking to work with different versions of SD webui.
|
| 318 |
+
|
| 319 |
+
## v1.2
|
| 320 |
+
* Support customer model folder
|
| 321 |
+
* Support readable model info file
|
| 322 |
+
* Support download preview image with max size
|
| 323 |
+
* Remove card buttons when extra network is in thumbnail mode
|
| 324 |
+
|
| 325 |
+
## v1.1
|
| 326 |
+
* Support subfolders
|
| 327 |
+
* Check if refresh is needed when clicking "Refresh Civitai Helper"
|
| 328 |
+
* Add space when adding trigger words
|
| 329 |
+
* Add memory Optimized sha256 as an option
|
claim_wall.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Claim Wall
|
| 2 |
+
|
| 3 |
+
Since this extension got a little hot, some users come to **claim many other issues to this extension**.
|
| 4 |
+
|
| 5 |
+
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.
|
| 6 |
+
|
| 7 |
+
If you are looking for guideline, go to section [What you should do](#what-you-should-do)
|
| 8 |
+
|
| 9 |
+
# Wall
|
| 10 |
+
|
| 11 |
+
### Didn't even update SD Webui and claim "tried everything"
|
| 12 |
+
|
| 13 |
+

|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
### Havn't even scanned model
|
| 17 |
+
After 4 replies, find that out, and modified his comment.
|
| 18 |
+
|
| 19 |
+

|
| 20 |
+
|
| 21 |
+
### Claim "pretty sure" this extension breaks his UI, takes 2days to find out it is not
|
| 22 |
+
Then removed his comment from civitai, but his post on reddit is still there, so you can know what's really going on there.
|
| 23 |
+
|
| 24 |
+
1. Claim "pretty sure" this extension breaks his UI
|
| 25 |
+
|
| 26 |
+

|
| 27 |
+
|
| 28 |
+

|
| 29 |
+
|
| 30 |
+
2. Find out it is not, after 2 days
|
| 31 |
+
|
| 32 |
+

|
| 33 |
+
|
| 34 |
+
3. Still don't remember what he did with other extensions, until another user tells him, about 4 days later.
|
| 35 |
+
|
| 36 |
+

|
| 37 |
+
|
| 38 |
+
### **Blame SD Webui's modification to this extension**
|
| 39 |
+
Latest SD webui removed a button from UI, they claim this extension did that, and want it back by this extension
|
| 40 |
+
|
| 41 |
+

|
| 42 |
+
|
| 43 |
+
### Claim other extension's error to this extension
|
| 44 |
+
Just because both extensions have "Civitai" in extension's name
|
| 45 |
+
|
| 46 |
+

|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
### **Didn't even use this extension and request a feature it already has**
|
| 50 |
+
|
| 51 |
+

|
| 52 |
+
|
| 53 |
+
### **Renamed model folder's name carelessly and forgot that**
|
| 54 |
+
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.
|
| 55 |
+
|
| 56 |
+
1. claim this extension can not open civitai url on checkpoint models
|
| 57 |
+
|
| 58 |
+

|
| 59 |
+
|
| 60 |
+
2. I reply that model he mentioned works well in my SDwebui
|
| 61 |
+
|
| 62 |
+

|
| 63 |
+
|
| 64 |
+
3. After 6 hours' trying, find out his model folder's name is modified.
|
| 65 |
+
|
| 66 |
+

|
| 67 |
+
|
| 68 |
+

|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
# What you should do
|
| 72 |
+
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:
|
| 73 |
+
|
| 74 |
+
* If you want to make your extension work, read the document.
|
| 75 |
+
|
| 76 |
+
* If your SD webui is broken, before you claim it is caused by this extension, you can disable it and try again.
|
| 77 |
+
|
| 78 |
+
* 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.
|
| 79 |
+
|
| 80 |
+
* 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.
|
| 81 |
+
|
| 82 |
+
* If you checked console log window's msg and understand what it means, you are welcome to submit your issue.
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
|
icon/.keep
ADDED
|
File without changes
|
img/all_in_one.png
ADDED
|
Git LFS Details
|
img/check_model_new_version.jpg
ADDED
|
img/check_model_new_version_output.jpg
ADDED
|
img/download_model.jpg
ADDED
|
img/extension_tab.jpg
ADDED
|
img/extra_network.jpg
ADDED
|
img/get_one_model_info.jpg
ADDED
|
img/model_card.jpg
ADDED
|
img/model_info_file.jpg
ADDED
|
img/other_setting.jpg
ADDED
|
img/refresh_ch.jpg
ADDED
|
img/thumb_mode.jpg
ADDED
|
|
img/user_claim_wall/blame_sdweui_update_to_this_ext.jpg
ADDED
|
img/user_claim_wall/changed_model_folder_name_then_forget_part1.jpg
ADDED
|
img/user_claim_wall/changed_model_folder_name_then_forget_part2.jpg
ADDED
|
img/user_claim_wall/changed_model_folder_name_then_forget_part3.jpg
ADDED
|
img/user_claim_wall/changed_model_folder_name_then_forget_part4.jpg
ADDED
|
img/user_claim_wall/css_issue_part1.jpg
ADDED
|
img/user_claim_wall/css_issue_part2.jpg
ADDED
|
img/user_claim_wall/css_issue_part3.jpg
ADDED
|
img/user_claim_wall/css_issue_part4.jpg
ADDED
|
img/user_claim_wall/did_not_relaunch_sdwebui.jpg
ADDED
|
img/user_claim_wall/do_not_even_use_this_ext.jpg
ADDED
|
img/user_claim_wall/have_not_scan_model.jpg
ADDED
|
img/user_claim_wall/have_not_update_sdwebui.jpg
ADDED
|
img/user_claim_wall/request_a_feature_it_already_has.jpg
ADDED
|
javascript/civitai_helper.js
ADDED
|
@@ -0,0 +1,728 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
function ch_convert_file_path_to_url(path){
|
| 5 |
+
let prefix = "file=";
|
| 6 |
+
let path_to_url = path.replaceAll('\\', '/');
|
| 7 |
+
return prefix+path_to_url;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
function ch_img_node_str(path){
|
| 11 |
+
return `<img src='${ch_convert_file_path_to_url(path)}' style="width:24px"/>`;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
function ch_gradio_version(){
|
| 16 |
+
let foot = gradioApp().getElementById("footer");
|
| 17 |
+
if (!foot){return null;}
|
| 18 |
+
|
| 19 |
+
let versions = foot.querySelector(".versions");
|
| 20 |
+
if (!versions){return null;}
|
| 21 |
+
|
| 22 |
+
if (versions.innerHTML.indexOf("gradio: 3.16.2")>0) {
|
| 23 |
+
return "3.16.2";
|
| 24 |
+
} else {
|
| 25 |
+
return "3.23.0";
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
// send msg to python side by filling a hidden text box
|
| 32 |
+
// then will click a button to trigger an action
|
| 33 |
+
// msg is an object, not a string, will be stringify in this function
|
| 34 |
+
function send_ch_py_msg(msg){
|
| 35 |
+
console.log("run send_ch_py_msg")
|
| 36 |
+
let js_msg_txtbox = gradioApp().querySelector("#ch_js_msg_txtbox textarea");
|
| 37 |
+
if (js_msg_txtbox && msg) {
|
| 38 |
+
// fill to msg box
|
| 39 |
+
js_msg_txtbox.value = JSON.stringify(msg);
|
| 40 |
+
js_msg_txtbox.dispatchEvent(new Event("input"));
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
// get msg from python side from a hidden textbox
|
| 46 |
+
// normally this is an old msg, need to wait for a new msg
|
| 47 |
+
function get_ch_py_msg(){
|
| 48 |
+
console.log("run get_ch_py_msg")
|
| 49 |
+
const py_msg_txtbox = gradioApp().querySelector("#ch_py_msg_txtbox textarea");
|
| 50 |
+
if (py_msg_txtbox && py_msg_txtbox.value) {
|
| 51 |
+
console.log("find py_msg_txtbox");
|
| 52 |
+
console.log("py_msg_txtbox value: ");
|
| 53 |
+
console.log(py_msg_txtbox.value)
|
| 54 |
+
return py_msg_txtbox.value
|
| 55 |
+
} else {
|
| 56 |
+
return ""
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
// get msg from python side from a hidden textbox
|
| 62 |
+
// it will try once in every sencond, until it reach the max try times
|
| 63 |
+
const get_new_ch_py_msg = (max_count=3) => new Promise((resolve, reject) => {
|
| 64 |
+
console.log("run get_new_ch_py_msg")
|
| 65 |
+
|
| 66 |
+
let count = 0;
|
| 67 |
+
let new_msg = "";
|
| 68 |
+
let find_msg = false;
|
| 69 |
+
const interval = setInterval(() => {
|
| 70 |
+
const py_msg_txtbox = gradioApp().querySelector("#ch_py_msg_txtbox textarea");
|
| 71 |
+
count++;
|
| 72 |
+
|
| 73 |
+
if (py_msg_txtbox && py_msg_txtbox.value) {
|
| 74 |
+
console.log("find py_msg_txtbox");
|
| 75 |
+
console.log("py_msg_txtbox value: ");
|
| 76 |
+
console.log(py_msg_txtbox.value)
|
| 77 |
+
|
| 78 |
+
new_msg = py_msg_txtbox.value
|
| 79 |
+
if (new_msg != "") {
|
| 80 |
+
find_msg=true
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
if (find_msg) {
|
| 85 |
+
//clear msg in both sides
|
| 86 |
+
py_msg_txtbox.value = "";
|
| 87 |
+
py_msg_txtbox.dispatchEvent(new Event("input"));
|
| 88 |
+
|
| 89 |
+
resolve(new_msg);
|
| 90 |
+
clearInterval(interval);
|
| 91 |
+
} else if (count > max_count) {
|
| 92 |
+
//clear msg in both sides
|
| 93 |
+
py_msg_txtbox.value = "";
|
| 94 |
+
py_msg_txtbox.dispatchEvent(new Event("input"));
|
| 95 |
+
|
| 96 |
+
reject('');
|
| 97 |
+
clearInterval(interval);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
}, 1000);
|
| 101 |
+
})
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
function getActiveTabType() {
|
| 105 |
+
const currentTab = get_uiCurrentTabContent();
|
| 106 |
+
switch (currentTab.id) {
|
| 107 |
+
case "tab_txt2img":
|
| 108 |
+
return "txt2img";
|
| 109 |
+
case "tab_img2img":
|
| 110 |
+
return "img2img";
|
| 111 |
+
}
|
| 112 |
+
return null;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
function getActivePrompt() {
|
| 118 |
+
const currentTab = get_uiCurrentTabContent();
|
| 119 |
+
switch (currentTab.id) {
|
| 120 |
+
case "tab_txt2img":
|
| 121 |
+
return currentTab.querySelector("#txt2img_prompt textarea");
|
| 122 |
+
case "tab_img2img":
|
| 123 |
+
return currentTab.querySelector("#img2img_prompt textarea");
|
| 124 |
+
}
|
| 125 |
+
return null;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
function getActiveNegativePrompt() {
|
| 129 |
+
const currentTab = get_uiCurrentTabContent();
|
| 130 |
+
switch (currentTab.id) {
|
| 131 |
+
case "tab_txt2img":
|
| 132 |
+
return currentTab.querySelector("#txt2img_neg_prompt textarea");
|
| 133 |
+
case "tab_img2img":
|
| 134 |
+
return currentTab.querySelector("#img2img_neg_prompt textarea");
|
| 135 |
+
}
|
| 136 |
+
return null;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
//button's click function
|
| 141 |
+
async function open_model_url(event, model_type, search_term){
|
| 142 |
+
console.log("start open_model_url");
|
| 143 |
+
|
| 144 |
+
//get hidden components of extension
|
| 145 |
+
let js_open_url_btn = gradioApp().getElementById("ch_js_open_url_btn");
|
| 146 |
+
if (!js_open_url_btn) {
|
| 147 |
+
return
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
//msg to python side
|
| 152 |
+
let msg = {
|
| 153 |
+
"action": "",
|
| 154 |
+
"model_type": "",
|
| 155 |
+
"search_term": "",
|
| 156 |
+
"prompt": "",
|
| 157 |
+
"neg_prompt": "",
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
msg["action"] = "open_url";
|
| 162 |
+
msg["model_type"] = model_type;
|
| 163 |
+
msg["search_term"] = search_term;
|
| 164 |
+
msg["prompt"] = "";
|
| 165 |
+
msg["neg_prompt"] = "";
|
| 166 |
+
|
| 167 |
+
// fill to msg box
|
| 168 |
+
send_ch_py_msg(msg)
|
| 169 |
+
|
| 170 |
+
//click hidden button
|
| 171 |
+
js_open_url_btn.click();
|
| 172 |
+
|
| 173 |
+
// stop parent event
|
| 174 |
+
event.stopPropagation()
|
| 175 |
+
event.preventDefault()
|
| 176 |
+
|
| 177 |
+
//check response msg from python
|
| 178 |
+
let new_py_msg = await get_new_ch_py_msg();
|
| 179 |
+
console.log("new_py_msg:");
|
| 180 |
+
console.log(new_py_msg);
|
| 181 |
+
|
| 182 |
+
//check msg
|
| 183 |
+
if (new_py_msg) {
|
| 184 |
+
let py_msg_json = JSON.parse(new_py_msg);
|
| 185 |
+
//check for url
|
| 186 |
+
if (py_msg_json && py_msg_json.content) {
|
| 187 |
+
if (py_msg_json.content.url) {
|
| 188 |
+
window.open(py_msg_json.content.url, "_blank");
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
console.log("end open_model_url");
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
function add_trigger_words(event, model_type, search_term){
|
| 203 |
+
console.log("start add_trigger_words");
|
| 204 |
+
|
| 205 |
+
//get hidden components of extension
|
| 206 |
+
let js_add_trigger_words_btn = gradioApp().getElementById("ch_js_add_trigger_words_btn");
|
| 207 |
+
if (!js_add_trigger_words_btn) {
|
| 208 |
+
return
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
//msg to python side
|
| 213 |
+
let msg = {
|
| 214 |
+
"action": "",
|
| 215 |
+
"model_type": "",
|
| 216 |
+
"search_term": "",
|
| 217 |
+
"prompt": "",
|
| 218 |
+
"neg_prompt": "",
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
msg["action"] = "add_trigger_words";
|
| 222 |
+
msg["model_type"] = model_type;
|
| 223 |
+
msg["search_term"] = search_term;
|
| 224 |
+
msg["neg_prompt"] = "";
|
| 225 |
+
|
| 226 |
+
// get active prompt
|
| 227 |
+
let act_prompt = getActivePrompt();
|
| 228 |
+
msg["prompt"] = act_prompt.value;
|
| 229 |
+
|
| 230 |
+
// fill to msg box
|
| 231 |
+
send_ch_py_msg(msg)
|
| 232 |
+
|
| 233 |
+
//click hidden button
|
| 234 |
+
js_add_trigger_words_btn.click();
|
| 235 |
+
|
| 236 |
+
console.log("end add_trigger_words");
|
| 237 |
+
|
| 238 |
+
event.stopPropagation()
|
| 239 |
+
event.preventDefault()
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
function use_preview_prompt(event, model_type, search_term){
|
| 245 |
+
console.log("start use_preview_prompt");
|
| 246 |
+
|
| 247 |
+
//get hidden components of extension
|
| 248 |
+
let js_use_preview_prompt_btn = gradioApp().getElementById("ch_js_use_preview_prompt_btn");
|
| 249 |
+
if (!js_use_preview_prompt_btn) {
|
| 250 |
+
return
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
//msg to python side
|
| 254 |
+
let msg = {
|
| 255 |
+
"action": "",
|
| 256 |
+
"model_type": "",
|
| 257 |
+
"search_term": "",
|
| 258 |
+
"prompt": "",
|
| 259 |
+
"neg_prompt": "",
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
msg["action"] = "use_preview_prompt";
|
| 263 |
+
msg["model_type"] = model_type;
|
| 264 |
+
msg["search_term"] = search_term;
|
| 265 |
+
|
| 266 |
+
// get active prompt
|
| 267 |
+
let act_prompt = getActivePrompt();
|
| 268 |
+
msg["prompt"] = act_prompt.value;
|
| 269 |
+
|
| 270 |
+
// get active neg prompt
|
| 271 |
+
let neg_prompt = getActiveNegativePrompt();
|
| 272 |
+
msg["neg_prompt"] = neg_prompt.value;
|
| 273 |
+
|
| 274 |
+
// fill to msg box
|
| 275 |
+
send_ch_py_msg(msg)
|
| 276 |
+
|
| 277 |
+
//click hidden button
|
| 278 |
+
js_use_preview_prompt_btn.click();
|
| 279 |
+
|
| 280 |
+
console.log("end use_preview_prompt");
|
| 281 |
+
|
| 282 |
+
event.stopPropagation()
|
| 283 |
+
event.preventDefault()
|
| 284 |
+
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
// download model's new version into SD at python side
|
| 290 |
+
function ch_dl_model_new_version(event, model_path, version_id, download_url){
|
| 291 |
+
console.log("start ch_dl_model_new_version");
|
| 292 |
+
|
| 293 |
+
// must confirm before downloading
|
| 294 |
+
let dl_confirm = "\nConfirm to download.\n\nCheck Download Model Section's log and console log for detail.";
|
| 295 |
+
if (!confirm(dl_confirm)) {
|
| 296 |
+
return
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
//get hidden components of extension
|
| 300 |
+
let js_dl_model_new_version_btn = gradioApp().getElementById("ch_js_dl_model_new_version_btn");
|
| 301 |
+
if (!js_dl_model_new_version_btn) {
|
| 302 |
+
return
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
//msg to python side
|
| 306 |
+
let msg = {
|
| 307 |
+
"action": "",
|
| 308 |
+
"model_path": "",
|
| 309 |
+
"version_id": "",
|
| 310 |
+
"download_url": "",
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
msg["action"] = "dl_model_new_version";
|
| 314 |
+
msg["model_path"] = model_path;
|
| 315 |
+
msg["version_id"] = version_id;
|
| 316 |
+
msg["download_url"] = download_url;
|
| 317 |
+
|
| 318 |
+
// fill to msg box
|
| 319 |
+
send_ch_py_msg(msg)
|
| 320 |
+
|
| 321 |
+
//click hidden button
|
| 322 |
+
js_dl_model_new_version_btn.click();
|
| 323 |
+
|
| 324 |
+
console.log("end dl_model_new_version");
|
| 325 |
+
|
| 326 |
+
event.stopPropagation()
|
| 327 |
+
event.preventDefault()
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
onUiLoaded(() => {
|
| 334 |
+
|
| 335 |
+
//get gradio version
|
| 336 |
+
let gradio_ver = ch_gradio_version();
|
| 337 |
+
console.log("gradio_ver:" + gradio_ver);
|
| 338 |
+
|
| 339 |
+
// get all extra network tabs
|
| 340 |
+
let tab_prefix_list = ["txt2img", "img2img"];
|
| 341 |
+
let model_type_list = ["textual_inversion", "hypernetworks", "checkpoints", "lora"];
|
| 342 |
+
let cardid_suffix = "cards";
|
| 343 |
+
|
| 344 |
+
//get init py msg
|
| 345 |
+
// let init_py_msg_str = get_ch_py_msg();
|
| 346 |
+
// let extension_path = "";
|
| 347 |
+
// if (!init_py_msg_str) {
|
| 348 |
+
// console.log("Can not get init_py_msg");
|
| 349 |
+
// } else {
|
| 350 |
+
// init_py_msg = JSON.parse(init_py_msg_str);
|
| 351 |
+
// if (init_py_msg) {
|
| 352 |
+
// extension_path = init_py_msg.extension_path;
|
| 353 |
+
// console.log("get extension path: " + extension_path);
|
| 354 |
+
// }
|
| 355 |
+
// }
|
| 356 |
+
|
| 357 |
+
// //icon image node as string
|
| 358 |
+
// function icon(icon_name){
|
| 359 |
+
// let icon_path = extension_path+"/icon/"+icon_name;
|
| 360 |
+
// return ch_img_node_str(icon_path);
|
| 361 |
+
// }
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
// update extra network tab pages' cards
|
| 365 |
+
// * replace "replace preview" text button into an icon
|
| 366 |
+
// * add 3 button to each card:
|
| 367 |
+
// - open model url 🌐
|
| 368 |
+
// - add trigger words 💡
|
| 369 |
+
// - use preview image's prompt 🏷️
|
| 370 |
+
// notice: javascript can not get response from python side
|
| 371 |
+
// so, these buttons just sent request to python
|
| 372 |
+
// then, python side gonna open url and update prompt text box, without telling js side.
|
| 373 |
+
function update_card_for_civitai(){
|
| 374 |
+
|
| 375 |
+
//css
|
| 376 |
+
let btn_margin = "0px 5px";
|
| 377 |
+
let btn_fontSize = "200%";
|
| 378 |
+
let btn_thumb_fontSize = "100%";
|
| 379 |
+
let btn_thumb_display = "inline";
|
| 380 |
+
let btn_thumb_pos = "static";
|
| 381 |
+
let btn_thumb_backgroundImage = "none";
|
| 382 |
+
let btn_thumb_background = "rgba(0, 0, 0, 0.8)";
|
| 383 |
+
|
| 384 |
+
let ch_btn_txts = ['🌐', '💡', '🏷️'];
|
| 385 |
+
let replace_preview_text = getTranslation("replace preview");
|
| 386 |
+
if (!replace_preview_text) {
|
| 387 |
+
replace_preview_text = "replace preview";
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
// get component
|
| 393 |
+
let ch_always_display_ckb = gradioApp().querySelector("#ch_always_display_ckb input");
|
| 394 |
+
let ch_show_btn_on_thumb_ckb = gradioApp().querySelector("#ch_show_btn_on_thumb_ckb input");
|
| 395 |
+
let ch_always_display = false;
|
| 396 |
+
let ch_show_btn_on_thumb = false;
|
| 397 |
+
if (ch_always_display_ckb) {
|
| 398 |
+
ch_always_display = ch_always_display_ckb.checked;
|
| 399 |
+
}
|
| 400 |
+
if (ch_show_btn_on_thumb_ckb) {
|
| 401 |
+
ch_show_btn_on_thumb = ch_show_btn_on_thumb_ckb.checked;
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
//change all "replace preview" into an icon
|
| 406 |
+
let extra_network_id = "";
|
| 407 |
+
let extra_network_node = null;
|
| 408 |
+
let metadata_button = null;
|
| 409 |
+
let additional_node = null;
|
| 410 |
+
let replace_preview_btn = null;
|
| 411 |
+
let ul_node = null;
|
| 412 |
+
let search_term_node = null;
|
| 413 |
+
let search_term = "";
|
| 414 |
+
let model_type = "";
|
| 415 |
+
let cards = null;
|
| 416 |
+
let need_to_add_buttons = false;
|
| 417 |
+
let is_thumb_mode = false;
|
| 418 |
+
|
| 419 |
+
//get current tab
|
| 420 |
+
let active_tab_type = getActiveTabType();
|
| 421 |
+
if (!active_tab_type){active_tab_type = "txt2img";}
|
| 422 |
+
|
| 423 |
+
for (const tab_prefix of tab_prefix_list) {
|
| 424 |
+
if (tab_prefix != active_tab_type) {continue;}
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
//find out current selected model type tab
|
| 428 |
+
let active_extra_tab_type = "";
|
| 429 |
+
let extra_tabs = gradioApp().getElementById(tab_prefix+"_extra_tabs");
|
| 430 |
+
if (!extra_tabs) {console.log("can not find extra_tabs: " + tab_prefix+"_extra_tabs");}
|
| 431 |
+
|
| 432 |
+
//get active extratab
|
| 433 |
+
const active_extra_tab = Array.from(get_uiCurrentTabContent().querySelectorAll('.extra-network-cards,.extra-network-thumbs'))
|
| 434 |
+
.find(el => el.closest('.tabitem').style.display === 'block')
|
| 435 |
+
?.id.match(/^(txt2img|img2img)_(.+)_cards$/)[2]
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
console.log("found active tab: " + active_extra_tab);
|
| 439 |
+
|
| 440 |
+
switch (active_extra_tab) {
|
| 441 |
+
case "textual_inversion":
|
| 442 |
+
active_extra_tab_type = "ti";
|
| 443 |
+
break;
|
| 444 |
+
case "hypernetworks":
|
| 445 |
+
active_extra_tab_type = "hyper";
|
| 446 |
+
break;
|
| 447 |
+
case "checkpoints":
|
| 448 |
+
active_extra_tab_type = "ckp";
|
| 449 |
+
break;
|
| 450 |
+
case "lora":
|
| 451 |
+
active_extra_tab_type = "lora";
|
| 452 |
+
break;
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
for (const js_model_type of model_type_list) {
|
| 457 |
+
//get model_type for python side
|
| 458 |
+
switch (js_model_type) {
|
| 459 |
+
case "textual_inversion":
|
| 460 |
+
model_type = "ti";
|
| 461 |
+
break;
|
| 462 |
+
case "hypernetworks":
|
| 463 |
+
model_type = "hyper";
|
| 464 |
+
break;
|
| 465 |
+
case "checkpoints":
|
| 466 |
+
model_type = "ckp";
|
| 467 |
+
break;
|
| 468 |
+
case "lora":
|
| 469 |
+
model_type = "lora";
|
| 470 |
+
break;
|
| 471 |
+
}
|
| 472 |
+
|
| 473 |
+
if (!model_type) {
|
| 474 |
+
console.log("can not get model_type from: " + js_model_type);
|
| 475 |
+
continue;
|
| 476 |
+
}
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
//only handle current sub-tab
|
| 480 |
+
if (model_type != active_extra_tab_type) {
|
| 481 |
+
continue;
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
console.log("handle active extra tab");
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
extra_network_id = tab_prefix+"_"+js_model_type+"_"+cardid_suffix;
|
| 488 |
+
// console.log("searching extra_network_node: " + extra_network_id);
|
| 489 |
+
extra_network_node = gradioApp().getElementById(extra_network_id);
|
| 490 |
+
// check if extr network is under thumbnail mode
|
| 491 |
+
is_thumb_mode = false
|
| 492 |
+
if (extra_network_node) {
|
| 493 |
+
if (extra_network_node.className == "extra-network-thumbs") {
|
| 494 |
+
console.log(extra_network_id + " is in thumbnail mode");
|
| 495 |
+
is_thumb_mode = true;
|
| 496 |
+
// if (!ch_show_btn_on_thumb) {continue;}
|
| 497 |
+
}
|
| 498 |
+
} else {
|
| 499 |
+
console.log("can not find extra_network_node: " + extra_network_id);
|
| 500 |
+
continue;
|
| 501 |
+
}
|
| 502 |
+
// console.log("find extra_network_node: " + extra_network_id);
|
| 503 |
+
|
| 504 |
+
// get all card nodes
|
| 505 |
+
cards = extra_network_node.querySelectorAll(".card");
|
| 506 |
+
for (let card of cards) {
|
| 507 |
+
//metadata_buttoncard
|
| 508 |
+
metadata_button = card.querySelector(".metadata-button");
|
| 509 |
+
//additional node
|
| 510 |
+
additional_node = card.querySelector(".actions .additional");
|
| 511 |
+
//get ul node, which is the parent of all buttons
|
| 512 |
+
ul_node = card.querySelector(".actions .additional ul");
|
| 513 |
+
// replace preview text button
|
| 514 |
+
replace_preview_btn = card.querySelector(".actions .additional a");
|
| 515 |
+
|
| 516 |
+
// check thumb mode
|
| 517 |
+
if (is_thumb_mode) {
|
| 518 |
+
additional_node.style.display = null;
|
| 519 |
+
|
| 520 |
+
if (ch_show_btn_on_thumb) {
|
| 521 |
+
ul_node.style.background = btn_thumb_background;
|
| 522 |
+
} else {
|
| 523 |
+
//reset
|
| 524 |
+
ul_node.style.background = null;
|
| 525 |
+
// console.log("remove existed buttons");
|
| 526 |
+
// remove existed buttons
|
| 527 |
+
if (ul_node) {
|
| 528 |
+
// find all .a child nodes
|
| 529 |
+
let atags = ul_node.querySelectorAll("a");
|
| 530 |
+
|
| 531 |
+
for (let atag of atags) {
|
| 532 |
+
//reset display
|
| 533 |
+
atag.style.display = null;
|
| 534 |
+
//remove extension's button
|
| 535 |
+
if (ch_btn_txts.indexOf(atag.innerHTML)>=0) {
|
| 536 |
+
//need to remove
|
| 537 |
+
ul_node.removeChild(atag);
|
| 538 |
+
} else {
|
| 539 |
+
//do not remove, just reset
|
| 540 |
+
atag.innerHTML = replace_preview_text;
|
| 541 |
+
atag.style.display = null;
|
| 542 |
+
atag.style.fontSize = null;
|
| 543 |
+
atag.style.position = null;
|
| 544 |
+
atag.style.backgroundImage = null;
|
| 545 |
+
}
|
| 546 |
+
}
|
| 547 |
+
|
| 548 |
+
//also remove br tag in ul
|
| 549 |
+
let brtag = ul_node.querySelector("br");
|
| 550 |
+
if (brtag) {
|
| 551 |
+
ul_node.removeChild(brtag);
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
}
|
| 555 |
+
//just reset and remove nodes, do nothing else
|
| 556 |
+
continue;
|
| 557 |
+
|
| 558 |
+
}
|
| 559 |
+
|
| 560 |
+
} else {
|
| 561 |
+
// full preview mode
|
| 562 |
+
if (ch_always_display) {
|
| 563 |
+
additional_node.style.display = "block";
|
| 564 |
+
} else {
|
| 565 |
+
additional_node.style.display = null;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
// remove br tag
|
| 569 |
+
let brtag = ul_node.querySelector("br");
|
| 570 |
+
if (brtag) {
|
| 571 |
+
ul_node.removeChild(brtag);
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
+
// change replace preview text button into icon
|
| 577 |
+
if (replace_preview_btn) {
|
| 578 |
+
if (replace_preview_btn.innerHTML !== "🖼️") {
|
| 579 |
+
need_to_add_buttons = true;
|
| 580 |
+
replace_preview_btn.innerHTML = "🖼️";
|
| 581 |
+
if (!is_thumb_mode) {
|
| 582 |
+
replace_preview_btn.style.fontSize = btn_fontSize;
|
| 583 |
+
replace_preview_btn.style.margin = btn_margin;
|
| 584 |
+
} else {
|
| 585 |
+
replace_preview_btn.style.display = btn_thumb_display;
|
| 586 |
+
replace_preview_btn.style.fontSize = btn_thumb_fontSize;
|
| 587 |
+
replace_preview_btn.style.position = btn_thumb_pos;
|
| 588 |
+
replace_preview_btn.style.backgroundImage = btn_thumb_backgroundImage;
|
| 589 |
+
}
|
| 590 |
+
|
| 591 |
+
}
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
+
if (!need_to_add_buttons) {
|
| 595 |
+
continue;
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
|
| 599 |
+
// search_term node
|
| 600 |
+
// search_term = subfolder path + model name + ext
|
| 601 |
+
search_term_node = card.querySelector(".actions .additional .search_term");
|
| 602 |
+
if (!search_term_node){
|
| 603 |
+
console.log("can not find search_term node for cards in " + extra_network_id);
|
| 604 |
+
continue;
|
| 605 |
+
}
|
| 606 |
+
|
| 607 |
+
// get search_term
|
| 608 |
+
search_term = search_term_node.innerHTML;
|
| 609 |
+
if (!search_term) {
|
| 610 |
+
console.log("search_term is empty for cards in " + extra_network_id);
|
| 611 |
+
continue;
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
// if (is_thumb_mode) {
|
| 617 |
+
// ul_node.style.background = btn_thumb_background;
|
| 618 |
+
// }
|
| 619 |
+
|
| 620 |
+
// then we need to add 3 buttons to each ul node:
|
| 621 |
+
let open_url_node = document.createElement("a");
|
| 622 |
+
open_url_node.href = "#";
|
| 623 |
+
open_url_node.innerHTML = "🌐";
|
| 624 |
+
if (!is_thumb_mode) {
|
| 625 |
+
open_url_node.style.fontSize = btn_fontSize;
|
| 626 |
+
open_url_node.style.margin = btn_margin;
|
| 627 |
+
} else {
|
| 628 |
+
open_url_node.style.display = btn_thumb_display;
|
| 629 |
+
open_url_node.style.fontSize = btn_thumb_fontSize;
|
| 630 |
+
open_url_node.style.position = btn_thumb_pos;
|
| 631 |
+
open_url_node.style.backgroundImage = btn_thumb_backgroundImage;
|
| 632 |
+
}
|
| 633 |
+
open_url_node.title = "Open this model's civitai url";
|
| 634 |
+
open_url_node.setAttribute("onclick","open_model_url(event, '"+model_type+"', '"+search_term+"')");
|
| 635 |
+
|
| 636 |
+
let add_trigger_words_node = document.createElement("a");
|
| 637 |
+
add_trigger_words_node.href = "#";
|
| 638 |
+
add_trigger_words_node.innerHTML = "💡";
|
| 639 |
+
if (!is_thumb_mode) {
|
| 640 |
+
add_trigger_words_node.style.fontSize = btn_fontSize;
|
| 641 |
+
add_trigger_words_node.style.margin = btn_margin;
|
| 642 |
+
} else {
|
| 643 |
+
add_trigger_words_node.style.display = btn_thumb_display;
|
| 644 |
+
add_trigger_words_node.style.fontSize = btn_thumb_fontSize;
|
| 645 |
+
add_trigger_words_node.style.position = btn_thumb_pos;
|
| 646 |
+
add_trigger_words_node.style.backgroundImage = btn_thumb_backgroundImage;
|
| 647 |
+
}
|
| 648 |
+
|
| 649 |
+
add_trigger_words_node.title = "Add trigger words to prompt";
|
| 650 |
+
add_trigger_words_node.setAttribute("onclick","add_trigger_words(event, '"+model_type+"', '"+search_term+"')");
|
| 651 |
+
|
| 652 |
+
let use_preview_prompt_node = document.createElement("a");
|
| 653 |
+
use_preview_prompt_node.href = "#";
|
| 654 |
+
use_preview_prompt_node.innerHTML = "🏷️";
|
| 655 |
+
if (!is_thumb_mode) {
|
| 656 |
+
use_preview_prompt_node.style.fontSize = btn_fontSize;
|
| 657 |
+
use_preview_prompt_node.style.margin = btn_margin;
|
| 658 |
+
} else {
|
| 659 |
+
use_preview_prompt_node.style.display = btn_thumb_display;
|
| 660 |
+
use_preview_prompt_node.style.fontSize = btn_thumb_fontSize;
|
| 661 |
+
use_preview_prompt_node.style.position = btn_thumb_pos;
|
| 662 |
+
use_preview_prompt_node.style.backgroundImage = btn_thumb_backgroundImage;
|
| 663 |
+
}
|
| 664 |
+
use_preview_prompt_node.title = "Use prompt from preview image";
|
| 665 |
+
use_preview_prompt_node.setAttribute("onclick","use_preview_prompt(event, '"+model_type+"', '"+search_term+"')");
|
| 666 |
+
|
| 667 |
+
//add to card
|
| 668 |
+
ul_node.appendChild(open_url_node);
|
| 669 |
+
//add br if metadata_button exists
|
| 670 |
+
if (is_thumb_mode && metadata_button) {
|
| 671 |
+
ul_node.appendChild(document.createElement("br"));
|
| 672 |
+
}
|
| 673 |
+
ul_node.appendChild(add_trigger_words_node);
|
| 674 |
+
ul_node.appendChild(use_preview_prompt_node);
|
| 675 |
+
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
|
| 679 |
+
}
|
| 680 |
+
|
| 681 |
+
|
| 682 |
+
}
|
| 683 |
+
}
|
| 684 |
+
|
| 685 |
+
|
| 686 |
+
}
|
| 687 |
+
|
| 688 |
+
|
| 689 |
+
let tab_id = ""
|
| 690 |
+
let extra_tab = null;
|
| 691 |
+
let extra_toolbar = null;
|
| 692 |
+
let extra_network_refresh_btn = null;
|
| 693 |
+
//add refresh button to extra network's toolbar
|
| 694 |
+
for (let prefix of tab_prefix_list) {
|
| 695 |
+
tab_id = prefix + "_extra_tabs";
|
| 696 |
+
extra_tab = gradioApp().getElementById(tab_id);
|
| 697 |
+
|
| 698 |
+
//get toolbar
|
| 699 |
+
//get Refresh button
|
| 700 |
+
extra_network_refresh_btn = gradioApp().getElementById(prefix+"_extra_refresh");
|
| 701 |
+
|
| 702 |
+
|
| 703 |
+
if (!extra_network_refresh_btn){
|
| 704 |
+
console.log("can not get extra network refresh button for " + tab_id);
|
| 705 |
+
continue;
|
| 706 |
+
}
|
| 707 |
+
|
| 708 |
+
// add refresh button to toolbar
|
| 709 |
+
let ch_refresh = document.createElement("button");
|
| 710 |
+
ch_refresh.innerHTML = "🔁";
|
| 711 |
+
ch_refresh.title = "Refresh Civitai Helper's additional buttons";
|
| 712 |
+
ch_refresh.className = "lg secondary gradio-button";
|
| 713 |
+
ch_refresh.style.fontSize = "200%";
|
| 714 |
+
ch_refresh.onclick = update_card_for_civitai;
|
| 715 |
+
|
| 716 |
+
extra_network_refresh_btn.parentNode.appendChild(ch_refresh);
|
| 717 |
+
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
|
| 721 |
+
//run it once
|
| 722 |
+
update_card_for_civitai();
|
| 723 |
+
|
| 724 |
+
|
| 725 |
+
});
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
|
scripts/ch_lib/__init__.py
ADDED
|
File without changes
|
scripts/ch_lib/civitai.py
ADDED
|
@@ -0,0 +1,612 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
# handle msg between js and python side
|
| 3 |
+
import os
|
| 4 |
+
import time
|
| 5 |
+
import json
|
| 6 |
+
import re
|
| 7 |
+
import requests
|
| 8 |
+
from . import util
|
| 9 |
+
from . import model
|
| 10 |
+
from . import setting
|
| 11 |
+
|
| 12 |
+
suffix = ".civitai"
|
| 13 |
+
|
| 14 |
+
url_dict = {
|
| 15 |
+
"modelPage":"https://civitai.com/models/",
|
| 16 |
+
"modelId": "https://civitai.com/api/v1/models/",
|
| 17 |
+
"modelVersionId": "https://civitai.com/api/v1/model-versions/",
|
| 18 |
+
"hash": "https://civitai.com/api/v1/model-versions/by-hash/"
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
model_type_dict = {
|
| 22 |
+
"Checkpoint": "ckp",
|
| 23 |
+
"TextualInversion": "ti",
|
| 24 |
+
"Hypernetwork": "hyper",
|
| 25 |
+
"LORA": "lora",
|
| 26 |
+
"LoCon": "lora",
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
# get image with full size
|
| 32 |
+
# width is in number, not string
|
| 33 |
+
# return: url str
|
| 34 |
+
def get_full_size_image_url(image_url, width):
|
| 35 |
+
return re.sub('/width=\d+/', '/width=' + str(width) + '/', image_url)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# use this sha256 to get model info from civitai
|
| 39 |
+
# return: model info dict
|
| 40 |
+
def get_model_info_by_hash(hash:str):
|
| 41 |
+
util.printD("Request model info from civitai")
|
| 42 |
+
|
| 43 |
+
if not hash:
|
| 44 |
+
util.printD("hash is empty")
|
| 45 |
+
return
|
| 46 |
+
|
| 47 |
+
r = requests.get(url_dict["hash"]+hash, headers=util.def_headers, proxies=util.proxies)
|
| 48 |
+
if not r.ok:
|
| 49 |
+
if r.status_code == 404:
|
| 50 |
+
# this is not a civitai model
|
| 51 |
+
util.printD("Civitai does not have this model")
|
| 52 |
+
return {}
|
| 53 |
+
else:
|
| 54 |
+
util.printD("Get error code: " + str(r.status_code))
|
| 55 |
+
util.printD(r.text)
|
| 56 |
+
return
|
| 57 |
+
|
| 58 |
+
# try to get content
|
| 59 |
+
content = None
|
| 60 |
+
try:
|
| 61 |
+
content = r.json()
|
| 62 |
+
except Exception as e:
|
| 63 |
+
util.printD("Parse response json failed")
|
| 64 |
+
util.printD(str(e))
|
| 65 |
+
util.printD("response:")
|
| 66 |
+
util.printD(r.text)
|
| 67 |
+
return
|
| 68 |
+
|
| 69 |
+
if not content:
|
| 70 |
+
util.printD("error, content from civitai is None")
|
| 71 |
+
return
|
| 72 |
+
|
| 73 |
+
return content
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def get_model_info_by_id(id:str) -> dict:
|
| 78 |
+
util.printD("Request model info from civitai")
|
| 79 |
+
|
| 80 |
+
if not id:
|
| 81 |
+
util.printD("id is empty")
|
| 82 |
+
return
|
| 83 |
+
|
| 84 |
+
r = requests.get(url_dict["modelId"]+str(id), headers=util.def_headers, proxies=util.proxies)
|
| 85 |
+
if not r.ok:
|
| 86 |
+
if r.status_code == 404:
|
| 87 |
+
# this is not a civitai model
|
| 88 |
+
util.printD("Civitai does not have this model")
|
| 89 |
+
return {}
|
| 90 |
+
else:
|
| 91 |
+
util.printD("Get error code: " + str(r.status_code))
|
| 92 |
+
util.printD(r.text)
|
| 93 |
+
return
|
| 94 |
+
|
| 95 |
+
# try to get content
|
| 96 |
+
content = None
|
| 97 |
+
try:
|
| 98 |
+
content = r.json()
|
| 99 |
+
except Exception as e:
|
| 100 |
+
util.printD("Parse response json failed")
|
| 101 |
+
util.printD(str(e))
|
| 102 |
+
util.printD("response:")
|
| 103 |
+
util.printD(r.text)
|
| 104 |
+
return
|
| 105 |
+
|
| 106 |
+
if not content:
|
| 107 |
+
util.printD("error, content from civitai is None")
|
| 108 |
+
return
|
| 109 |
+
|
| 110 |
+
return content
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def get_version_info_by_version_id(id:str) -> dict:
|
| 114 |
+
util.printD("Request version info from civitai")
|
| 115 |
+
|
| 116 |
+
if not id:
|
| 117 |
+
util.printD("id is empty")
|
| 118 |
+
return
|
| 119 |
+
|
| 120 |
+
r = requests.get(url_dict["modelVersionId"]+str(id), headers=util.def_headers, proxies=util.proxies)
|
| 121 |
+
if not r.ok:
|
| 122 |
+
if r.status_code == 404:
|
| 123 |
+
# this is not a civitai model
|
| 124 |
+
util.printD("Civitai does not have this model version")
|
| 125 |
+
return {}
|
| 126 |
+
else:
|
| 127 |
+
util.printD("Get error code: " + str(r.status_code))
|
| 128 |
+
util.printD(r.text)
|
| 129 |
+
return
|
| 130 |
+
|
| 131 |
+
# try to get content
|
| 132 |
+
content = None
|
| 133 |
+
try:
|
| 134 |
+
content = r.json()
|
| 135 |
+
except Exception as e:
|
| 136 |
+
util.printD("Parse response json failed")
|
| 137 |
+
util.printD(str(e))
|
| 138 |
+
util.printD("response:")
|
| 139 |
+
util.printD(r.text)
|
| 140 |
+
return
|
| 141 |
+
|
| 142 |
+
if not content:
|
| 143 |
+
util.printD("error, content from civitai is None")
|
| 144 |
+
return
|
| 145 |
+
|
| 146 |
+
return content
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def get_version_info_by_model_id(id:str) -> dict:
|
| 150 |
+
|
| 151 |
+
model_info = get_model_info_by_id(id)
|
| 152 |
+
if not model_info:
|
| 153 |
+
util.printD(f"Failed to get model info by id: {id}")
|
| 154 |
+
return
|
| 155 |
+
|
| 156 |
+
# check content to get version id
|
| 157 |
+
if "modelVersions" not in model_info.keys():
|
| 158 |
+
util.printD("There is no modelVersions in this model_info")
|
| 159 |
+
return
|
| 160 |
+
|
| 161 |
+
if not model_info["modelVersions"]:
|
| 162 |
+
util.printD("modelVersions is None")
|
| 163 |
+
return
|
| 164 |
+
|
| 165 |
+
if len(model_info["modelVersions"])==0:
|
| 166 |
+
util.printD("modelVersions is Empty")
|
| 167 |
+
return
|
| 168 |
+
|
| 169 |
+
def_version = model_info["modelVersions"][0]
|
| 170 |
+
if not def_version:
|
| 171 |
+
util.printD("default version is None")
|
| 172 |
+
return
|
| 173 |
+
|
| 174 |
+
if "id" not in def_version.keys():
|
| 175 |
+
util.printD("default version has no id")
|
| 176 |
+
return
|
| 177 |
+
|
| 178 |
+
version_id = def_version["id"]
|
| 179 |
+
|
| 180 |
+
if not version_id:
|
| 181 |
+
util.printD("default version's id is None")
|
| 182 |
+
return
|
| 183 |
+
|
| 184 |
+
# get version info
|
| 185 |
+
version_info = get_version_info_by_version_id(str(version_id))
|
| 186 |
+
if not version_info:
|
| 187 |
+
util.printD(f"Failed to get version info by version_id: {version_id}")
|
| 188 |
+
return
|
| 189 |
+
|
| 190 |
+
return version_info
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
# get model info file's content by model type and search_term
|
| 196 |
+
# parameter: model_type, search_term
|
| 197 |
+
# return: model_info
|
| 198 |
+
def load_model_info_by_search_term(model_type, search_term):
|
| 199 |
+
util.printD(f"Load model info of {search_term} in {model_type}")
|
| 200 |
+
if model_type not in model.folders.keys():
|
| 201 |
+
util.printD("unknow model type: " + model_type)
|
| 202 |
+
return
|
| 203 |
+
|
| 204 |
+
# search_term = subfolderpath + model name + ext. And it always start with a / even there is no sub folder
|
| 205 |
+
base, ext = os.path.splitext(search_term)
|
| 206 |
+
model_info_base = base
|
| 207 |
+
if base[:1] == "/":
|
| 208 |
+
model_info_base = base[1:]
|
| 209 |
+
|
| 210 |
+
model_folder = model.folders[model_type]
|
| 211 |
+
model_info_filename = model_info_base + suffix + model.info_ext
|
| 212 |
+
model_info_filepath = os.path.join(model_folder, model_info_filename)
|
| 213 |
+
|
| 214 |
+
if not os.path.isfile(model_info_filepath):
|
| 215 |
+
util.printD("Can not find model info file: " + model_info_filepath)
|
| 216 |
+
return
|
| 217 |
+
|
| 218 |
+
return model.load_model_info(model_info_filepath)
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
# get model file names by model type
|
| 225 |
+
# parameter: model_type - string
|
| 226 |
+
# parameter: filter - dict, which kind of model you need
|
| 227 |
+
# return: model name list
|
| 228 |
+
def get_model_names_by_type_and_filter(model_type:str, filter:dict) -> list:
|
| 229 |
+
|
| 230 |
+
model_folder = model.folders[model_type]
|
| 231 |
+
|
| 232 |
+
# set filter
|
| 233 |
+
# only get models don't have a civitai info file
|
| 234 |
+
no_info_only = False
|
| 235 |
+
empty_info_only = False
|
| 236 |
+
|
| 237 |
+
if filter:
|
| 238 |
+
if "no_info_only" in filter.keys():
|
| 239 |
+
no_info_only = filter["no_info_only"]
|
| 240 |
+
if "empty_info_only" in filter.keys():
|
| 241 |
+
empty_info_only = filter["empty_info_only"]
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
# get information from filter
|
| 246 |
+
# only get those model names don't have a civitai model info file
|
| 247 |
+
model_names = []
|
| 248 |
+
for root, dirs, files in os.walk(model_folder, followlinks=True):
|
| 249 |
+
for filename in files:
|
| 250 |
+
item = os.path.join(root, filename)
|
| 251 |
+
# check extension
|
| 252 |
+
base, ext = os.path.splitext(item)
|
| 253 |
+
if ext in model.exts:
|
| 254 |
+
# find a model
|
| 255 |
+
|
| 256 |
+
# check filter
|
| 257 |
+
if no_info_only:
|
| 258 |
+
# check model info file
|
| 259 |
+
info_file = base + suffix + model.info_ext
|
| 260 |
+
if os.path.isfile(info_file):
|
| 261 |
+
continue
|
| 262 |
+
|
| 263 |
+
if empty_info_only:
|
| 264 |
+
# check model info file
|
| 265 |
+
info_file = base + suffix + model.info_ext
|
| 266 |
+
if os.path.isfile(info_file):
|
| 267 |
+
# load model info
|
| 268 |
+
model_info = model.load_model_info(info_file)
|
| 269 |
+
# check content
|
| 270 |
+
if model_info:
|
| 271 |
+
if "id" in model_info.keys():
|
| 272 |
+
# find a non-empty model info file
|
| 273 |
+
continue
|
| 274 |
+
|
| 275 |
+
model_names.append(filename)
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
return model_names
|
| 279 |
+
|
| 280 |
+
def get_model_names_by_input(model_type, empty_info_only):
|
| 281 |
+
return get_model_names_by_type_and_filter(model_type, {"empty_info_only":empty_info_only})
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
# get id from url
|
| 285 |
+
def get_model_id_from_url(url:str) -> str:
|
| 286 |
+
util.printD("Run get_model_id_from_url")
|
| 287 |
+
id = ""
|
| 288 |
+
|
| 289 |
+
if not url:
|
| 290 |
+
util.printD("url or model id can not be empty")
|
| 291 |
+
return ""
|
| 292 |
+
|
| 293 |
+
if url.isnumeric():
|
| 294 |
+
# is already an id
|
| 295 |
+
id = str(url)
|
| 296 |
+
return id
|
| 297 |
+
|
| 298 |
+
s = re.sub("\\?.+$", "", url).split("/")
|
| 299 |
+
if len(s) < 2:
|
| 300 |
+
util.printD("url is not valid")
|
| 301 |
+
return ""
|
| 302 |
+
|
| 303 |
+
if s[-2].isnumeric():
|
| 304 |
+
id = s[-2]
|
| 305 |
+
elif s[-1].isnumeric():
|
| 306 |
+
id = s[-1]
|
| 307 |
+
else:
|
| 308 |
+
util.printD("There is no model id in this url")
|
| 309 |
+
return ""
|
| 310 |
+
|
| 311 |
+
return id
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
# get preview image by model path
|
| 315 |
+
# image will be saved to file, so no return
|
| 316 |
+
def get_preview_image_by_model_path(model_path:str, max_size_preview, skip_nsfw_preview):
|
| 317 |
+
if not model_path:
|
| 318 |
+
util.printD("model_path is empty")
|
| 319 |
+
return
|
| 320 |
+
|
| 321 |
+
if not os.path.isfile(model_path):
|
| 322 |
+
util.printD("model_path is not a file: "+model_path)
|
| 323 |
+
return
|
| 324 |
+
|
| 325 |
+
base, ext = os.path.splitext(model_path)
|
| 326 |
+
first_preview = base+".png"
|
| 327 |
+
sec_preview = base+".preview.png"
|
| 328 |
+
info_file = base + suffix + model.info_ext
|
| 329 |
+
|
| 330 |
+
# check preview image
|
| 331 |
+
if not os.path.isfile(sec_preview):
|
| 332 |
+
# need to download preview image
|
| 333 |
+
util.printD("Checking preview image for model: " + model_path)
|
| 334 |
+
# load model_info file
|
| 335 |
+
if os.path.isfile(info_file):
|
| 336 |
+
model_info = model.load_model_info(info_file)
|
| 337 |
+
if not model_info:
|
| 338 |
+
util.printD("Model Info is empty")
|
| 339 |
+
return
|
| 340 |
+
|
| 341 |
+
if "images" in model_info.keys():
|
| 342 |
+
if model_info["images"]:
|
| 343 |
+
for img_dict in model_info["images"]:
|
| 344 |
+
if "nsfw" in img_dict.keys():
|
| 345 |
+
if img_dict["nsfw"]:
|
| 346 |
+
util.printD("This image is NSFW")
|
| 347 |
+
if skip_nsfw_preview:
|
| 348 |
+
util.printD("Skip NSFW image")
|
| 349 |
+
continue
|
| 350 |
+
|
| 351 |
+
if "url" in img_dict.keys():
|
| 352 |
+
img_url = img_dict["url"]
|
| 353 |
+
if max_size_preview:
|
| 354 |
+
# use max width
|
| 355 |
+
if "width" in img_dict.keys():
|
| 356 |
+
if img_dict["width"]:
|
| 357 |
+
img_url = get_full_size_image_url(img_url, img_dict["width"])
|
| 358 |
+
|
| 359 |
+
util.download_file(img_url, sec_preview)
|
| 360 |
+
# we only need 1 preview image
|
| 361 |
+
break
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
# search local model by version id in 1 folder, no subfolder
|
| 366 |
+
# return - model_info
|
| 367 |
+
def search_local_model_info_by_version_id(folder:str, version_id:int) -> dict:
|
| 368 |
+
util.printD("Searching local model by version id")
|
| 369 |
+
util.printD("folder: " + folder)
|
| 370 |
+
util.printD("version_id: " + str(version_id))
|
| 371 |
+
|
| 372 |
+
if not folder:
|
| 373 |
+
util.printD("folder is none")
|
| 374 |
+
return
|
| 375 |
+
|
| 376 |
+
if not os.path.isdir(folder):
|
| 377 |
+
util.printD("folder is not a dir")
|
| 378 |
+
return
|
| 379 |
+
|
| 380 |
+
if not version_id:
|
| 381 |
+
util.printD("version_id is none")
|
| 382 |
+
return
|
| 383 |
+
|
| 384 |
+
# search civitai model info file
|
| 385 |
+
for filename in os.listdir(folder):
|
| 386 |
+
# check ext
|
| 387 |
+
base, ext = os.path.splitext(filename)
|
| 388 |
+
if ext == model.info_ext:
|
| 389 |
+
# find info file
|
| 390 |
+
if len(base) < 9:
|
| 391 |
+
# not a civitai info file
|
| 392 |
+
continue
|
| 393 |
+
|
| 394 |
+
if base[-8:] == suffix:
|
| 395 |
+
# find a civitai info file
|
| 396 |
+
path = os.path.join(folder, filename)
|
| 397 |
+
model_info = model.load_model_info(path)
|
| 398 |
+
if not model_info:
|
| 399 |
+
continue
|
| 400 |
+
|
| 401 |
+
if "id" not in model_info.keys():
|
| 402 |
+
continue
|
| 403 |
+
|
| 404 |
+
id = model_info["id"]
|
| 405 |
+
if not id:
|
| 406 |
+
continue
|
| 407 |
+
|
| 408 |
+
# util.printD(f"Compare version id, src: {id}, target:{version_id}")
|
| 409 |
+
if str(id) == str(version_id):
|
| 410 |
+
# find the one
|
| 411 |
+
return model_info
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
return
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
# check new version for a model by model path
|
| 421 |
+
# return (model_path, model_id, model_name, new_verion_id, new_version_name, description, download_url, img_url)
|
| 422 |
+
def check_model_new_version_by_path(model_path:str, delay:float=1) -> tuple:
|
| 423 |
+
if not model_path:
|
| 424 |
+
util.printD("model_path is empty")
|
| 425 |
+
return
|
| 426 |
+
|
| 427 |
+
if not os.path.isfile(model_path):
|
| 428 |
+
util.printD("model_path is not a file: "+model_path)
|
| 429 |
+
return
|
| 430 |
+
|
| 431 |
+
# get model info file name
|
| 432 |
+
base, ext = os.path.splitext(model_path)
|
| 433 |
+
info_file = base + suffix + model.info_ext
|
| 434 |
+
|
| 435 |
+
if not os.path.isfile(info_file):
|
| 436 |
+
return
|
| 437 |
+
|
| 438 |
+
# get model info
|
| 439 |
+
model_info_file = model.load_model_info(info_file)
|
| 440 |
+
if not model_info_file:
|
| 441 |
+
return
|
| 442 |
+
|
| 443 |
+
if "id" not in model_info_file.keys():
|
| 444 |
+
return
|
| 445 |
+
|
| 446 |
+
local_version_id = model_info_file["id"]
|
| 447 |
+
if not local_version_id:
|
| 448 |
+
return
|
| 449 |
+
|
| 450 |
+
if "modelId" not in model_info_file.keys():
|
| 451 |
+
return
|
| 452 |
+
|
| 453 |
+
model_id = model_info_file["modelId"]
|
| 454 |
+
if not model_id:
|
| 455 |
+
return
|
| 456 |
+
|
| 457 |
+
# get model info by id from civitai
|
| 458 |
+
model_info = get_model_info_by_id(model_id)
|
| 459 |
+
# delay before next request, to prevent to be treat as DDoS
|
| 460 |
+
util.printD(f"delay:{delay} second")
|
| 461 |
+
time.sleep(delay)
|
| 462 |
+
|
| 463 |
+
if not model_info:
|
| 464 |
+
return
|
| 465 |
+
|
| 466 |
+
if "modelVersions" not in model_info.keys():
|
| 467 |
+
return
|
| 468 |
+
|
| 469 |
+
modelVersions = model_info["modelVersions"]
|
| 470 |
+
if not modelVersions:
|
| 471 |
+
return
|
| 472 |
+
|
| 473 |
+
if not len(modelVersions):
|
| 474 |
+
return
|
| 475 |
+
|
| 476 |
+
current_version = modelVersions[0]
|
| 477 |
+
if not current_version:
|
| 478 |
+
return
|
| 479 |
+
|
| 480 |
+
if "id" not in current_version.keys():
|
| 481 |
+
return
|
| 482 |
+
|
| 483 |
+
current_version_id = current_version["id"]
|
| 484 |
+
if not current_version_id:
|
| 485 |
+
return
|
| 486 |
+
|
| 487 |
+
util.printD(f"Compare version id, local: {local_version_id}, remote: {current_version_id} ")
|
| 488 |
+
if current_version_id == local_version_id:
|
| 489 |
+
return
|
| 490 |
+
|
| 491 |
+
model_name = ""
|
| 492 |
+
if "name" in model_info.keys():
|
| 493 |
+
model_name = model_info["name"]
|
| 494 |
+
|
| 495 |
+
if not model_name:
|
| 496 |
+
model_name = ""
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
new_version_name = ""
|
| 500 |
+
if "name" in current_version.keys():
|
| 501 |
+
new_version_name = current_version["name"]
|
| 502 |
+
|
| 503 |
+
if not new_version_name:
|
| 504 |
+
new_version_name = ""
|
| 505 |
+
|
| 506 |
+
description = ""
|
| 507 |
+
if "description" in current_version.keys():
|
| 508 |
+
description = current_version["description"]
|
| 509 |
+
|
| 510 |
+
if not description:
|
| 511 |
+
description = ""
|
| 512 |
+
|
| 513 |
+
downloadUrl = ""
|
| 514 |
+
if "downloadUrl" in current_version.keys():
|
| 515 |
+
downloadUrl = current_version["downloadUrl"]
|
| 516 |
+
|
| 517 |
+
if not downloadUrl:
|
| 518 |
+
downloadUrl = ""
|
| 519 |
+
|
| 520 |
+
# get 1 preview image
|
| 521 |
+
img_url = ""
|
| 522 |
+
if "images" in current_version.keys():
|
| 523 |
+
if current_version["images"]:
|
| 524 |
+
if current_version["images"][0]:
|
| 525 |
+
if "url" in current_version["images"][0].keys():
|
| 526 |
+
img_url = current_version["images"][0]["url"]
|
| 527 |
+
if not img_url:
|
| 528 |
+
img_url = ""
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
|
| 532 |
+
return (model_path, model_id, model_name, current_version_id, new_version_name, description, downloadUrl, img_url)
|
| 533 |
+
|
| 534 |
+
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
# check model's new version
|
| 538 |
+
# parameter: delay - float, how many seconds to delay between each request to civitai
|
| 539 |
+
# 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)
|
| 540 |
+
def check_models_new_version_by_model_types(model_types:list, delay:float=1) -> list:
|
| 541 |
+
util.printD("Checking models' new version")
|
| 542 |
+
|
| 543 |
+
if not model_types:
|
| 544 |
+
return []
|
| 545 |
+
|
| 546 |
+
# check model types, which cloud be a string as 1 type
|
| 547 |
+
mts = []
|
| 548 |
+
if type(model_types) == str:
|
| 549 |
+
mts.append(model_types)
|
| 550 |
+
elif type(model_types) == list:
|
| 551 |
+
mts = model_types
|
| 552 |
+
else:
|
| 553 |
+
util.printD("Unknow model types:")
|
| 554 |
+
util.printD(model_types)
|
| 555 |
+
return []
|
| 556 |
+
|
| 557 |
+
# output is a markdown document string to show a list of new versions on UI
|
| 558 |
+
output = ""
|
| 559 |
+
# new version list
|
| 560 |
+
new_versions = []
|
| 561 |
+
|
| 562 |
+
# walk all models
|
| 563 |
+
for model_type, model_folder in model.folders.items():
|
| 564 |
+
if model_type not in mts:
|
| 565 |
+
continue
|
| 566 |
+
|
| 567 |
+
util.printD("Scanning path: " + model_folder)
|
| 568 |
+
for root, dirs, files in os.walk(model_folder, followlinks=True):
|
| 569 |
+
for filename in files:
|
| 570 |
+
# check ext
|
| 571 |
+
item = os.path.join(root, filename)
|
| 572 |
+
base, ext = os.path.splitext(item)
|
| 573 |
+
if ext in model.exts:
|
| 574 |
+
# find a model
|
| 575 |
+
r = check_model_new_version_by_path(item, delay)
|
| 576 |
+
|
| 577 |
+
if not r:
|
| 578 |
+
continue
|
| 579 |
+
|
| 580 |
+
model_path, model_id, model_name, current_version_id, new_version_name, description, downloadUrl, img_url = r
|
| 581 |
+
# check exist
|
| 582 |
+
if not current_version_id:
|
| 583 |
+
continue
|
| 584 |
+
|
| 585 |
+
# check this version id in list
|
| 586 |
+
is_already_in_list = False
|
| 587 |
+
for new_version in new_versions:
|
| 588 |
+
if current_version_id == new_version[3]:
|
| 589 |
+
# already in list
|
| 590 |
+
is_already_in_list = True
|
| 591 |
+
break
|
| 592 |
+
|
| 593 |
+
if is_already_in_list:
|
| 594 |
+
util.printD("New version is already in list")
|
| 595 |
+
continue
|
| 596 |
+
|
| 597 |
+
# search this new version id to check if this model is already downloaded
|
| 598 |
+
target_model_info = search_local_model_info_by_version_id(root, current_version_id)
|
| 599 |
+
if target_model_info:
|
| 600 |
+
util.printD("New version is already existed")
|
| 601 |
+
continue
|
| 602 |
+
|
| 603 |
+
# add to list
|
| 604 |
+
new_versions.append(r)
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
|
| 608 |
+
|
| 609 |
+
return new_versions
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
|
scripts/ch_lib/downloader.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
import sys
|
| 3 |
+
import requests
|
| 4 |
+
import os
|
| 5 |
+
from . import util
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
dl_ext = ".downloading"
|
| 9 |
+
|
| 10 |
+
# disable ssl warning info
|
| 11 |
+
requests.packages.urllib3.disable_warnings()
|
| 12 |
+
|
| 13 |
+
# output is downloaded file path
|
| 14 |
+
def dl(url, folder, filename, filepath):
|
| 15 |
+
util.printD("Start downloading from: " + url)
|
| 16 |
+
# get file_path
|
| 17 |
+
file_path = ""
|
| 18 |
+
if filepath:
|
| 19 |
+
file_path = filepath
|
| 20 |
+
else:
|
| 21 |
+
# if file_path is not in parameter, then folder must be in parameter
|
| 22 |
+
if not folder:
|
| 23 |
+
util.printD("folder is none")
|
| 24 |
+
return
|
| 25 |
+
|
| 26 |
+
if not os.path.isdir(folder):
|
| 27 |
+
util.printD("folder does not exist: "+folder)
|
| 28 |
+
return
|
| 29 |
+
|
| 30 |
+
if filename:
|
| 31 |
+
file_path = os.path.join(folder, filename)
|
| 32 |
+
|
| 33 |
+
# first request for header
|
| 34 |
+
rh = requests.get(url, stream=True, verify=False, headers=util.def_headers, proxies=util.proxies)
|
| 35 |
+
# get file size
|
| 36 |
+
total_size = 0
|
| 37 |
+
total_size = int(rh.headers['Content-Length'])
|
| 38 |
+
util.printD(f"File size: {total_size}")
|
| 39 |
+
|
| 40 |
+
# if file_path is empty, need to get file name from download url's header
|
| 41 |
+
if not file_path:
|
| 42 |
+
filename = ""
|
| 43 |
+
if "Content-Disposition" in rh.headers.keys():
|
| 44 |
+
cd = rh.headers["Content-Disposition"]
|
| 45 |
+
# Extract the filename from the header
|
| 46 |
+
# content of a CD: "attachment;filename=FileName.txt"
|
| 47 |
+
# in case "" is in CD filename's start and end, need to strip them out
|
| 48 |
+
filename = cd.split("=")[1].strip('"')
|
| 49 |
+
if not filename:
|
| 50 |
+
util.printD("Fail to get file name from Content-Disposition: " + cd)
|
| 51 |
+
return
|
| 52 |
+
|
| 53 |
+
if not filename:
|
| 54 |
+
util.printD("Can not get file name from download url's header")
|
| 55 |
+
return
|
| 56 |
+
|
| 57 |
+
# with folder and filename, now we have the full file path
|
| 58 |
+
file_path = os.path.join(folder, filename)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
util.printD("Target file path: " + file_path)
|
| 62 |
+
base, ext = os.path.splitext(file_path)
|
| 63 |
+
|
| 64 |
+
# check if file is already exist
|
| 65 |
+
count = 2
|
| 66 |
+
new_base = base
|
| 67 |
+
while os.path.isfile(file_path):
|
| 68 |
+
util.printD("Target file already exist.")
|
| 69 |
+
# re-name
|
| 70 |
+
new_base = base + "_" + str(count)
|
| 71 |
+
file_path = new_base + ext
|
| 72 |
+
count += 1
|
| 73 |
+
|
| 74 |
+
# use a temp file for downloading
|
| 75 |
+
dl_file_path = new_base+dl_ext
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
util.printD(f"Downloading to temp file: {dl_file_path}")
|
| 79 |
+
|
| 80 |
+
# check if downloading file is exsited
|
| 81 |
+
downloaded_size = 0
|
| 82 |
+
if os.path.exists(dl_file_path):
|
| 83 |
+
downloaded_size = os.path.getsize(dl_file_path)
|
| 84 |
+
|
| 85 |
+
util.printD(f"Downloaded size: {downloaded_size}")
|
| 86 |
+
|
| 87 |
+
# create header range
|
| 88 |
+
headers = {'Range': 'bytes=%d-' % downloaded_size}
|
| 89 |
+
headers['User-Agent'] = util.def_headers['User-Agent']
|
| 90 |
+
|
| 91 |
+
# download with header
|
| 92 |
+
r = requests.get(url, stream=True, verify=False, headers=headers, proxies=util.proxies)
|
| 93 |
+
|
| 94 |
+
# write to file
|
| 95 |
+
with open(dl_file_path, "ab") as f:
|
| 96 |
+
for chunk in r.iter_content(chunk_size=1024):
|
| 97 |
+
if chunk:
|
| 98 |
+
downloaded_size += len(chunk)
|
| 99 |
+
f.write(chunk)
|
| 100 |
+
# force to write to disk
|
| 101 |
+
f.flush()
|
| 102 |
+
|
| 103 |
+
# progress
|
| 104 |
+
progress = int(50 * downloaded_size / total_size)
|
| 105 |
+
sys.stdout.reconfigure(encoding='utf-8')
|
| 106 |
+
sys.stdout.write("\r[%s%s] %d%%" % ('-' * progress, ' ' * (50 - progress), 100 * downloaded_size / total_size))
|
| 107 |
+
sys.stdout.flush()
|
| 108 |
+
|
| 109 |
+
print()
|
| 110 |
+
|
| 111 |
+
# rename file
|
| 112 |
+
os.rename(dl_file_path, file_path)
|
| 113 |
+
util.printD(f"File Downloaded to: {file_path}")
|
| 114 |
+
return file_path
|
| 115 |
+
|
scripts/ch_lib/js_action_civitai.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
# handle msg between js and python side
|
| 3 |
+
import os
|
| 4 |
+
import json
|
| 5 |
+
import requests
|
| 6 |
+
import webbrowser
|
| 7 |
+
from . import util
|
| 8 |
+
from . import model
|
| 9 |
+
from . import civitai
|
| 10 |
+
from . import msg_handler
|
| 11 |
+
from . import downloader
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
# get civitai's model url and open it in browser
|
| 16 |
+
# parameter: model_type, search_term
|
| 17 |
+
# output: python msg - will be sent to hidden textbox then picked by js side
|
| 18 |
+
def open_model_url(msg, open_url_with_js):
|
| 19 |
+
util.printD("Start open_model_url")
|
| 20 |
+
|
| 21 |
+
output = ""
|
| 22 |
+
result = msg_handler.parse_js_msg(msg)
|
| 23 |
+
if not result:
|
| 24 |
+
util.printD("Parsing js ms failed")
|
| 25 |
+
return
|
| 26 |
+
|
| 27 |
+
model_type = result["model_type"]
|
| 28 |
+
search_term = result["search_term"]
|
| 29 |
+
|
| 30 |
+
model_info = civitai.load_model_info_by_search_term(model_type, search_term)
|
| 31 |
+
if not model_info:
|
| 32 |
+
util.printD(f"Failed to get model info for {model_type} {search_term}")
|
| 33 |
+
return ""
|
| 34 |
+
|
| 35 |
+
if "modelId" not in model_info.keys():
|
| 36 |
+
util.printD(f"Failed to get model id from info file for {model_type} {search_term}")
|
| 37 |
+
return ""
|
| 38 |
+
|
| 39 |
+
model_id = model_info["modelId"]
|
| 40 |
+
if not model_id:
|
| 41 |
+
util.printD(f"model id from info file of {model_type} {search_term} is None")
|
| 42 |
+
return ""
|
| 43 |
+
|
| 44 |
+
url = civitai.url_dict["modelPage"]+str(model_id)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
# msg content for js
|
| 48 |
+
content = {
|
| 49 |
+
"url":""
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
if not open_url_with_js:
|
| 53 |
+
util.printD("Open Url: " + url)
|
| 54 |
+
# open url
|
| 55 |
+
webbrowser.open_new_tab(url)
|
| 56 |
+
else:
|
| 57 |
+
util.printD("Send Url to js")
|
| 58 |
+
content["url"] = url
|
| 59 |
+
output = msg_handler.build_py_msg("open_url", content)
|
| 60 |
+
|
| 61 |
+
util.printD("End open_model_url")
|
| 62 |
+
return output
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
# add trigger words to prompt
|
| 67 |
+
# parameter: model_type, search_term, prompt
|
| 68 |
+
# return: [new_prompt, new_prompt] - new prompt with trigger words, return twice for txt2img and img2img
|
| 69 |
+
def add_trigger_words(msg):
|
| 70 |
+
util.printD("Start add_trigger_words")
|
| 71 |
+
|
| 72 |
+
result = msg_handler.parse_js_msg(msg)
|
| 73 |
+
if not result:
|
| 74 |
+
util.printD("Parsing js ms failed")
|
| 75 |
+
return
|
| 76 |
+
|
| 77 |
+
model_type = result["model_type"]
|
| 78 |
+
search_term = result["search_term"]
|
| 79 |
+
prompt = result["prompt"]
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
model_info = civitai.load_model_info_by_search_term(model_type, search_term)
|
| 83 |
+
if not model_info:
|
| 84 |
+
util.printD(f"Failed to get model info for {model_type} {search_term}")
|
| 85 |
+
return [prompt, prompt]
|
| 86 |
+
|
| 87 |
+
if "trainedWords" not in model_info.keys():
|
| 88 |
+
util.printD(f"Failed to get trainedWords from info file for {model_type} {search_term}")
|
| 89 |
+
return [prompt, prompt]
|
| 90 |
+
|
| 91 |
+
trainedWords = model_info["trainedWords"]
|
| 92 |
+
if not trainedWords:
|
| 93 |
+
util.printD(f"No trainedWords from info file for {model_type} {search_term}")
|
| 94 |
+
return [prompt, prompt]
|
| 95 |
+
|
| 96 |
+
if len(trainedWords) == 0:
|
| 97 |
+
util.printD(f"trainedWords from info file for {model_type} {search_term} is empty")
|
| 98 |
+
return [prompt, prompt]
|
| 99 |
+
|
| 100 |
+
# get ful trigger words
|
| 101 |
+
trigger_words = ""
|
| 102 |
+
for word in trainedWords:
|
| 103 |
+
trigger_words = trigger_words + word + ", "
|
| 104 |
+
|
| 105 |
+
new_prompt = prompt + " " + trigger_words
|
| 106 |
+
util.printD("trigger_words: " + trigger_words)
|
| 107 |
+
util.printD("prompt: " + prompt)
|
| 108 |
+
util.printD("new_prompt: " + new_prompt)
|
| 109 |
+
|
| 110 |
+
util.printD("End add_trigger_words")
|
| 111 |
+
|
| 112 |
+
# add to prompt
|
| 113 |
+
return [new_prompt, new_prompt]
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
# use preview image's prompt as prompt
|
| 118 |
+
# parameter: model_type, model_name, prompt, neg_prompt
|
| 119 |
+
# return: [new_prompt, new_neg_prompt, new_prompt, new_neg_prompt,] - return twice for txt2img and img2img
|
| 120 |
+
def use_preview_image_prompt(msg):
|
| 121 |
+
util.printD("Start use_preview_image_prompt")
|
| 122 |
+
|
| 123 |
+
result = msg_handler.parse_js_msg(msg)
|
| 124 |
+
if not result:
|
| 125 |
+
util.printD("Parsing js ms failed")
|
| 126 |
+
return
|
| 127 |
+
|
| 128 |
+
model_type = result["model_type"]
|
| 129 |
+
search_term = result["search_term"]
|
| 130 |
+
prompt = result["prompt"]
|
| 131 |
+
neg_prompt = result["neg_prompt"]
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
model_info = civitai.load_model_info_by_search_term(model_type, search_term)
|
| 135 |
+
if not model_info:
|
| 136 |
+
util.printD(f"Failed to get model info for {model_type} {search_term}")
|
| 137 |
+
return [prompt, neg_prompt, prompt, neg_prompt]
|
| 138 |
+
|
| 139 |
+
if "images" not in model_info.keys():
|
| 140 |
+
util.printD(f"Failed to get images from info file for {model_type} {search_term}")
|
| 141 |
+
return [prompt, neg_prompt, prompt, neg_prompt]
|
| 142 |
+
|
| 143 |
+
images = model_info["images"]
|
| 144 |
+
if not images:
|
| 145 |
+
util.printD(f"No images from info file for {model_type} {search_term}")
|
| 146 |
+
return [prompt, neg_prompt, prompt, neg_prompt]
|
| 147 |
+
|
| 148 |
+
if len(images) == 0:
|
| 149 |
+
util.printD(f"images from info file for {model_type} {search_term} is empty")
|
| 150 |
+
return [prompt, neg_prompt, prompt, neg_prompt]
|
| 151 |
+
|
| 152 |
+
# get prompt from preview images' meta data
|
| 153 |
+
preview_prompt = ""
|
| 154 |
+
preview_neg_prompt = ""
|
| 155 |
+
for img in images:
|
| 156 |
+
if "meta" in img.keys():
|
| 157 |
+
if img["meta"]:
|
| 158 |
+
if "prompt" in img["meta"].keys():
|
| 159 |
+
if img["meta"]["prompt"]:
|
| 160 |
+
preview_prompt = img["meta"]["prompt"]
|
| 161 |
+
|
| 162 |
+
if "negativePrompt" in img["meta"].keys():
|
| 163 |
+
if img["meta"]["negativePrompt"]:
|
| 164 |
+
preview_neg_prompt = img["meta"]["negativePrompt"]
|
| 165 |
+
|
| 166 |
+
# we only need 1 prompt
|
| 167 |
+
if preview_prompt:
|
| 168 |
+
break
|
| 169 |
+
|
| 170 |
+
if not preview_prompt:
|
| 171 |
+
util.printD(f"There is no prompt of {model_type} {search_term} in its preview image")
|
| 172 |
+
return [prompt, neg_prompt, prompt, neg_prompt]
|
| 173 |
+
|
| 174 |
+
util.printD("End use_preview_image_prompt")
|
| 175 |
+
|
| 176 |
+
return [preview_prompt, preview_neg_prompt, preview_prompt, preview_neg_prompt]
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
# download model's new verson by model path, version id and download url
|
| 180 |
+
# output is a md log
|
| 181 |
+
def dl_model_new_version(msg, max_size_preview, skip_nsfw_preview):
|
| 182 |
+
util.printD("Start dl_model_new_version")
|
| 183 |
+
|
| 184 |
+
output = ""
|
| 185 |
+
|
| 186 |
+
result = msg_handler.parse_js_msg(msg)
|
| 187 |
+
if not result:
|
| 188 |
+
output = "Parsing js ms failed"
|
| 189 |
+
util.printD(output)
|
| 190 |
+
return output
|
| 191 |
+
|
| 192 |
+
model_path = result["model_path"]
|
| 193 |
+
version_id = result["version_id"]
|
| 194 |
+
download_url = result["download_url"]
|
| 195 |
+
|
| 196 |
+
util.printD("model_path: " + model_path)
|
| 197 |
+
util.printD("version_id: " + str(version_id))
|
| 198 |
+
util.printD("download_url: " + download_url)
|
| 199 |
+
|
| 200 |
+
# check data
|
| 201 |
+
if not model_path:
|
| 202 |
+
output = "model_path is empty"
|
| 203 |
+
util.printD(output)
|
| 204 |
+
return output
|
| 205 |
+
|
| 206 |
+
if not version_id:
|
| 207 |
+
output = "version_id is empty"
|
| 208 |
+
util.printD(output)
|
| 209 |
+
return output
|
| 210 |
+
|
| 211 |
+
if not download_url:
|
| 212 |
+
output = "download_url is empty"
|
| 213 |
+
util.printD(output)
|
| 214 |
+
return output
|
| 215 |
+
|
| 216 |
+
if not os.path.isfile(model_path):
|
| 217 |
+
output = "model_path is not a file: "+ model_path
|
| 218 |
+
util.printD(output)
|
| 219 |
+
return output
|
| 220 |
+
|
| 221 |
+
# get model folder from model path
|
| 222 |
+
model_folder = os.path.dirname(model_path)
|
| 223 |
+
|
| 224 |
+
# no need to check when downloading new version, since checking new version is already checked
|
| 225 |
+
# check if this model is already existed
|
| 226 |
+
# r = civitai.search_local_model_info_by_version_id(model_folder, version_id)
|
| 227 |
+
# if r:
|
| 228 |
+
# output = "This model version is already existed"
|
| 229 |
+
# util.printD(output)
|
| 230 |
+
# return output
|
| 231 |
+
|
| 232 |
+
# download file
|
| 233 |
+
new_model_path = downloader.dl(download_url, model_folder, None, None)
|
| 234 |
+
if not new_model_path:
|
| 235 |
+
output = "Download failed, check console log for detail. Download url: " + download_url
|
| 236 |
+
util.printD(output)
|
| 237 |
+
return output
|
| 238 |
+
|
| 239 |
+
# get version info
|
| 240 |
+
version_info = civitai.get_version_info_by_version_id(version_id)
|
| 241 |
+
if not version_info:
|
| 242 |
+
output = "Model downloaded, but failed to get version info, check console log for detail. Model saved to: " + new_model_path
|
| 243 |
+
util.printD(output)
|
| 244 |
+
return output
|
| 245 |
+
|
| 246 |
+
# now write version info to file
|
| 247 |
+
base, ext = os.path.splitext(new_model_path)
|
| 248 |
+
info_file = base + civitai.suffix + model.info_ext
|
| 249 |
+
model.write_model_info(info_file, version_info)
|
| 250 |
+
|
| 251 |
+
# then, get preview image
|
| 252 |
+
civitai.get_preview_image_by_model_path(new_model_path, max_size_preview, skip_nsfw_preview)
|
| 253 |
+
|
| 254 |
+
output = "Done. Model downloaded to: " + new_model_path
|
| 255 |
+
util.printD(output)
|
| 256 |
+
return output
|
scripts/ch_lib/model.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
# handle msg between js and python side
|
| 3 |
+
import os
|
| 4 |
+
import json
|
| 5 |
+
from . import util
|
| 6 |
+
from modules import shared
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# this is the default root path
|
| 10 |
+
root_path = os.getcwd()
|
| 11 |
+
|
| 12 |
+
# if command line arguement is used to change model folder,
|
| 13 |
+
# then model folder is in absolute path, not based on this root path anymore.
|
| 14 |
+
# so to make extension work with those absolute model folder paths, model folder also need to be in absolute path
|
| 15 |
+
folders = {
|
| 16 |
+
"ti": os.path.join(root_path, "embeddings"),
|
| 17 |
+
"hyper": os.path.join(root_path, "models", "hypernetworks"),
|
| 18 |
+
"ckp": os.path.join(root_path, "models", "Stable-diffusion"),
|
| 19 |
+
"lora": os.path.join(root_path, "models", "Lora"),
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
exts = (".bin", ".pt", ".safetensors", ".ckpt")
|
| 23 |
+
info_ext = ".info"
|
| 24 |
+
vae_suffix = ".vae"
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
# get cusomter model path
|
| 28 |
+
def get_custom_model_folder():
|
| 29 |
+
util.printD("Get Custom Model Folder")
|
| 30 |
+
|
| 31 |
+
global folders
|
| 32 |
+
|
| 33 |
+
if shared.cmd_opts.embeddings_dir and os.path.isdir(shared.cmd_opts.embeddings_dir):
|
| 34 |
+
folders["ti"] = shared.cmd_opts.embeddings_dir
|
| 35 |
+
|
| 36 |
+
if shared.cmd_opts.hypernetwork_dir and os.path.isdir(shared.cmd_opts.hypernetwork_dir):
|
| 37 |
+
folders["hyper"] = shared.cmd_opts.hypernetwork_dir
|
| 38 |
+
|
| 39 |
+
if shared.cmd_opts.ckpt_dir and os.path.isdir(shared.cmd_opts.ckpt_dir):
|
| 40 |
+
folders["ckp"] = shared.cmd_opts.ckpt_dir
|
| 41 |
+
|
| 42 |
+
if shared.cmd_opts.lora_dir and os.path.isdir(shared.cmd_opts.lora_dir):
|
| 43 |
+
folders["lora"] = shared.cmd_opts.lora_dir
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# write model info to file
|
| 50 |
+
def write_model_info(path, model_info):
|
| 51 |
+
util.printD("Write model info to file: " + path)
|
| 52 |
+
with open(os.path.realpath(path), 'w') as f:
|
| 53 |
+
f.write(json.dumps(model_info, indent=4))
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def load_model_info(path):
|
| 57 |
+
# util.printD("Load model info from file: " + path)
|
| 58 |
+
model_info = None
|
| 59 |
+
with open(os.path.realpath(path), 'r') as f:
|
| 60 |
+
try:
|
| 61 |
+
model_info = json.load(f)
|
| 62 |
+
except Exception as e:
|
| 63 |
+
util.printD("Selected file is not json: " + path)
|
| 64 |
+
util.printD(e)
|
| 65 |
+
return
|
| 66 |
+
|
| 67 |
+
return model_info
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
# get model file names by model type
|
| 71 |
+
# parameter: model_type - string
|
| 72 |
+
# return: model name list
|
| 73 |
+
def get_model_names_by_type(model_type:str) -> list:
|
| 74 |
+
|
| 75 |
+
model_folder = folders[model_type]
|
| 76 |
+
|
| 77 |
+
# get information from filter
|
| 78 |
+
# only get those model names don't have a civitai model info file
|
| 79 |
+
model_names = []
|
| 80 |
+
for root, dirs, files in os.walk(model_folder, followlinks=True):
|
| 81 |
+
for filename in files:
|
| 82 |
+
item = os.path.join(root, filename)
|
| 83 |
+
# check extension
|
| 84 |
+
base, ext = os.path.splitext(item)
|
| 85 |
+
if ext in exts:
|
| 86 |
+
# find a model
|
| 87 |
+
model_names.append(filename)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
return model_names
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
# return 2 values: (model_root, model_path)
|
| 94 |
+
def get_model_path_by_type_and_name(model_type:str, model_name:str) -> str:
|
| 95 |
+
util.printD("Run get_model_path_by_type_and_name")
|
| 96 |
+
if model_type not in folders.keys():
|
| 97 |
+
util.printD("unknown model_type: " + model_type)
|
| 98 |
+
return
|
| 99 |
+
|
| 100 |
+
if not model_name:
|
| 101 |
+
util.printD("model name can not be empty")
|
| 102 |
+
return
|
| 103 |
+
|
| 104 |
+
folder = folders[model_type]
|
| 105 |
+
|
| 106 |
+
# model could be in subfolder, need to walk.
|
| 107 |
+
model_root = ""
|
| 108 |
+
model_path = ""
|
| 109 |
+
for root, dirs, files in os.walk(folder, followlinks=True):
|
| 110 |
+
for filename in files:
|
| 111 |
+
if filename == model_name:
|
| 112 |
+
# find model
|
| 113 |
+
model_root = root
|
| 114 |
+
model_path = os.path.join(root, filename)
|
| 115 |
+
return (model_root, model_path)
|
| 116 |
+
|
| 117 |
+
return
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
|
scripts/ch_lib/model_action_civitai.py
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
# handle msg between js and python side
|
| 3 |
+
import os
|
| 4 |
+
import time
|
| 5 |
+
from . import util
|
| 6 |
+
from . import model
|
| 7 |
+
from . import civitai
|
| 8 |
+
from . import downloader
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# scan model to generate SHA256, then use this SHA256 to get model info from civitai
|
| 12 |
+
# return output msg
|
| 13 |
+
def scan_model(scan_model_types, max_size_preview, skip_nsfw_preview):
|
| 14 |
+
util.printD("Start scan_model")
|
| 15 |
+
output = ""
|
| 16 |
+
|
| 17 |
+
# check model types
|
| 18 |
+
if not scan_model_types:
|
| 19 |
+
output = "Model Types is None, can not scan."
|
| 20 |
+
util.printD(output)
|
| 21 |
+
return output
|
| 22 |
+
|
| 23 |
+
model_types = []
|
| 24 |
+
# check type if it is a string
|
| 25 |
+
if type(scan_model_types) == str:
|
| 26 |
+
model_types.append(scan_model_types)
|
| 27 |
+
else:
|
| 28 |
+
model_types = scan_model_types
|
| 29 |
+
|
| 30 |
+
model_count = 0
|
| 31 |
+
image_count = 0
|
| 32 |
+
# scan_log = ""
|
| 33 |
+
for model_type, model_folder in model.folders.items():
|
| 34 |
+
if model_type not in model_types:
|
| 35 |
+
continue
|
| 36 |
+
|
| 37 |
+
util.printD("Scanning path: " + model_folder)
|
| 38 |
+
for root, dirs, files in os.walk(model_folder, followlinks=True):
|
| 39 |
+
for filename in files:
|
| 40 |
+
# check ext
|
| 41 |
+
item = os.path.join(root, filename)
|
| 42 |
+
base, ext = os.path.splitext(item)
|
| 43 |
+
if ext in model.exts:
|
| 44 |
+
# ignore vae file
|
| 45 |
+
if len(base) > 4:
|
| 46 |
+
if base[-4:] == model.vae_suffix:
|
| 47 |
+
# find .vae
|
| 48 |
+
util.printD("This is a vae file: " + filename)
|
| 49 |
+
continue
|
| 50 |
+
|
| 51 |
+
# find a model
|
| 52 |
+
# get info file
|
| 53 |
+
info_file = base + civitai.suffix + model.info_ext
|
| 54 |
+
# check info file
|
| 55 |
+
if not os.path.isfile(info_file):
|
| 56 |
+
util.printD("Creating model info for: " + filename)
|
| 57 |
+
# get model's sha256
|
| 58 |
+
hash = util.gen_file_sha256(item)
|
| 59 |
+
|
| 60 |
+
if not hash:
|
| 61 |
+
output = "failed generating SHA256 for model:" + filename
|
| 62 |
+
util.printD(output)
|
| 63 |
+
return output
|
| 64 |
+
|
| 65 |
+
# use this sha256 to get model info from civitai
|
| 66 |
+
model_info = civitai.get_model_info_by_hash(hash)
|
| 67 |
+
# delay 1 second for ti
|
| 68 |
+
if model_type == "ti":
|
| 69 |
+
util.printD("Delay 1 second for TI")
|
| 70 |
+
time.sleep(1)
|
| 71 |
+
|
| 72 |
+
if model_info is None:
|
| 73 |
+
output = "Connect to Civitai API service failed. Wait a while and try again"
|
| 74 |
+
util.printD(output)
|
| 75 |
+
return output+", check console log for detail"
|
| 76 |
+
|
| 77 |
+
# write model info to file
|
| 78 |
+
model.write_model_info(info_file, model_info)
|
| 79 |
+
|
| 80 |
+
# set model_count
|
| 81 |
+
model_count = model_count+1
|
| 82 |
+
|
| 83 |
+
# check preview image
|
| 84 |
+
civitai.get_preview_image_by_model_path(item, max_size_preview, skip_nsfw_preview)
|
| 85 |
+
image_count = image_count+1
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
# scan_log = "Done"
|
| 89 |
+
|
| 90 |
+
output = f"Done. Scanned {model_count} models, checked {image_count} images"
|
| 91 |
+
|
| 92 |
+
util.printD(output)
|
| 93 |
+
|
| 94 |
+
return output
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
# Get model info by model type, name and url
|
| 99 |
+
# output is log info to display on markdown component
|
| 100 |
+
def get_model_info_by_input(model_type, model_name, model_url_or_id, max_size_preview, skip_nsfw_preview):
|
| 101 |
+
output = ""
|
| 102 |
+
# parse model id
|
| 103 |
+
model_id = civitai.get_model_id_from_url(model_url_or_id)
|
| 104 |
+
if not model_id:
|
| 105 |
+
output = "failed to parse model id from url: " + model_url_or_id
|
| 106 |
+
util.printD(output)
|
| 107 |
+
return output
|
| 108 |
+
|
| 109 |
+
# get model file path
|
| 110 |
+
# model could be in subfolder
|
| 111 |
+
result = model.get_model_path_by_type_and_name(model_type, model_name)
|
| 112 |
+
if not result:
|
| 113 |
+
output = "failed to get model file path"
|
| 114 |
+
util.printD(output)
|
| 115 |
+
return output
|
| 116 |
+
|
| 117 |
+
model_root, model_path = result
|
| 118 |
+
if not model_path:
|
| 119 |
+
output = "model path is empty"
|
| 120 |
+
util.printD(output)
|
| 121 |
+
return output
|
| 122 |
+
|
| 123 |
+
# get info file path
|
| 124 |
+
base, ext = os.path.splitext(model_path)
|
| 125 |
+
info_file = base + civitai.suffix + model.info_ext
|
| 126 |
+
|
| 127 |
+
# get model info
|
| 128 |
+
#we call it model_info, but in civitai, it is actually version info
|
| 129 |
+
model_info = civitai.get_version_info_by_model_id(model_id)
|
| 130 |
+
|
| 131 |
+
if not model_info:
|
| 132 |
+
output = "failed to get model info from url: " + model_url_or_id
|
| 133 |
+
util.printD(output)
|
| 134 |
+
return output
|
| 135 |
+
|
| 136 |
+
# write model info to file
|
| 137 |
+
model.write_model_info(info_file, model_info)
|
| 138 |
+
|
| 139 |
+
util.printD("Saved model info to: "+ info_file)
|
| 140 |
+
|
| 141 |
+
# check preview image
|
| 142 |
+
civitai.get_preview_image_by_model_path(model_path, max_size_preview, skip_nsfw_preview)
|
| 143 |
+
|
| 144 |
+
output = "Model Info saved to: " + info_file
|
| 145 |
+
return output
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
# check models' new version and output to UI as markdown doc
|
| 150 |
+
def check_models_new_version_to_md(model_types:list) -> str:
|
| 151 |
+
new_versions = civitai.check_models_new_version_by_model_types(model_types, 1)
|
| 152 |
+
|
| 153 |
+
count = 0
|
| 154 |
+
output = ""
|
| 155 |
+
if not new_versions:
|
| 156 |
+
output = "No model has new version"
|
| 157 |
+
else:
|
| 158 |
+
output = "Found new version for following models: <br>"
|
| 159 |
+
for new_version in new_versions:
|
| 160 |
+
count = count+1
|
| 161 |
+
model_path, model_id, model_name, new_verion_id, new_version_name, description, download_url, img_url = new_version
|
| 162 |
+
# in md, each part is something like this:
|
| 163 |
+
# [model_name](model_url)
|
| 164 |
+
# [version_name](download_url)
|
| 165 |
+
# version description
|
| 166 |
+
url = civitai.url_dict["modelPage"]+str(model_id)
|
| 167 |
+
|
| 168 |
+
part = f'<div style="font-size:20px;margin:6px 0px;"><b>Model: <a href="{url}" target="_blank"><u>{model_name}</u></a></b></div>'
|
| 169 |
+
part = part + f'<div style="font-size:16px">File: {model_path}</div>'
|
| 170 |
+
if download_url:
|
| 171 |
+
# replace "\" to "/" in model_path for windows
|
| 172 |
+
model_path = model_path.replace('\\', '\\\\')
|
| 173 |
+
part = part + f'<div style="font-size:16px;margin:6px 0px;">New Version: <u><a href="{download_url}" target="_blank" style="margin:0px 10px;">{new_version_name}</a></u>'
|
| 174 |
+
# add js function to download new version into SD webui by python
|
| 175 |
+
part = part + " "
|
| 176 |
+
# in embed HTML, onclick= will also follow a ", never a ', so have to write it as following
|
| 177 |
+
part = part + f"<u><a href='#' style='margin:0px 10px;' onclick=\"ch_dl_model_new_version(event, '{model_path}', '{new_verion_id}', '{download_url}')\">[Download into SD]</a></u>"
|
| 178 |
+
|
| 179 |
+
else:
|
| 180 |
+
part = part + f'<div style="font-size:16px;margin:6px 0px;">New Version: {new_version_name}'
|
| 181 |
+
part = part + '</div>'
|
| 182 |
+
|
| 183 |
+
# description
|
| 184 |
+
if description:
|
| 185 |
+
part = part + '<blockquote style="font-size:16px;margin:6px 0px;">'+ description + '</blockquote><br>'
|
| 186 |
+
|
| 187 |
+
# preview image
|
| 188 |
+
if img_url:
|
| 189 |
+
part = part + f"<img src='{img_url}'><br>"
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
output = output + part
|
| 193 |
+
|
| 194 |
+
util.printD(f"Done. Find {count} models have new version. Check UI for detail.")
|
| 195 |
+
|
| 196 |
+
return output
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
# get model info by url
|
| 200 |
+
def get_model_info_by_url(model_url_or_id:str) -> tuple:
|
| 201 |
+
util.printD("Getting model info by: " + model_url_or_id)
|
| 202 |
+
|
| 203 |
+
# parse model id
|
| 204 |
+
model_id = civitai.get_model_id_from_url(model_url_or_id)
|
| 205 |
+
if not model_id:
|
| 206 |
+
util.printD("failed to parse model id from url or id")
|
| 207 |
+
return
|
| 208 |
+
|
| 209 |
+
model_info = civitai.get_model_info_by_id(model_id)
|
| 210 |
+
if model_info is None:
|
| 211 |
+
util.printD("Connect to Civitai API service failed. Wait a while and try again")
|
| 212 |
+
return
|
| 213 |
+
|
| 214 |
+
if not model_info:
|
| 215 |
+
util.printD("failed to get model info from url or id")
|
| 216 |
+
return
|
| 217 |
+
|
| 218 |
+
# parse model type, model name, subfolder, version from this model info
|
| 219 |
+
# get model type
|
| 220 |
+
if "type" not in model_info.keys():
|
| 221 |
+
util.printD("model type is not in model_info")
|
| 222 |
+
return
|
| 223 |
+
|
| 224 |
+
civitai_model_type = model_info["type"]
|
| 225 |
+
if civitai_model_type not in civitai.model_type_dict.keys():
|
| 226 |
+
util.printD("This model type is not supported:"+civitai_model_type)
|
| 227 |
+
return
|
| 228 |
+
|
| 229 |
+
model_type = civitai.model_type_dict[civitai_model_type]
|
| 230 |
+
|
| 231 |
+
# get model type
|
| 232 |
+
if "name" not in model_info.keys():
|
| 233 |
+
util.printD("model name is not in model_info")
|
| 234 |
+
return
|
| 235 |
+
|
| 236 |
+
model_name = model_info["name"]
|
| 237 |
+
if not model_name:
|
| 238 |
+
util.printD("model name is Empty")
|
| 239 |
+
model_name = ""
|
| 240 |
+
|
| 241 |
+
# get version list
|
| 242 |
+
if "modelVersions" not in model_info.keys():
|
| 243 |
+
util.printD("modelVersions is not in model_info")
|
| 244 |
+
return
|
| 245 |
+
|
| 246 |
+
modelVersions = model_info["modelVersions"]
|
| 247 |
+
if not modelVersions:
|
| 248 |
+
util.printD("modelVersions is Empty")
|
| 249 |
+
return
|
| 250 |
+
|
| 251 |
+
version_strs = []
|
| 252 |
+
for version in modelVersions:
|
| 253 |
+
# version name can not be used as id
|
| 254 |
+
# version id is not readable
|
| 255 |
+
# so , we use name_id as version string
|
| 256 |
+
version_str = version["name"]+"_"+str(version["id"])
|
| 257 |
+
version_strs.append(version_str)
|
| 258 |
+
|
| 259 |
+
# get folder by model type
|
| 260 |
+
folder = model.folders[model_type]
|
| 261 |
+
# get subfolders
|
| 262 |
+
subfolders = util.get_subfolders(folder)
|
| 263 |
+
if not subfolders:
|
| 264 |
+
subfolders = []
|
| 265 |
+
|
| 266 |
+
# add default root folder
|
| 267 |
+
subfolders.append("/")
|
| 268 |
+
|
| 269 |
+
util.printD("Get following info for downloading:")
|
| 270 |
+
util.printD(f"model_name:{model_name}")
|
| 271 |
+
util.printD(f"model_type:{model_type}")
|
| 272 |
+
util.printD(f"subfolders:{subfolders}")
|
| 273 |
+
util.printD(f"version_strs:{version_strs}")
|
| 274 |
+
|
| 275 |
+
return (model_info, model_name, model_type, subfolders, version_strs)
|
| 276 |
+
|
| 277 |
+
# get version info by version string
|
| 278 |
+
def get_ver_info_by_ver_str(version_str:str, model_info:dict) -> dict:
|
| 279 |
+
if not version_str:
|
| 280 |
+
util.printD("version_str is empty")
|
| 281 |
+
return
|
| 282 |
+
|
| 283 |
+
if not model_info:
|
| 284 |
+
util.printD("model_info is None")
|
| 285 |
+
return
|
| 286 |
+
|
| 287 |
+
# get version list
|
| 288 |
+
if "modelVersions" not in model_info.keys():
|
| 289 |
+
util.printD("modelVersions is not in model_info")
|
| 290 |
+
return
|
| 291 |
+
|
| 292 |
+
modelVersions = model_info["modelVersions"]
|
| 293 |
+
if not modelVersions:
|
| 294 |
+
util.printD("modelVersions is Empty")
|
| 295 |
+
return
|
| 296 |
+
|
| 297 |
+
# find version by version_str
|
| 298 |
+
version = None
|
| 299 |
+
for ver in modelVersions:
|
| 300 |
+
# version name can not be used as id
|
| 301 |
+
# version id is not readable
|
| 302 |
+
# so , we use name_id as version string
|
| 303 |
+
ver_str = ver["name"]+"_"+str(ver["id"])
|
| 304 |
+
if ver_str == version_str:
|
| 305 |
+
# find version
|
| 306 |
+
version = ver
|
| 307 |
+
|
| 308 |
+
if not version:
|
| 309 |
+
util.printD("can not find version by version string: " + version_str)
|
| 310 |
+
return
|
| 311 |
+
|
| 312 |
+
# get version id
|
| 313 |
+
if "id" not in version.keys():
|
| 314 |
+
util.printD("this version has no id")
|
| 315 |
+
return
|
| 316 |
+
|
| 317 |
+
return version
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
# get download url from model info by version string
|
| 321 |
+
# return - (version_id, download_url)
|
| 322 |
+
def get_id_and_dl_url_by_version_str(version_str:str, model_info:dict) -> tuple:
|
| 323 |
+
if not version_str:
|
| 324 |
+
util.printD("version_str is empty")
|
| 325 |
+
return
|
| 326 |
+
|
| 327 |
+
if not model_info:
|
| 328 |
+
util.printD("model_info is None")
|
| 329 |
+
return
|
| 330 |
+
|
| 331 |
+
# get version list
|
| 332 |
+
if "modelVersions" not in model_info.keys():
|
| 333 |
+
util.printD("modelVersions is not in model_info")
|
| 334 |
+
return
|
| 335 |
+
|
| 336 |
+
modelVersions = model_info["modelVersions"]
|
| 337 |
+
if not modelVersions:
|
| 338 |
+
util.printD("modelVersions is Empty")
|
| 339 |
+
return
|
| 340 |
+
|
| 341 |
+
# find version by version_str
|
| 342 |
+
version = None
|
| 343 |
+
for ver in modelVersions:
|
| 344 |
+
# version name can not be used as id
|
| 345 |
+
# version id is not readable
|
| 346 |
+
# so , we use name_id as version string
|
| 347 |
+
ver_str = ver["name"]+"_"+str(ver["id"])
|
| 348 |
+
if ver_str == version_str:
|
| 349 |
+
# find version
|
| 350 |
+
version = ver
|
| 351 |
+
|
| 352 |
+
if not version:
|
| 353 |
+
util.printD("can not find version by version string: " + version_str)
|
| 354 |
+
return
|
| 355 |
+
|
| 356 |
+
# get version id
|
| 357 |
+
if "id" not in version.keys():
|
| 358 |
+
util.printD("this version has no id")
|
| 359 |
+
return
|
| 360 |
+
|
| 361 |
+
version_id = version["id"]
|
| 362 |
+
if not version_id:
|
| 363 |
+
util.printD("version id is Empty")
|
| 364 |
+
return
|
| 365 |
+
|
| 366 |
+
# get download url
|
| 367 |
+
if "downloadUrl" not in version.keys():
|
| 368 |
+
util.printD("downloadUrl is not in this version")
|
| 369 |
+
return
|
| 370 |
+
|
| 371 |
+
downloadUrl = version["downloadUrl"]
|
| 372 |
+
if not downloadUrl:
|
| 373 |
+
util.printD("downloadUrl is Empty")
|
| 374 |
+
return
|
| 375 |
+
|
| 376 |
+
util.printD("Get Download Url: " + downloadUrl)
|
| 377 |
+
|
| 378 |
+
return (version_id, downloadUrl)
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
# download model from civitai by input
|
| 382 |
+
# output to markdown log
|
| 383 |
+
def dl_model_by_input(model_info:dict, model_type:str, subfolder_str:str, version_str:str, dl_all_bool:bool, max_size_preview:bool, skip_nsfw_preview:bool) -> str:
|
| 384 |
+
|
| 385 |
+
output = ""
|
| 386 |
+
|
| 387 |
+
if not model_info:
|
| 388 |
+
output = "model_info is None"
|
| 389 |
+
util.printD(output)
|
| 390 |
+
return output
|
| 391 |
+
|
| 392 |
+
if not model_type:
|
| 393 |
+
output = "model_type is None"
|
| 394 |
+
util.printD(output)
|
| 395 |
+
return output
|
| 396 |
+
|
| 397 |
+
if not subfolder_str:
|
| 398 |
+
output = "subfolder string is None"
|
| 399 |
+
util.printD(output)
|
| 400 |
+
return output
|
| 401 |
+
|
| 402 |
+
if not version_str:
|
| 403 |
+
output = "version_str is None"
|
| 404 |
+
util.printD(output)
|
| 405 |
+
return output
|
| 406 |
+
|
| 407 |
+
# get model root folder
|
| 408 |
+
if model_type not in model.folders.keys():
|
| 409 |
+
output = "Unknow model type: "+model_type
|
| 410 |
+
util.printD(output)
|
| 411 |
+
return output
|
| 412 |
+
|
| 413 |
+
model_root_folder = model.folders[model_type]
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
# get subfolder
|
| 417 |
+
subfolder = ""
|
| 418 |
+
if subfolder_str == "/" or subfolder_str == "\\":
|
| 419 |
+
subfolder = ""
|
| 420 |
+
elif subfolder_str[:1] == "/" or subfolder_str[:1] == "\\":
|
| 421 |
+
subfolder = subfolder_str[1:]
|
| 422 |
+
else:
|
| 423 |
+
subfolder = subfolder_str
|
| 424 |
+
|
| 425 |
+
# get model folder for downloading
|
| 426 |
+
model_folder = os.path.join(model_root_folder, subfolder)
|
| 427 |
+
if not os.path.isdir(model_folder):
|
| 428 |
+
output = "Model folder is not a dir: "+ model_folder
|
| 429 |
+
util.printD(output)
|
| 430 |
+
return output
|
| 431 |
+
|
| 432 |
+
# get version info
|
| 433 |
+
ver_info = get_ver_info_by_ver_str(version_str, model_info)
|
| 434 |
+
if not ver_info:
|
| 435 |
+
output = "Fail to get version info, check console log for detail"
|
| 436 |
+
util.printD(output)
|
| 437 |
+
return output
|
| 438 |
+
|
| 439 |
+
version_id = ver_info["id"]
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
if dl_all_bool:
|
| 443 |
+
# get all download url from files info
|
| 444 |
+
# some model versions have multiple files
|
| 445 |
+
download_urls = []
|
| 446 |
+
if "files" in ver_info.keys():
|
| 447 |
+
for file_info in ver_info["files"]:
|
| 448 |
+
if "downloadUrl" in file_info.keys():
|
| 449 |
+
download_urls.append(file_info["downloadUrl"])
|
| 450 |
+
|
| 451 |
+
if not len(download_urls):
|
| 452 |
+
if "downloadUrl" in ver_info.keys():
|
| 453 |
+
download_urls.append(ver_info["downloadUrl"])
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
# check if this model is already existed
|
| 457 |
+
r = civitai.search_local_model_info_by_version_id(model_folder, version_id)
|
| 458 |
+
if r:
|
| 459 |
+
output = "This model version is already existed"
|
| 460 |
+
util.printD(output)
|
| 461 |
+
return output
|
| 462 |
+
|
| 463 |
+
# download
|
| 464 |
+
filepath = ""
|
| 465 |
+
for url in download_urls:
|
| 466 |
+
model_filepath = downloader.dl(url, model_folder, None, None)
|
| 467 |
+
if not model_filepath:
|
| 468 |
+
output = "Downloading failed, check console log for detail"
|
| 469 |
+
util.printD(output)
|
| 470 |
+
return output
|
| 471 |
+
|
| 472 |
+
if url == ver_info["downloadUrl"]:
|
| 473 |
+
filepath = model_filepath
|
| 474 |
+
else:
|
| 475 |
+
# only download one file
|
| 476 |
+
# get download url
|
| 477 |
+
url = ver_info["downloadUrl"]
|
| 478 |
+
if not url:
|
| 479 |
+
output = "Fail to get download url, check console log for detail"
|
| 480 |
+
util.printD(output)
|
| 481 |
+
return output
|
| 482 |
+
|
| 483 |
+
# download
|
| 484 |
+
filepath = downloader.dl(url, model_folder, None, None)
|
| 485 |
+
if not filepath:
|
| 486 |
+
output = "Downloading failed, check console log for detail"
|
| 487 |
+
util.printD(output)
|
| 488 |
+
return output
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
if not filepath:
|
| 492 |
+
filepath = model_filepath
|
| 493 |
+
|
| 494 |
+
# get version info
|
| 495 |
+
version_info = civitai.get_version_info_by_version_id(version_id)
|
| 496 |
+
if not version_info:
|
| 497 |
+
output = "Model downloaded, but failed to get version info, check console log for detail. Model saved to: " + filepath
|
| 498 |
+
util.printD(output)
|
| 499 |
+
return output
|
| 500 |
+
|
| 501 |
+
# write version info to file
|
| 502 |
+
base, ext = os.path.splitext(filepath)
|
| 503 |
+
info_file = base + civitai.suffix + model.info_ext
|
| 504 |
+
model.write_model_info(info_file, version_info)
|
| 505 |
+
|
| 506 |
+
# then, get preview image
|
| 507 |
+
civitai.get_preview_image_by_model_path(filepath, max_size_preview, skip_nsfw_preview)
|
| 508 |
+
|
| 509 |
+
output = "Done. Model downloaded to: " + filepath
|
| 510 |
+
util.printD(output)
|
| 511 |
+
return output
|
scripts/ch_lib/msg_handler.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
# handle msg between js and python side
|
| 3 |
+
import json
|
| 4 |
+
from . import util
|
| 5 |
+
|
| 6 |
+
# action list
|
| 7 |
+
js_actions = ("open_url", "add_trigger_words", "use_preview_prompt", "dl_model_new_version")
|
| 8 |
+
py_actions = ("open_url")
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# handle request from javascript
|
| 12 |
+
# parameter: msg - msg from js as string in a hidden textbox
|
| 13 |
+
# return: dict for result
|
| 14 |
+
def parse_js_msg(msg):
|
| 15 |
+
util.printD("Start parse js msg")
|
| 16 |
+
msg_dict = json.loads(msg)
|
| 17 |
+
|
| 18 |
+
# in case client side run JSON.stringify twice
|
| 19 |
+
if (type(msg_dict) == str):
|
| 20 |
+
msg_dict = json.loads(msg_dict)
|
| 21 |
+
|
| 22 |
+
if "action" not in msg_dict.keys():
|
| 23 |
+
util.printD("Can not find action from js request")
|
| 24 |
+
return
|
| 25 |
+
|
| 26 |
+
action = msg_dict["action"]
|
| 27 |
+
if not action:
|
| 28 |
+
util.printD("Action from js request is None")
|
| 29 |
+
return
|
| 30 |
+
|
| 31 |
+
if action not in js_actions:
|
| 32 |
+
util.printD("Unknow action: " + action)
|
| 33 |
+
return
|
| 34 |
+
|
| 35 |
+
util.printD("End parse js msg")
|
| 36 |
+
|
| 37 |
+
return msg_dict
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
# build python side msg for sending to js
|
| 41 |
+
# parameter: content dict
|
| 42 |
+
# return: msg as string, to fill into a hidden textbox
|
| 43 |
+
def build_py_msg(action:str, content:dict):
|
| 44 |
+
util.printD("Start build_msg")
|
| 45 |
+
if not content:
|
| 46 |
+
util.printD("Content is None")
|
| 47 |
+
return
|
| 48 |
+
|
| 49 |
+
if not action:
|
| 50 |
+
util.printD("Action is None")
|
| 51 |
+
return
|
| 52 |
+
|
| 53 |
+
if action not in py_actions:
|
| 54 |
+
util.printD("Unknow action: " + action)
|
| 55 |
+
return
|
| 56 |
+
|
| 57 |
+
msg = {
|
| 58 |
+
"action" : action,
|
| 59 |
+
"content": content
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
util.printD("End build_msg")
|
| 64 |
+
return json.dumps(msg)
|
scripts/ch_lib/setting.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
# collecting settings to here
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
import modules.scripts as scripts
|
| 6 |
+
from . import util
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
name = "setting.json"
|
| 10 |
+
path = os.path.join(scripts.basedir(), name)
|
| 11 |
+
|
| 12 |
+
data = {
|
| 13 |
+
"model":{
|
| 14 |
+
"max_size_preview": True,
|
| 15 |
+
"skip_nsfw_preview": False
|
| 16 |
+
},
|
| 17 |
+
"general":{
|
| 18 |
+
"open_url_with_js": True,
|
| 19 |
+
"always_display": False,
|
| 20 |
+
"show_btn_on_thumb": True,
|
| 21 |
+
"proxy": "",
|
| 22 |
+
},
|
| 23 |
+
"tool":{
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# save setting
|
| 30 |
+
# return output msg for log
|
| 31 |
+
def save():
|
| 32 |
+
print("Saving setting to: " + path)
|
| 33 |
+
|
| 34 |
+
json_data = json.dumps(data, indent=4)
|
| 35 |
+
|
| 36 |
+
output = ""
|
| 37 |
+
|
| 38 |
+
#write to file
|
| 39 |
+
try:
|
| 40 |
+
with open(path, 'w') as f:
|
| 41 |
+
f.write(json_data)
|
| 42 |
+
except Exception as e:
|
| 43 |
+
util.printD("Error when writing file:"+path)
|
| 44 |
+
output = str(e)
|
| 45 |
+
util.printD(str(e))
|
| 46 |
+
return output
|
| 47 |
+
|
| 48 |
+
output = "Setting saved to: " + path
|
| 49 |
+
util.printD(output)
|
| 50 |
+
|
| 51 |
+
return output
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# load setting to global data
|
| 55 |
+
def load():
|
| 56 |
+
# load data into globel data
|
| 57 |
+
global data
|
| 58 |
+
|
| 59 |
+
util.printD("Load setting from: " + path)
|
| 60 |
+
|
| 61 |
+
if not os.path.isfile(path):
|
| 62 |
+
util.printD("No setting file, use default")
|
| 63 |
+
return
|
| 64 |
+
|
| 65 |
+
json_data = None
|
| 66 |
+
with open(path, 'r') as f:
|
| 67 |
+
json_data = json.load(f)
|
| 68 |
+
|
| 69 |
+
# check error
|
| 70 |
+
if not json_data:
|
| 71 |
+
util.printD("load setting file failed")
|
| 72 |
+
return
|
| 73 |
+
|
| 74 |
+
data = json_data
|
| 75 |
+
|
| 76 |
+
# check for new key
|
| 77 |
+
if "always_display" not in data["general"].keys():
|
| 78 |
+
data["general"]["always_display"] = False
|
| 79 |
+
|
| 80 |
+
if "show_btn_on_thumb" not in data["general"].keys():
|
| 81 |
+
data["general"]["show_btn_on_thumb"] = True
|
| 82 |
+
|
| 83 |
+
if "proxy" not in data["general"].keys():
|
| 84 |
+
data["general"]["proxy"] = ""
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
return
|
| 88 |
+
|
| 89 |
+
# save setting from parameter
|
| 90 |
+
def save_from_input(max_size_preview, skip_nsfw_preview, open_url_with_js, always_display, show_btn_on_thumb, proxy):
|
| 91 |
+
global data
|
| 92 |
+
data = {
|
| 93 |
+
"model":{
|
| 94 |
+
"max_size_preview": max_size_preview,
|
| 95 |
+
"skip_nsfw_preview": skip_nsfw_preview
|
| 96 |
+
},
|
| 97 |
+
"general":{
|
| 98 |
+
"open_url_with_js": open_url_with_js,
|
| 99 |
+
"always_display": always_display,
|
| 100 |
+
"show_btn_on_thumb": show_btn_on_thumb,
|
| 101 |
+
"proxy": proxy,
|
| 102 |
+
},
|
| 103 |
+
"tool":{
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
output = save()
|
| 108 |
+
|
| 109 |
+
if not output:
|
| 110 |
+
output = ""
|
| 111 |
+
|
| 112 |
+
return output
|
| 113 |
+
|
scripts/ch_lib/util.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
import os
|
| 3 |
+
import io
|
| 4 |
+
import hashlib
|
| 5 |
+
import requests
|
| 6 |
+
import shutil
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
version = "1.6.4"
|
| 10 |
+
|
| 11 |
+
def_headers = {'User-Agent': 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'}
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
proxies = None
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# print for debugging
|
| 18 |
+
def printD(msg):
|
| 19 |
+
print(f"Civitai Helper: {msg}")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE):
|
| 23 |
+
"""Yield pieces of data from a file-like object until EOF."""
|
| 24 |
+
while True:
|
| 25 |
+
chunk = file.read(size)
|
| 26 |
+
if not chunk:
|
| 27 |
+
break
|
| 28 |
+
yield chunk
|
| 29 |
+
|
| 30 |
+
# Now, hashing use the same way as pip's source code.
|
| 31 |
+
def gen_file_sha256(filname):
|
| 32 |
+
printD("Use Memory Optimized SHA256")
|
| 33 |
+
blocksize=1 << 20
|
| 34 |
+
h = hashlib.sha256()
|
| 35 |
+
length = 0
|
| 36 |
+
with open(os.path.realpath(filname), 'rb') as f:
|
| 37 |
+
for block in read_chunks(f, size=blocksize):
|
| 38 |
+
length += len(block)
|
| 39 |
+
h.update(block)
|
| 40 |
+
|
| 41 |
+
hash_value = h.hexdigest()
|
| 42 |
+
printD("sha256: " + hash_value)
|
| 43 |
+
printD("length: " + str(length))
|
| 44 |
+
return hash_value
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# get preview image
|
| 49 |
+
def download_file(url, path):
|
| 50 |
+
printD("Downloading file from: " + url)
|
| 51 |
+
# get file
|
| 52 |
+
r = requests.get(url, stream=True, headers=def_headers, proxies=proxies)
|
| 53 |
+
if not r.ok:
|
| 54 |
+
printD("Get error code: " + str(r.status_code))
|
| 55 |
+
printD(r.text)
|
| 56 |
+
return
|
| 57 |
+
|
| 58 |
+
# write to file
|
| 59 |
+
with open(os.path.realpath(path), 'wb') as f:
|
| 60 |
+
r.raw.decode_content = True
|
| 61 |
+
shutil.copyfileobj(r.raw, f)
|
| 62 |
+
|
| 63 |
+
printD("File downloaded to: " + path)
|
| 64 |
+
|
| 65 |
+
# get subfolder list
|
| 66 |
+
def get_subfolders(folder:str) -> list:
|
| 67 |
+
printD("Get subfolder for: " + folder)
|
| 68 |
+
if not folder:
|
| 69 |
+
printD("folder can not be None")
|
| 70 |
+
return
|
| 71 |
+
|
| 72 |
+
if not os.path.isdir(folder):
|
| 73 |
+
printD("path is not a folder")
|
| 74 |
+
return
|
| 75 |
+
|
| 76 |
+
prefix_len = len(folder)
|
| 77 |
+
subfolders = []
|
| 78 |
+
for root, dirs, files in os.walk(folder, followlinks=True):
|
| 79 |
+
for dir in dirs:
|
| 80 |
+
full_dir_path = os.path.join(root, dir)
|
| 81 |
+
# get subfolder path from it
|
| 82 |
+
subfolder = full_dir_path[prefix_len:]
|
| 83 |
+
subfolders.append(subfolder)
|
| 84 |
+
|
| 85 |
+
return subfolders
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
# get relative path
|
| 89 |
+
def get_relative_path(item_path:str, parent_path:str) -> str:
|
| 90 |
+
# printD("item_path:"+item_path)
|
| 91 |
+
# printD("parent_path:"+parent_path)
|
| 92 |
+
# item path must start with parent_path
|
| 93 |
+
if not item_path:
|
| 94 |
+
return ""
|
| 95 |
+
if not parent_path:
|
| 96 |
+
return ""
|
| 97 |
+
if not item_path.startswith(parent_path):
|
| 98 |
+
return item_path
|
| 99 |
+
|
| 100 |
+
relative = item_path[len(parent_path):]
|
| 101 |
+
if relative[:1] == "/" or relative[:1] == "\\":
|
| 102 |
+
relative = relative[1:]
|
| 103 |
+
|
| 104 |
+
# printD("relative:"+relative)
|
| 105 |
+
return relative
|
scripts/civitai_helper.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: UTF-8 -*-
|
| 2 |
+
# This extension can help you manage your models from civitai. It can download preview, add trigger words, open model page and use the prompt from preview image
|
| 3 |
+
# repo: https://github.com/butaixianran/
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
import modules.scripts as scripts
|
| 8 |
+
import gradio as gr
|
| 9 |
+
import os
|
| 10 |
+
import webbrowser
|
| 11 |
+
import requests
|
| 12 |
+
import random
|
| 13 |
+
import hashlib
|
| 14 |
+
import json
|
| 15 |
+
import shutil
|
| 16 |
+
import re
|
| 17 |
+
import modules
|
| 18 |
+
from modules import script_callbacks
|
| 19 |
+
from modules import shared
|
| 20 |
+
from scripts.ch_lib import model
|
| 21 |
+
from scripts.ch_lib import js_action_civitai
|
| 22 |
+
from scripts.ch_lib import model_action_civitai
|
| 23 |
+
from scripts.ch_lib import setting
|
| 24 |
+
from scripts.ch_lib import civitai
|
| 25 |
+
from scripts.ch_lib import util
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# init
|
| 29 |
+
|
| 30 |
+
# root path
|
| 31 |
+
root_path = os.getcwd()
|
| 32 |
+
|
| 33 |
+
# extension path
|
| 34 |
+
extension_path = scripts.basedir()
|
| 35 |
+
|
| 36 |
+
model.get_custom_model_folder()
|
| 37 |
+
setting.load()
|
| 38 |
+
|
| 39 |
+
# set proxy
|
| 40 |
+
if setting.data["general"]["proxy"]:
|
| 41 |
+
util.printD("Set Proxy: "+setting.data["general"]["proxy"])
|
| 42 |
+
util.proxies = {
|
| 43 |
+
"http": setting.data["general"]["proxy"],
|
| 44 |
+
"https": setting.data["general"]["proxy"],
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def on_ui_tabs():
|
| 51 |
+
# init
|
| 52 |
+
# init_py_msg = {
|
| 53 |
+
# # relative extension path
|
| 54 |
+
# "extension_path": util.get_relative_path(extension_path, root_path),
|
| 55 |
+
# }
|
| 56 |
+
# init_py_msg_str = json.dumps(init_py_msg)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
# get prompt textarea
|
| 60 |
+
# check modules/ui.py, search for txt2img_paste_fields
|
| 61 |
+
# Negative prompt is the second element
|
| 62 |
+
txt2img_prompt = modules.ui.txt2img_paste_fields[0][0]
|
| 63 |
+
txt2img_neg_prompt = modules.ui.txt2img_paste_fields[1][0]
|
| 64 |
+
img2img_prompt = modules.ui.img2img_paste_fields[0][0]
|
| 65 |
+
img2img_neg_prompt = modules.ui.img2img_paste_fields[1][0]
|
| 66 |
+
|
| 67 |
+
# ====Event's function====
|
| 68 |
+
def get_model_names_by_input(model_type, empty_info_only):
|
| 69 |
+
names = civitai.get_model_names_by_input(model_type, empty_info_only)
|
| 70 |
+
return model_name_drop.update(choices=names)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def get_model_info_by_url(url):
|
| 74 |
+
r = model_action_civitai.get_model_info_by_url(url)
|
| 75 |
+
|
| 76 |
+
model_info = {}
|
| 77 |
+
model_name = ""
|
| 78 |
+
model_type = ""
|
| 79 |
+
subfolders = []
|
| 80 |
+
version_strs = []
|
| 81 |
+
if r:
|
| 82 |
+
model_info, model_name, model_type, subfolders, version_strs = r
|
| 83 |
+
|
| 84 |
+
return [model_info, model_name, model_type, dl_subfolder_drop.update(choices=subfolders), dl_version_drop.update(choices=version_strs)]
|
| 85 |
+
|
| 86 |
+
# ====UI====
|
| 87 |
+
with gr.Blocks(analytics_enabled=False) as civitai_helper:
|
| 88 |
+
# with gr.Blocks(css=".block.padded {padding: 10px !important}") as civitai_helper:
|
| 89 |
+
|
| 90 |
+
# init
|
| 91 |
+
max_size_preview = setting.data["model"]["max_size_preview"]
|
| 92 |
+
skip_nsfw_preview = setting.data["model"]["skip_nsfw_preview"]
|
| 93 |
+
open_url_with_js = setting.data["general"]["open_url_with_js"]
|
| 94 |
+
always_display = setting.data["general"]["always_display"]
|
| 95 |
+
show_btn_on_thumb = setting.data["general"]["show_btn_on_thumb"]
|
| 96 |
+
proxy = setting.data["general"]["proxy"]
|
| 97 |
+
|
| 98 |
+
model_types = list(model.folders.keys())
|
| 99 |
+
no_info_model_names = civitai.get_model_names_by_input("ckp", False)
|
| 100 |
+
|
| 101 |
+
# session data
|
| 102 |
+
dl_model_info = gr.State({})
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
with gr.Box(elem_classes="ch_box"):
|
| 107 |
+
with gr.Column():
|
| 108 |
+
gr.Markdown("### Scan Models for Civitai")
|
| 109 |
+
with gr.Row():
|
| 110 |
+
max_size_preview_ckb = gr.Checkbox(label="Download Max Size Preview", value=max_size_preview, elem_id="ch_max_size_preview_ckb")
|
| 111 |
+
skip_nsfw_preview_ckb = gr.Checkbox(label="Skip NSFW Preview Images", value=skip_nsfw_preview, elem_id="ch_skip_nsfw_preview_ckb")
|
| 112 |
+
scan_model_types_ckbg = gr.CheckboxGroup(choices=model_types, label="Model Types", value=model_types)
|
| 113 |
+
|
| 114 |
+
# with gr.Row():
|
| 115 |
+
scan_model_civitai_btn = gr.Button(value="Scan", variant="primary", elem_id="ch_scan_model_civitai_btn")
|
| 116 |
+
# with gr.Row():
|
| 117 |
+
scan_model_log_md = gr.Markdown(value="Scanning takes time, just wait. Check console log for detail", elem_id="ch_scan_model_log_md")
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
with gr.Box(elem_classes="ch_box"):
|
| 121 |
+
with gr.Column():
|
| 122 |
+
gr.Markdown("### Get Model Info from Civitai by URL")
|
| 123 |
+
gr.Markdown("Use this when scanning can not find a local model on civitai")
|
| 124 |
+
with gr.Row():
|
| 125 |
+
model_type_drop = gr.Dropdown(choices=model_types, label="Model Type", value="ckp", multiselect=False)
|
| 126 |
+
empty_info_only_ckb = gr.Checkbox(label="Only Show Models have no Info", value=False, elem_id="ch_empty_info_only_ckb", elem_classes="ch_vpadding")
|
| 127 |
+
model_name_drop = gr.Dropdown(choices=no_info_model_names, label="Model", value="ckp", multiselect=False)
|
| 128 |
+
|
| 129 |
+
model_url_or_id_txtbox = gr.Textbox(label="Civitai URL", lines=1, value="")
|
| 130 |
+
get_civitai_model_info_by_id_btn = gr.Button(value="Get Model Info from Civitai", variant="primary")
|
| 131 |
+
get_model_by_id_log_md = gr.Markdown("")
|
| 132 |
+
|
| 133 |
+
with gr.Box(elem_classes="ch_box"):
|
| 134 |
+
with gr.Column():
|
| 135 |
+
gr.Markdown("### Download Model")
|
| 136 |
+
with gr.Row():
|
| 137 |
+
dl_model_url_or_id_txtbox = gr.Textbox(label="Civitai URL", lines=1, value="")
|
| 138 |
+
dl_model_info_btn = gr.Button(value="1. Get Model Info by Civitai Url", variant="primary")
|
| 139 |
+
|
| 140 |
+
gr.Markdown(value="2. Pick Subfolder and Model Version")
|
| 141 |
+
with gr.Row():
|
| 142 |
+
dl_model_name_txtbox = gr.Textbox(label="Model Name", interactive=False, lines=1, value="")
|
| 143 |
+
dl_model_type_txtbox = gr.Textbox(label="Model Type", interactive=False, lines=1, value="")
|
| 144 |
+
dl_subfolder_drop = gr.Dropdown(choices=[], label="Sub-folder", value="", interactive=True, multiselect=False)
|
| 145 |
+
dl_version_drop = gr.Dropdown(choices=[], label="Model Version", value="", interactive=True, multiselect=False)
|
| 146 |
+
dl_all_ckb = gr.Checkbox(label="Download All files", value=False, elem_id="ch_dl_all_ckb", elem_classes="ch_vpadding")
|
| 147 |
+
|
| 148 |
+
dl_civitai_model_by_id_btn = gr.Button(value="3. Download Model", variant="primary")
|
| 149 |
+
dl_log_md = gr.Markdown(value="Check Console log for Downloading Status")
|
| 150 |
+
|
| 151 |
+
with gr.Box(elem_classes="ch_box"):
|
| 152 |
+
with gr.Column():
|
| 153 |
+
gr.Markdown("### Check models' new version")
|
| 154 |
+
with gr.Row():
|
| 155 |
+
model_types_ckbg = gr.CheckboxGroup(choices=model_types, label="Model Types", value=["lora"])
|
| 156 |
+
check_models_new_version_btn = gr.Button(value="Check New Version from Civitai", variant="primary")
|
| 157 |
+
|
| 158 |
+
check_models_new_version_log_md = gr.HTML("It takes time, just wait. Check console log for detail")
|
| 159 |
+
|
| 160 |
+
with gr.Box(elem_classes="ch_box"):
|
| 161 |
+
with gr.Column():
|
| 162 |
+
gr.Markdown("### Other Setting")
|
| 163 |
+
with gr.Row():
|
| 164 |
+
open_url_with_js_ckb = gr.Checkbox(label="Open Url At Client Side", value=open_url_with_js, elem_id="ch_open_url_with_js_ckb")
|
| 165 |
+
always_display_ckb = gr.Checkbox(label="Always Display Buttons", value=always_display, elem_id="ch_always_display_ckb")
|
| 166 |
+
show_btn_on_thumb_ckb = gr.Checkbox(label="Show Button On Thumb Mode", value=show_btn_on_thumb, elem_id="ch_show_btn_on_thumb_ckb")
|
| 167 |
+
|
| 168 |
+
proxy_txtbox = gr.Textbox(label="Proxy", interactive=True, lines=1, value=proxy, info="format: http://127.0.0.1:port")
|
| 169 |
+
|
| 170 |
+
save_setting_btn = gr.Button(value="Save Setting")
|
| 171 |
+
general_log_md = gr.Markdown(value="")
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
# ====Footer====
|
| 175 |
+
gr.Markdown(f"<center>version:{util.version}</center>")
|
| 176 |
+
|
| 177 |
+
# ====hidden component for js, not in any tab====
|
| 178 |
+
js_msg_txtbox = gr.Textbox(label="Request Msg From Js", visible=False, lines=1, value="", elem_id="ch_js_msg_txtbox")
|
| 179 |
+
py_msg_txtbox = gr.Textbox(label="Response Msg From Python", visible=False, lines=1, value="", elem_id="ch_py_msg_txtbox")
|
| 180 |
+
|
| 181 |
+
js_open_url_btn = gr.Button(value="Open Model Url", visible=False, elem_id="ch_js_open_url_btn")
|
| 182 |
+
js_add_trigger_words_btn = gr.Button(value="Add Trigger Words", visible=False, elem_id="ch_js_add_trigger_words_btn")
|
| 183 |
+
js_use_preview_prompt_btn = gr.Button(value="Use Prompt from Preview Image", visible=False, elem_id="ch_js_use_preview_prompt_btn")
|
| 184 |
+
js_dl_model_new_version_btn = gr.Button(value="Download Model's new version", visible=False, elem_id="ch_js_dl_model_new_version_btn")
|
| 185 |
+
|
| 186 |
+
# ====events====
|
| 187 |
+
# Scan Models for Civitai
|
| 188 |
+
scan_model_civitai_btn.click(model_action_civitai.scan_model, inputs=[scan_model_types_ckbg, max_size_preview_ckb, skip_nsfw_preview_ckb], outputs=scan_model_log_md)
|
| 189 |
+
|
| 190 |
+
# Get Civitai Model Info by Model Page URL
|
| 191 |
+
model_type_drop.change(get_model_names_by_input, inputs=[model_type_drop, empty_info_only_ckb], outputs=model_name_drop)
|
| 192 |
+
empty_info_only_ckb.change(get_model_names_by_input, inputs=[model_type_drop, empty_info_only_ckb], outputs=model_name_drop)
|
| 193 |
+
|
| 194 |
+
get_civitai_model_info_by_id_btn.click(model_action_civitai.get_model_info_by_input, inputs=[model_type_drop, model_name_drop, model_url_or_id_txtbox, max_size_preview_ckb, skip_nsfw_preview_ckb], outputs=get_model_by_id_log_md)
|
| 195 |
+
|
| 196 |
+
# Download Model
|
| 197 |
+
dl_model_info_btn.click(get_model_info_by_url, inputs=dl_model_url_or_id_txtbox, outputs=[dl_model_info, dl_model_name_txtbox, dl_model_type_txtbox, dl_subfolder_drop, dl_version_drop])
|
| 198 |
+
dl_civitai_model_by_id_btn.click(model_action_civitai.dl_model_by_input, inputs=[dl_model_info, dl_model_type_txtbox, dl_subfolder_drop, dl_version_drop, dl_all_ckb, max_size_preview_ckb, skip_nsfw_preview_ckb], outputs=dl_log_md)
|
| 199 |
+
|
| 200 |
+
# Check models' new version
|
| 201 |
+
check_models_new_version_btn.click(model_action_civitai.check_models_new_version_to_md, inputs=model_types_ckbg, outputs=check_models_new_version_log_md)
|
| 202 |
+
|
| 203 |
+
# Other Setting
|
| 204 |
+
save_setting_btn.click(setting.save_from_input, inputs=[max_size_preview_ckb, skip_nsfw_preview_ckb, open_url_with_js_ckb, always_display_ckb, show_btn_on_thumb_ckb, proxy_txtbox], outputs=general_log_md)
|
| 205 |
+
|
| 206 |
+
# js action
|
| 207 |
+
js_open_url_btn.click(js_action_civitai.open_model_url, inputs=[js_msg_txtbox, open_url_with_js_ckb], outputs=py_msg_txtbox)
|
| 208 |
+
js_add_trigger_words_btn.click(js_action_civitai.add_trigger_words, inputs=[js_msg_txtbox], outputs=[txt2img_prompt, img2img_prompt])
|
| 209 |
+
js_use_preview_prompt_btn.click(js_action_civitai.use_preview_image_prompt, inputs=[js_msg_txtbox], outputs=[txt2img_prompt, txt2img_neg_prompt, img2img_prompt, img2img_neg_prompt])
|
| 210 |
+
js_dl_model_new_version_btn.click(js_action_civitai.dl_model_new_version, inputs=[js_msg_txtbox, max_size_preview_ckb, skip_nsfw_preview_ckb], outputs=dl_log_md)
|
| 211 |
+
|
| 212 |
+
# the third parameter is the element id on html, with a "tab_" as prefix
|
| 213 |
+
return (civitai_helper , "Civitai Helper", "civitai_helper"),
|
| 214 |
+
|
| 215 |
+
script_callbacks.on_ui_tabs(on_ui_tabs)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
|
style.css
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
blockquote ul {
|
| 2 |
+
list-style:disc;
|
| 3 |
+
margin:4px 40px;
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
blockquote ol {
|
| 7 |
+
list-style:decimal;
|
| 8 |
+
margin:4px 40px;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
.block.padded.ch_box {
|
| 12 |
+
padding: 10px !important;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.block.padded.ch_vpadding {
|
| 16 |
+
padding: 10px 0 !important;
|
| 17 |
+
}
|
| 18 |
+
|