├── .gitignore ├── README.KR.md ├── README.md ├── img ├── card-no-preview.png └── nsfw-no-preview.png ├── scripts ├── civitai_manager_libs │ ├── __init__.py │ ├── civitai.py │ ├── civitai_gallery_action.py │ ├── civitai_shortcut_action.py │ ├── classification.py │ ├── classification_action.py │ ├── classification_browser_page.py │ ├── downloader.py │ ├── ishortcut.py │ ├── ishortcut_action.py │ ├── model.py │ ├── model_action.py │ ├── prompt.py │ ├── prompt_ui.py │ ├── recipe.py │ ├── recipe_action.py │ ├── recipe_browser_page.py │ ├── sc_browser_page.py │ ├── scan_action.py │ ├── setting.py │ ├── setting_action.py │ └── util.py └── civitai_shortcut.py └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__ 2 | sc_gallery 3 | sc_infos 4 | sc_thumb_images 5 | CivitaiShortCut.json 6 | CivitaiShortCutBackupUrl.json 7 | CivitaiShortCutClassification.json 8 | CivitaiShortCutRecipeCollection.json 9 | CivitaiShortCutSetting.json 10 | -------------------------------------------------------------------------------- /README.KR.md: -------------------------------------------------------------------------------- 1 | # 개인적인 사정으로 인해 추가 업데이트가 어렵습니다. 출처에 제한 없이 자유롭게 사용하시기 바랍니다. 진심으로 사과드립니다. 2 | 3 | # 이 업데이트는 Gradio 버전 3.41.2 이상과 호환됩니다. 올바르게 작동하려면 A1111의 최신 업데이트가 필요합니다. 4 | 5 | # Civitai Shortcut 6 | 7 | Stable Diffusion Webui Extension for Civitai, to download civitai shortcut and models. 8 | 9 | # Install 10 | 11 | Stable Diffusion Webui의 Extension 탭에서 'URL에서 설치' 서브 탭으로 이동하세요. 해당 프로젝트의 URL을 복사하여 붙여넣고 '설치'를 클릭하세요. 12 | 13 | git clone https://github.com/sunnyark/civitai-shortcut 14 | URL : https://github.com/sunnyark/civitai-shortcut 15 | 16 | # Usage instructions 17 | 18 | ![screenshot 2023-09-15 203613](https://github.com/sunnyark/civitai-shortcut/assets/40237431/fdac59c0-0ced-41fb-8faa-83029b3ffc3f) 19 | 20 | * Register Model : 이 기능은 Civitai 사이트의 모델 URL을 이용하여 모델의 정보를 Civitai에서 가져와 드록합니다. 21 | 주소 창에서 URL을 클릭하고 드래그하거나 저장된 인터넷 바로 가기를 드래그앤드롭할 수 있습니다. 또한 여러 개의 인터넷 바로 가기를 선택하고 한 번에 드롭할 수도 있습니다. 22 | * Model Browser : 이 기능은 등록된 모델을 섬네일 형식으로 표시하며, 선택 시 창의 오른쪽에 해당 모델의 세부 정보를 표시합니다. 23 | * Scan New Version : 이 기능은 Civitai 사이트에서 다운로드한 모델의 최신 버전을 검색하는 기능입니다. 사이트에서 정보를 가져오며, 사이트가 정상 작동할 때에만 정상적으로 작동합니다. 24 | * Model Information 의 정보를 최신 상태로 유지하기 위해서는 주기적인 데이터 업데이트가 필요합니다. 25 | 방법에는 model information 하면에서 Update Shortcut 기능을 이용해서 개별적으로 업데이트 하거나, manage->setting->option 의 자동 업데이트를 활성화 , Manage->Scan and Update Models->Update the model information for the shortcut 기능을 이용하는 방법이 있습니다. 26 | 27 | ![register_model_direct](https://github.com/sunnyark/civitai-shortcut/assets/40237431/c6db4ced-9cec-4488-ac3f-9a17fadb42b8) 28 | 29 | ![register_model_shortcut](https://github.com/sunnyark/civitai-shortcut/assets/40237431/a18cc188-0d7a-4860-91fa-b9b2b27f4bdc) 30 | 31 | * Prompt Recipe : Prompt recipe는 자주 사용하는 Prompt를 등록하고 관리 할수 있는 기능입니다. 32 | 33 | ![screenshot 2023-09-15 201815](https://github.com/sunnyark/civitai-shortcut/assets/40237431/d3d61c0a-c749-40ee-bc8c-69c35e9c6ba7) 34 | 35 | ![screenshot 2023-09-15 201833](https://github.com/sunnyark/civitai-shortcut/assets/40237431/773dc92f-3fd5-4509-94bb-99a9e50bec34) 36 | 37 | ![screenshot 2023-09-15 201853](https://github.com/sunnyark/civitai-shortcut/assets/40237431/ecf6e1a7-59f8-4eb5-a58f-5a7ff7824437) 38 | 39 | * Assistance : 40 | 41 | 1. Classification : Shortcut에서 사용하는 분류항목을 관리하는 기능입니다. 42 | 43 | ![screenshot 2023-09-15 201933](https://github.com/sunnyark/civitai-shortcut/assets/40237431/7881d3d8-d2a3-4502-b39c-fb40a17cf21c) 44 | 45 | ![screenshot 2023-09-15 201956](https://github.com/sunnyark/civitai-shortcut/assets/40237431/94b2b2a1-f148-42dc-b6a8-21c1381dc55f) 46 | 47 | ![screenshot 2023-09-15 202004](https://github.com/sunnyark/civitai-shortcut/assets/40237431/9003d94d-5a13-4613-9fa6-722b1e892874) 48 | 49 | 2. Scan and Update Models 50 | Scan Models : 현재 보유 중인 모델 정보가 없는 모델에 대해 스캔하여 바로 가기를 등록하는 기능입니다. 51 | Update the model information for the shortcut : 등록된 바로 가기의 정보를 최신 정보로 업데이트합니다. 52 | Scan downloaded models for shortcut registration : 삭제되거나 모델 정보가 누락된 다운로드된 모델에 대해 새로운 바로 가기를 등록합니다. 53 | Update Downloaded Model : 이 기능은 Lora 메타데이터 파일이 없는 다운로드된 모델을 위한 Lora 메타데이터 파일을 생성합니다. 54 | 55 | ![screenshot 2023-09-15 202018](https://github.com/sunnyark/civitai-shortcut/assets/40237431/7f200d24-a4ca-4e23-834a-71470590ee49) 56 | 57 | * Setting : 확장프로그램의 다양한 설정값을 관리합니다. 58 | 59 | ![screenshot 2023-09-15 202037](https://github.com/sunnyark/civitai-shortcut/assets/40237431/67e2e7c5-0cd6-4917-a4c8-b9ffb45832f9) 60 | 61 | # Features 62 | 63 | Civitai 사이트의 모델 URL을 저장하여 나중에 참조하고 보관할 수 있습니다. 64 | 65 | 이를 통해 필요할 때 모델을 다운로드하고 모델이 최신 버전으로 업데이트되었는지 확인할 수 있습니다. 66 | 다운로드 된 모델은 지정된 저장 위치에 저장됩니다. 67 | 68 | # Notice 69 | 70 | 4개의 폴더와 5개의 json 파일이 생성되며 각각의 역할은 다음과 같습니다. 71 | 72 | * sc_recipes : Prompt Recipe이미지가 저장되는 폴더 73 | * sc_gallery : 유저갤러리의 이미지 캐싱을 위한폴더 74 | * sc_thumb_images : 등록된 url의 thumbnail이 저장되는 폴더 75 | * sc_infos : 등록시 모델정보와 이미지가 저장되는 폴더 76 | * CivitaiShortCut.json : 등록된 model url 을 기록 관리 하는 json 파일 77 | * CivitaiShortCutClassification.json: 분류항목을 관리하는 파일 78 | * CivitaiShortCutSetting.json: 설정값을 저장하는 파일 79 | * CivitaiShortCutRecipeCollection.json : Prompt Recipe 의 데이터를 관리하는 파일 80 | * CivitaiShortCutBackupUrl.json : Shortcut 등록시의 URL을 백업하는 파일 81 | 82 | # Change Log 83 | 84 | v 1.6.7 85 | 86 | * 기능에 맞도록 각 매뉴의 명칭을 변경했습니다. 87 | 88 | v 1.6.6 89 | 90 | * Prompt Recipe에서 레퍼런스를 관리할때 Delete from references when selecting a thumbnail 을 선택해제하고 리퍼런스 모델을 클릭했을시 모델 인포메이션으로 이동하지 않고 상단에 간략한 정보를 보여주도록 변경됨. 91 | * Prompt Recipe에서 선택한 reference model 의 type 에 따라 프롬프트에 지시어를 입력할 수 있는 기능추가 92 | * classification의 Delete from classification when selecting a thumbnail 을 선택해제하고 classification 에 속한 모델을 클릭했을시 모델 인포메이션으로 이동하지 않고 우측애 간략한 정보를 보여주도록 변경됨. 93 | 94 | v 1.6.5 95 | 96 | * 이 업데이트는 Gradio 버전 3.41.2 이상과 호환됩니다. 97 | * 올바르게 작동하려면 A1111의 최신 업데이트가 필요합니다. 98 | 99 | v 1.6.4 100 | 101 | * sdui의 LoRa metadata editor에 사용가능한 model meta data를 저장.(단, 기존것이 없을경우에만 저장됩니다.) 102 | * 일부 메뉴의 명칭을 변경 103 | * Assistance->Scan and Update Models 에 누락된 lora metadata file을 생성할 수 있는 기능 추가(2023-08-19) 104 | 105 | v 1,6.3 106 | 107 | * classification 의 인터페이스 변경 108 | * classification 의 shortcut browser 기능추가, 편의성 개선 109 | * shortcut information의 이미지에서 recipe로 prompt를 보낼때 해당 shortcut을 기본 레퍼런스 모델로 추가하게 수정. 110 | 111 | v 1.6.2 112 | 113 | * shortcut의 등록날짜를 업데이트 114 | (기존데이터는 Assistance->Update Shortcuts:Update the model information for the shortcut 수행필요) 115 | * gallery의 설정을 직관적으로 변경 116 | 117 | v 1.6.1 118 | 119 | * recipe의 reference shortcut model의 인터페이스 변경 120 | * 섬네일이 자동으로 생성될때 nsfw level이 가장 낮은 (건전한) 모델이 선정되게 변경. 121 | * 전체 모든 범위에서 nsfw filter 가 작동하게 변경 122 | * classification 에서 표시되는 shortcut model중 삭제된 모델이 표시되도록 변경 123 | 124 | v 1.6.0 125 | 126 | * NSFW Filter 기능 추가 127 | * prompt recipe 리뉴얼 128 | * prompt recipe에 reference model을 등록 할수 있는 tab추가 129 | * prompt recipe 검색기능 보강 130 | * shortcut broswer의 검색창의 위치를 섬네일 목록의 상/하중 원하는 위치로 변경할 수 있도록 setting 에 추가. 131 | 132 | v 1.5.8 133 | 134 | * 모델 인포메이션에 personal note 항목을 추가, 검색에서 "@" 를 이용해서 검색가능 135 | 136 | v 1.5.7 137 | 138 | * Civitai에서 제공하는 기본 모델을 사용하여 필터링하는 기능이 추가되었습니다. 139 | * 분류 항목을 검색 섹션에서 검색하는 대신 드롭다운 목록에서 선택할 수 있도록 변경합니다. 선택된 분류 항목은 'AND' 연산으로 작동하여 교집합처럼 동작합니다. 이렇게 하면 다중 카테고리에 속하는 바로 가기들을 더 자세하게 관리할 수 있습니다. 140 | 141 | v 1.5.6 142 | 143 | * Civitai에서 권장하는 대로 "user gallery paging" 방법을 cursor paging 으로 변경합니다. 144 | 145 | v 1.5.5 146 | 147 | * 파일 다운로드시 다운로드 목록에 primary 파일이 없으면 version info 와 프리뷰 이미지를 생성하지 않도록 수정됨. 해당파일만 다운로드하게됨. 148 | * 다운로드 파일 목록에 primary 여부 표시. 149 | * shortcut이 name 을 기준으로 정렬 되도록 수정 150 | 151 | v 1.5.4 152 | 153 | * 프롬프트 레시피에서 이미지를 drop할때 새 레시피가 생성되지 않고 현재 상태의 프롬프트 내용만 갱신되도록 변경 154 | * 모델 다운로드 폴더명에 사용가능한 이름을 제시기능 추가 : 모델명, 작성자, 모델 tag 순으로 나열됨 155 | * 오탈자 수정 156 | * shortcut을 선택할때 information이 선택한 탭에서만 로딩되도록 변경. 157 | * User Gallery 화면에서 사용하는 모델정보는 다운로드한 모델 정보에서 검색하도록 수정합니다. Civitai 웹 사이트의 사용자 갤러리 이미지에서 이미지 정보만 실시간으로 검색합니다. (ASGI 애플리케이션 오류가 줄어들 것으로 기대합니다.) 158 | 159 | v 1.5.3 160 | 161 | * 파일명을 변경할수 있게 수정 162 | * 파일 선택시 체크 박스를 통해서만 선택가능하게 변경 163 | * 파일명을 클릭하면 파일명을 변경할 수 있는 입력란이 나타납니다. 164 | * 파일 다운로드시 다운로드 파일이 없어도 인포와 프리뷰 이미지가 다운로드 되던 것을 못하게 변경 165 | 파일명을 변경가능함에따라 혼동을 막기위함 166 | * Scan Models 의 검색조건을 변경하여 다운로드 파일이 여러개인 경우도 처리 하도록 변경 167 | * 썸네일의 이미지를 크기를 작게 수정 168 | 썸네일의 이미지를 변경 가능하게 수정 169 | * Shortcut Browser screen ratio 오류 수정 170 | 171 | v 1.5.2 172 | 173 | * model download시 생성되는 폴더생성에 대한 설명추가/문구 변경. 174 | * Prompt Recipe 의 위치 변경 (Assistance -> Top level) 175 | * Scan and Update Models 의 위치변경 (Manage -> Assictance) 176 | 177 | v 1.5.1 178 | 179 | * preview 이미지의 변경 기능 추가 model imformation tab -> images : change preview image 버튼 180 | 모델을 다운로드한 경우에만 버튼이 나타납니다. 181 | * 파일 다운로드시 create model folder 선택시 다운로드할 모델폴더명을 변경가능하게 변경. 182 | 다운로드된 모델의 경우 기본 표기되는 폴더이름이 다운로드된 폴더정보를 기본 정보로 표기되도록 변경. 모델이 중복 다운로드된 경우 부정확할수 있음. 183 | Downloaded Model Information Tab에서 다운로드한 파일의 다운로드된 위치, 파일을 확인할수 있습니다. 184 | 185 | v 1.5 186 | 187 | * information tab의 civitai model information 과 saved model information tab이 통합되었습니다. 188 | civitai 싸이트의 실시간 정보를 가져오던 civitai model information이 제거된대신 stable diffusion 시작시 등록된 솟컷의 정보를 자동으로 업데이트 하는 기능이 추가되었습니다. 189 | manage->setting->option 에서 기능을 끄고 켤수 있습니다. 190 | * Assistance tab이 추가되고 classification이 Assistance tab으로 이동 하였습니다. 191 | * Assistance tab에 새로운 기능인 Prompt recipe 기능이 추가 되었습니다. 192 | * Prompt recipe는 자주 사용하는 Prompt를 등록하고 관리 할수 있는 기능입니다. 193 | 프롬프트의 등록은 직접 작성하거나 , 이미지 파일일을 업로드 또는 Model Information 또는 User Gallery 의 Image Information 의 send to recipe 버튼으로 등록 할수 있습니다. 194 | * classification 과 prompt recipe에서 목록의 [New Classification] ,[New Prompt Recipe] 상태에서만 Create 버튼이 나타나도록 변경하여 Create 상태와 Update 상태를 명확히 하였습니다. 195 | * bug : prompt recipe 에서 저장되는 이미지는 이미지 생성정보가 사라지는 버그가 있습니다. gradio Image 컴포넌트 문제인것 같습니다. 196 | * 숏컷의 등록 URL을 백업하던 sc_saved 폴더가 더이상 사용되지 않습니다. (삭제 가능) 197 | 대신 CivitaiShortCutBackupUrl.json 파일에 {url : 이름} 형태로 저장됩니다. 198 | Upload항목에 이 파일을 upload 하면 숏컷을 다시 등록 할 수 있습니다. 199 | 이 파일은 Manage->Scan and Update Models ->Update Shorts -> "Update the model information for the shortcut" 을 수행하거나 자동 업데이트 기능을 켜놓으면 자동으로 생성, 업데이트 됩니다. 200 | * prompt recipe 의 이미지를 저장하는 sc_recipe 폴더 생성됩니다. 201 | * manage->setting->option 에 classification gallery preview mode disable 이 생겼습니다. 이는 실험적인것입니다. 202 | gradio 에 정식으로 기능이 생기면 제거될것입니다. 203 | 204 | v 1.4a 205 | 206 | * 인터페이스 디자인 인이 변경되었습니다. 207 | * 다운 받을 파일에 대한 좀더 자세한 정보를 제공하도록 변경되었습니다. 208 | * 파일을 선택하지 않고 다운로드 시에도 version information 파일과 preview 이미지를 생성 하도록 변경하였습니다. 209 | * 실제 파일을 다운 로드 하지 않고도 Downloaded Model Information을 이용하실수 있습니다. 210 | * 파일을 다운 받지 않았어도 다운 받은걸로 인식되므로 참고 부탁드립니다. 211 | * 인포메이션탭에서 제공하는 open folder 기능은 여러곳에 중복 다운로드했을시 부정확할수 있습니다. 212 | 정확한 내용을 알고 싶으시면 Downloaded Model Information tab 을 활용해주세요. 213 | * Downloaded Model Information tab 에서 파일명이 동일한 파일을 다운 받을 경우 모두 다은받은걸로 인식되는 오류가 있습니다. 214 | * Manage -> Classification 디자인 과 기능에 일부 변경이 있습니다. 215 | * Classification 의 생성 수정 삭제 업데이트 기능이 좌측의 Shortcut Item 항목과 통합되었습니다. 216 | 217 | - Update 기능은 classification 의 이름과 설명의 Update만 합니다. 218 | - classification에 등록되는 shortcut Item 의 저장은 Save Classification Shortcuts 을 이용하셔야 합니다. 219 | * Manage -> Setting 에 Screen Style 항목이 추가되었습니다. 220 | Shortcut Brower 와 Information Tab 간의 비율을 설정할수있습니다. 221 | 자신의 모니터나 작업취향에 따라 적절한 비율을 선택하시고 적용할수 있습니다. 222 | Shortcut Browser and Information Images 항목과 적절히 조합하시길 추천드립니다. 223 | * Download Folder for Extensions 에 LyCORIS의 다운로드 위치를 설정할수 있는 항목이 추가 되었습니다. 224 | 기존에 는 LoRA 폴더를 같이 이용했지만 LyCORIS확장을 사용하시면 extra networks 에서도 인식되는것을 알게되어 적절히 수정 하실수 있게 추가 하였습니다. 225 | (알려주시어 감사합니다.) 226 | * Reload UI 버튼이 추가 되었습니다. SDUI 를 리로드 시킵니다. 227 | 단 setting을 저장하는 기능은 넣지 않았습니다. 저장은 옆의 Save Setting 버튼을 이용해주세요. :) 228 | * Upload 항목에 업로드 되는 파일의 내부 형식을 조금 변경하였습니다. 229 | 230 | - url=civitai model url 의 구성을하면 인식되도록 수정하였습니다. 231 | - e.g.) 232 | sample.txt : 233 | url=https://civitai.com/models/64863/cute-oil-impasto-chibi 234 | url=https://civitai.com/models/64850/koga-tomoe-from-bunny-girl-senpai 235 | url=https://civitai.com/models/64849/blackwhite 236 | * Upload 의 드래그 & 드롭이 리눅스 환경에서 제대로 작동 되지 않습니다. 237 | 불편하시겠지만 당분간 윗쪽의 Textbox 를 활용해주세요. 238 | 239 | v 1.4 240 | 241 | * 인포메이션 탭에 Downloaded Model Information 탭이 추가 되었습니다. 242 | 현재 다운로드한 파일의 정보를 확인 할 수 있는 탭입니다. 다운로드한 모델의 버전 리스트가 나타나며 선택시 하단에 세부 정보가 나타납니다. 세부표시 항목에서는 실제 다운 로드한 파일의 정보를 확인할수 있으며 다운로드된 폴더에 접근할수 있습니다. 파일 중복등의 사유로 다소 부정확할수 있습니다. 또한 해당버전의 Civitai 제공 정보를 json형태로 확인 하실수 있습니다. 243 | 모든 Open Folder 기능은 해당 폴더가 존재 할때만 작동됩니다. 244 | 245 | - Open Download Image Folder : 다운로드한 이미지들이 포함되어 있는 폴더를 열어줍니다. 246 | - Open Saved Information Folder : Shortcut 등록시 모델의 정보파일과 이미지들이 다운로드 되는 폴더를 열어줍니다. 247 | 삭제시 되었을시 Civitai Shortcut->Saved Model Inforamtion : Update Model Information 이나 Manage->Scan and Update Models->Update Shortcuts : Update the model information for the shortcut 으로 복구할 수 있습니다. 248 | * 좌측의 shortcut browser의 표시 방식을 설정할 수 있습니다. 249 | Manage->Setting->Shortcut Browser and Information Images : Shortcut Browser Thumbnail Count per Page 250 | 항목에서 설정할 수 있으며 0으로 설정시 기존의 방식처럼 전체 리스트가 출력됩니다. 251 | * 추가 기능 설명 1 (Update the model information for the shortcut) 252 | Manage->Scan and Update Models->Update Shortcuts : Update the model information for the shortcut 253 | 기능은 이미등록된 shortcut 의 정보를 최신 정보로 업데이트 하는 기능입니다. 이는 등록된 모든 숏컷을 대상으로 합니다. 254 | 숏컷의 개별적인 업데이트는 Civitai Shortcut->Saved Model Inforamtion : Update Model Information 에서 할수 있습니다. 255 | * 추가 기능 설명 2 (Scan new version model) 256 | Civitai Shortcut->Scan New Version : Scan new version model: 의 기능은 다운 로드된 모델 버전파일의 새로운 버전이 있는지 스캔하는 기능입니다. 다운로드하지 않은 모델은 검색하지 않습니다. 257 | 258 | v 1.3ss 259 | 260 | * 모델의 다운로드 규칙, 방법에 변경이 있습니다. 261 | 다운로드시 폴더를 지정할수 있습니다. 사용자 분류 항목을 모델의 다운로드 항목으로 설정할수있습니다. 262 | * 사용자가 등록한 분류 항목으로 설정하면 하위폴더는 작성할 수 없으며 사용자가 지정한 분류항목의 폴더가 model type base folder(e.g. model/lora) 에 만들어지고 다운로드 됩니다. 263 | * "Create Model Name Folder" 선택하면 model type base folder(e.g. model/lora) 에 모델명을 베이스로 폴더가 만들어지며 버전에 따라 서브폴더를 원하는 이름으로 만들수 있습니다. 264 | * 다운로드된 모델파일은 원하는 폴더로 자유롭게 이동하셔도 됩니다. 265 | 본 확장프로그램은 파일의 다운로드의 편의를 돕기만할뿐 다운로드된 파일을 관리 하지는 않습니다. 266 | 편하게 이동 하셔도 문제 없습니다. 267 | * 이미지 다운로드는 기본으로 outputs/download-images 에 다운 로드되며 setting->download for extension->Download Images Folder 에서 설정할수 있습니다. 시스템에따라 접근권한의 설정이 필요할수 있습니다. 268 | * 사용자 정의 분류항목이 폴더명으로 사용되어지므로 폴더생성에 사용하기 어려운 문자는 변경을 권장드립니다. 269 | 폴더 생성시 "-" 문자로 대체됩니다. 270 | * thumbnail 이미지의 display type 을 변경할수 있습니다. setting->Shortcut Browser and Information Images->Gallery Thumbnail Image Style 에서 설정할수 있습니다. 271 | * 쇼컷 등록시 다운로드 되는 이미지의 수를 설정할수 있습니다. setting->Shortcut Browser and Information Images->Maximum number of download images per version 에서 절정하룻 있으며 0일때는 모두 다운로드 됩니다. 272 | 273 | v 1.3c 274 | 275 | * Manage tab 에 scan and update models 와 setting 탭추가 276 | * Scan and Update Models tab 277 | Scan Models for Civitai - 보유하고 있는 모델중 모델 정보가 없는 파일을 스캔하고 숏컷으로 등록 함 278 | Update Shortcut - Upload tab 에 있던 shortcut update 기능을 옯겨옴 279 | Update the model information for the shortcut - 등록된 shortcut의 정보를 최신정보로 업데이트함 280 | Scan downloaded models for shortcut registration - 다운 로드 받았지만 삭제되었거나 모델 정보가 있는 모델의 shortcut을 새로이 등록해줌 281 | * Setting tab 282 | Shortcut Browser and Information Images,User Gallery Images - 이미지 갤러리의 컬럼의 수를 설정함. 283 | Download Folder for Extensions - 특정 확장프로그램의 다운로드 경로를 설정함 284 | * 모델의 정보를 기록하는 모델 인포 파일의 이름이 변경되었습니다. 285 | 이에 따라 정상적인 model info 파일이 있는 모델도 "Scan Models for Civitai" 작업시 새로운 폴더로 이동되어질수 있습니다. 286 | "Create a model folder corresponding to the model type." 옵션을 해제 하시면 이동을 막을수 있습니다. 287 | 288 | v 1.3a 289 | 290 | * A new feature has been added that allows you to manage and classify items. 291 | You can add, delete, and update classification items in the "manage" -> "classification" tab. 292 | To add a shortcut, select the desired classification item in the center top and click on the list on the left to register the desired shortcut. When you click, the registered shortcut appears in the center of the screen, and you can remove it by clicking on the registered shortcut. 293 | Click the "update" button to complete the registration. 294 | In the "civitai shortcut" -> "information" tab, a "model classification" item has been added on the right side, and you can perform registration and deletion of shortcuts for the model corresponding to the desired classification item. 295 | After modification, click the "update" button to complete the task. 296 | * In the browsing "search" feature, you can check the items registered in the classification. 297 | When you select a classification item from the dropdown list, the selected item appears in the list and works in conjunction with the "filter model type" and "search" features. 298 | The "search" feature works by entering items such as tags, classification, and search keywords. 299 | The tags, classification, and search keywords are applied with "and" operation, and each item is applied with "or" operation. Each item is separated by ",". 300 | Although only one item can be selected from the classification dropdown list, you can enter multiple items by using the "@" prefix. 301 | 302 | v 1.2a 303 | 304 | * Saved Model Information tab 과 중복되는 기능을 가진 Downloaded Model tab이 제거되었습니다. 305 | * 이미지 생성정보 적용방식이 내부적으로 변경되었습니다. 306 | 이미지외에 civitai 의 information 정보에서도 가져오도록 수정되었습니다. 307 | 이에따라 내부적으로 이미지 저장명의 변경이 있었습니다. "Update Shortcut's Model Information" 으로 이미지 업데이트가 필요합니다. 308 | 309 | v 1.2 310 | 311 | * Civitai User Gallery 탭이 추가 되었습니다. 312 | * 모델의 Gallery 의 정보를 확인 할수 있습니다. 해당모델 갤러리에 이미지가 없으면 아무것도 나타나지 않을수 있습니다. 313 | 또한 api로 제공되는 데이터와 시간차가 있기도 하더군요. 314 | 어떤 기능을 넣으면 좋을지 아이디어를 구하고 있습니다. 315 | * 좌측에 Upload 하단에 "Update Downloaded Model Information" 버튼이 추가 되었습니다. 316 | Stable Diffusion 작동중에 임의로 폴더명을 바꾸거나 했을때 내부 정보를 갱신 시켜줍니다. 317 | * 다운로드 부분에 더이상 Additional networks 로 선택해서 다운 로드 하는 항목을 제거 하였습니다. 318 | 효용성도 떨어지고 Settings->Additional Networks 원하시는 폴더를 지정하면 되기때문에 필요없다 생각했습니다. 319 | 저는 models\Lora 입력하여 같이 사용중에 있습니다. 320 | * 모델을 개별폴더에 다운 받을 때 폴더명을 직접 지정할수 있습니다. 321 | 기존 적으로는 "모델명-버전명" 형시으로 보여지며 원하는 값으로 입력해주시면 됩니다. 322 | 공백으로 하면 "모델명-버전명"으로 만들어 집니다. 323 | 제시되는 폴더명은 다르게 나타날수도 있는데, 324 | 같은 버전의 모델을 다운 받은 폴더가 해당 모델 폴더 하위에 있으면 그 이름이 나타납니다. 325 | * 그 외에 소소하게 디자인 이 변경되었습니다. 326 | * 버그 :많은 버그가 있지만 ; , 갤러리의 이미지를 full size로 볼때 information 쪽과 Browsing 쪽의 이미지와 제어가 섞입니다. 327 | 328 | v 1.1c 329 | 330 | * information 로딩에 병목현상을 일부 완화 331 | * Search 에 #tags 검색 기능 추가 332 | "," 구분되며 search keywords 끼리는 or, tags 끼리도 or , search keyword와 tags는 and 이 적용됩니다. 333 | * Tags검색 기능 추가를 위해 shortcut 저장 테이블이 변경되었습니다. 334 | 기존의 shortcut은 tag 검색을 위해 Update Shortcut's Model Information 이 필요합니다. 335 | 336 | v 1.1 337 | 338 | * Shortcut 등록 시 모델정보와 이미지가 별도의 폴더에 저장됩니다. 339 | * 이를 통해 사용자는 Civitai 사이트에 연결되지 않은 경우에도Saved Model Information 탭에서 모델 정보에 액세스할 수 있습니다. 340 | * 모델 정보와 이미지를 최신 상태로 유지하기 위해 "Thumbnail Update" 버튼이 제거되고 "Update Shortcut's Model Information" 버튼으로 대체됩니다. 341 | * 섬네일 업데이트 버튼을 삭제하고 shortcut 의 모델 정보와 이미지를 최신 정보로 유지하기위한 Update Shortcut's Model Information 버튼을 추가 342 | * 라이브로 접속 가능한 "Civitai Model Information" 탭에서 Download images Only 버튼이 제거되고, Delete shortcut 버튼이 Saved Model Information 탭으로 이동됩니다. 343 | * Delete shortcut는 sc_infos에 저장된 모델 정보와 이미지를 한 번에 제거합니다. 344 | *전체 모델 정보를 업데이트하는 "Update Shortcut's Model Information" 버튼 외에 개별적으로 모델 정보를 업데이트할 수 있는 "Update Model Information" 버튼이 "Saved Model Information" 탭에 추가됩니다. 345 | 346 | # Screenshot 347 | 348 | ![screenshot 2023-09-15 203613](https://github.com/sunnyark/civitai-shortcut/assets/40237431/fdac59c0-0ced-41fb-8faa-83029b3ffc3f) 349 | ![screenshot 2023-09-15 202037](https://github.com/sunnyark/civitai-shortcut/assets/40237431/67e2e7c5-0cd6-4917-a4c8-b9ffb45832f9) 350 | ![screenshot 2023-09-15 202018](https://github.com/sunnyark/civitai-shortcut/assets/40237431/7f200d24-a4ca-4e23-834a-71470590ee49) 351 | ![screenshot 2023-09-15 202004](https://github.com/sunnyark/civitai-shortcut/assets/40237431/9003d94d-5a13-4613-9fa6-722b1e892874) 352 | ![screenshot 2023-09-15 201956](https://github.com/sunnyark/civitai-shortcut/assets/40237431/94b2b2a1-f148-42dc-b6a8-21c1381dc55f) 353 | ![screenshot 2023-09-15 201933](https://github.com/sunnyark/civitai-shortcut/assets/40237431/7881d3d8-d2a3-4502-b39c-fb40a17cf21c) 354 | ![screenshot 2023-09-15 201909](https://github.com/sunnyark/civitai-shortcut/assets/40237431/d3a300da-bccf-4452-ad59-11904c44f585) 355 | ![screenshot 2023-09-15 201853](https://github.com/sunnyark/civitai-shortcut/assets/40237431/ecf6e1a7-59f8-4eb5-a58f-5a7ff7824437) 356 | ![screenshot 2023-09-15 201845](https://github.com/sunnyark/civitai-shortcut/assets/40237431/c1726ab5-15b7-499f-9067-9755fba332c0) 357 | ![screenshot 2023-09-15 201833](https://github.com/sunnyark/civitai-shortcut/assets/40237431/773dc92f-3fd5-4509-94bb-99a9e50bec34) 358 | ![screenshot 2023-09-15 201815](https://github.com/sunnyark/civitai-shortcut/assets/40237431/d3d61c0a-c749-40ee-bc8c-69c35e9c6ba7) 359 | ![screenshot 2023-09-15 201738](https://github.com/sunnyark/civitai-shortcut/assets/40237431/b8673673-ea42-43ad-942f-15437fce6b26) 360 | ![screenshot 2023-09-15 201708](https://github.com/sunnyark/civitai-shortcut/assets/40237431/9f8f7ab3-245c-4878-b722-5cb7de2f1c9d) 361 | ![register_model_shortcut](https://github.com/sunnyark/civitai-shortcut/assets/40237431/a18cc188-0d7a-4860-91fa-b9b2b27f4bdc) 362 | ![register_model_direct](https://github.com/sunnyark/civitai-shortcut/assets/40237431/c6db4ced-9cec-4488-ac3f-9a17fadb42b8) 363 | -------------------------------------------------------------------------------- /img/card-no-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyark/civitai-shortcut/592be0a03f43bc2cdebada22ba68a12057ea3787/img/card-no-preview.png -------------------------------------------------------------------------------- /img/nsfw-no-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyark/civitai-shortcut/592be0a03f43bc2cdebada22ba68a12057ea3787/img/nsfw-no-preview.png -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyark/civitai-shortcut/592be0a03f43bc2cdebada22ba68a12057ea3787/scripts/civitai_manager_libs/__init__.py -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/civitai.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import json 4 | import requests 5 | from . import util 6 | from . import setting 7 | 8 | # Set the URL for the API endpoint 9 | 10 | url_dict = { 11 | "modelPage":"https://civitai.com/models/", 12 | "modelId": "https://civitai.com/api/v1/models/", 13 | "modelVersionId": "https://civitai.com/api/v1/model-versions/", 14 | "modelHash": "https://civitai.com/api/v1/model-versions/by-hash/", 15 | "imagePage" : "https://civitai.com/api/v1/images" 16 | } 17 | 18 | def Url_Page(): 19 | return url_dict["modelPage"] 20 | 21 | def Url_ModelId(): 22 | return url_dict["modelId"] 23 | 24 | def Url_VersionId(): 25 | return url_dict["modelVersionId"] 26 | 27 | def Url_Hash(): 28 | return url_dict["modelHash"] 29 | 30 | def Url_ImagePage(): 31 | return url_dict["imagePage"] 32 | 33 | def request_models(api_url=None): 34 | try: 35 | # Make a GET request to the API 36 | with requests.get(api_url) as response: 37 | # Check the status code of the response 38 | if response.status_code != 200: 39 | util.printD("Request failed with status code: {}".format(response.status_code)) 40 | return 41 | data = json.loads(response.text) 42 | except Exception as e: 43 | return 44 | return data 45 | 46 | def get_model_info(id:str) -> dict: 47 | if not id: 48 | return 49 | 50 | content = None 51 | try: 52 | with requests.get(Url_ModelId()+str(id)) as response: 53 | content = response.json() 54 | 55 | if 'id' not in content.keys(): 56 | return None 57 | 58 | except Exception as e: 59 | return None 60 | 61 | return content 62 | 63 | # def get_model_info_by_version_id(version_id:str) -> dict: 64 | # if not version_id: 65 | # return 66 | 67 | # version_info = get_version_info_by_version_id(version_id) 68 | # return get_model_info_by_version_info(version_info) 69 | 70 | # def get_model_info_by_version_info(version_info) -> dict: 71 | # if not version_info: 72 | # return 73 | # return get_model_info(version_info['modelId']) 74 | 75 | def get_version_info_by_hash(hash) -> dict: 76 | if not hash: 77 | return 78 | 79 | content = None 80 | 81 | try: 82 | with requests.get(f"{Url_Hash()}{hash}") as response: 83 | content = response.json() 84 | 85 | if 'id' not in content.keys(): 86 | return None 87 | 88 | except Exception as e: 89 | return None 90 | 91 | return content 92 | 93 | def get_version_info_by_version_id(version_id:str) -> dict: 94 | if not version_id: 95 | return 96 | 97 | content = None 98 | 99 | try: 100 | with requests.get(Url_VersionId()+str(version_id)) as response: 101 | content = response.json() 102 | 103 | if 'id' not in content.keys(): 104 | return None 105 | 106 | except Exception as e: 107 | return None 108 | 109 | return content 110 | 111 | def get_latest_version_info_by_model_id(id:str) -> dict: 112 | 113 | model_info = get_model_info(id) 114 | if not model_info: 115 | return 116 | 117 | if "modelVersions" not in model_info.keys(): 118 | return 119 | 120 | def_version = model_info["modelVersions"][0] 121 | if not def_version: 122 | return 123 | 124 | if "id" not in def_version.keys(): 125 | return 126 | 127 | version_id = def_version["id"] 128 | 129 | # 모델에서 얻는 버전 인포는 모델 정보가 없으므로 새로 받아오자 130 | version_info = get_version_info_by_version_id(str(version_id)) 131 | 132 | return version_info 133 | 134 | def get_version_id_by_version_name(model_id:str,name:str)->str: 135 | version_id = None 136 | if not model_id: 137 | return 138 | 139 | model_info = get_model_info(model_id) 140 | if not model_info: 141 | return 142 | 143 | if "modelVersions" not in model_info.keys(): 144 | return 145 | 146 | version_id = None 147 | 148 | for version in model_info['modelVersions']: 149 | if version['name'] == name: 150 | version_id = version['id'] 151 | break 152 | 153 | return version_id 154 | 155 | def get_files_by_version_info(version_info:dict)->dict: 156 | download_files = {} 157 | 158 | if not version_info: 159 | return 160 | 161 | for file in version_info['files']: 162 | download_files[str(file['id'])] = file 163 | 164 | return download_files 165 | 166 | def get_files_by_version_id(version_id=None)->dict: 167 | if not version_id: 168 | return 169 | 170 | version_info = get_version_info_by_version_id(version_id) 171 | 172 | return get_files_by_version_info(version_info) 173 | 174 | def get_primary_file_by_version_info(version_info:dict)->dict: 175 | 176 | if not version_info: 177 | return 178 | 179 | for file in version_info['files']: 180 | if 'primary' in file.keys(): 181 | if file['primary']: 182 | return file 183 | return 184 | 185 | def get_primary_file_by_version_id(version_id=None)->dict: 186 | if not version_id: 187 | return 188 | 189 | version_info = get_version_info_by_version_id(version_id) 190 | 191 | return get_primary_file_by_version_info(version_info) 192 | 193 | def get_images_by_version_id(version_id=None)->dict: 194 | if not version_id: 195 | return 196 | 197 | version_info = get_version_info_by_version_id(version_id) 198 | 199 | return get_images_by_version_info(version_info) 200 | 201 | 202 | def get_images_by_version_info(version_info:dict)->dict: 203 | if not version_info: 204 | return 205 | 206 | return version_info["images"] 207 | 208 | 209 | def get_triger_by_version_info(version_info:dict)->str: 210 | if not version_info: 211 | return 212 | try: 213 | triger_words = ", ".join(version_info['trainedWords']) 214 | if len(triger_words.strip()) > 0: 215 | return triger_words 216 | except: 217 | pass 218 | 219 | return 220 | 221 | def get_triger_by_version_id(version_id=None)->str: 222 | if not version_id: 223 | return 224 | 225 | version_info = get_version_info_by_version_id(version_id) 226 | 227 | return get_triger_by_version_info(version_info) 228 | 229 | def write_model_info(file, model_info:dict)->str: 230 | if not model_info: 231 | return False 232 | 233 | try: 234 | with open(file, 'w') as f: 235 | f.write(json.dumps(model_info, indent=4)) 236 | except Exception as e: 237 | return False 238 | 239 | return True 240 | 241 | def write_version_info(file, version_info:dict): 242 | if not version_info: 243 | return False 244 | 245 | try: 246 | with open(file, 'w') as f: 247 | f.write(json.dumps(version_info, indent=4)) 248 | except Exception as e: 249 | return False 250 | 251 | return True 252 | 253 | def write_triger_words_by_version_id(file, version_id:str): 254 | if not version_id: 255 | return False 256 | 257 | version_info = get_version_info_by_version_id(version_id) 258 | 259 | return write_triger_words(file,version_info) 260 | 261 | def write_triger_words(file, version_info:dict): 262 | if not version_info: 263 | return False 264 | 265 | triger_words = get_triger_by_version_info(version_info) 266 | 267 | if not triger_words: 268 | return False 269 | 270 | try: 271 | with open(file, 'w') as f: 272 | f.write(triger_words) 273 | except Exception as e: 274 | return False 275 | 276 | return True 277 | 278 | def write_LoRa_metadata_by_version_id(file, version_id:str): 279 | if not version_id: 280 | return False 281 | 282 | version_info = get_version_info_by_version_id(version_id) 283 | 284 | return write_LoRa_metadata(file,version_info) 285 | 286 | def write_LoRa_metadata(filepath, version_info): 287 | 288 | LoRa_metadata = { 289 | "description": None, 290 | "sd version": None, 291 | "activation text": None, 292 | "preferred weight": 0, 293 | "notes": None 294 | } 295 | 296 | if not version_info: 297 | return False 298 | 299 | if os.path.isfile(filepath): 300 | return False 301 | 302 | if "description" in version_info.keys(): 303 | LoRa_metadata['description'] = version_info["description"] 304 | 305 | if "baseModel" in version_info.keys(): 306 | baseModel = version_info["baseModel"] 307 | if baseModel in setting.model_basemodels.keys(): 308 | LoRa_metadata['sd version'] = setting.model_basemodels[baseModel] 309 | else: 310 | LoRa_metadata['sd version'] = 'Unknown' 311 | 312 | if "trainedWords" in version_info.keys(): 313 | LoRa_metadata['activation text'] = ", ".join(version_info['trainedWords']) 314 | 315 | notes = list() 316 | if "modelId" in version_info.keys(): 317 | notes.append(f"{url_dict['modelPage']}{version_info['modelId']}") 318 | 319 | if "downloadUrl" in version_info.keys(): 320 | notes.append(version_info['downloadUrl']) 321 | 322 | if len(notes) > 0: 323 | LoRa_metadata['notes'] = ", ".join(notes) 324 | 325 | try: 326 | with open(filepath, 'w') as f: 327 | json.dump(LoRa_metadata, f, indent=4) 328 | except Exception as e: 329 | return False 330 | 331 | return True 332 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/civitai_shortcut_action.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import datetime 3 | import os 4 | 5 | from . import model 6 | from . import civitai 7 | from . import setting 8 | from . import sc_browser_page 9 | 10 | from . import util 11 | from . import ishortcut 12 | from . import model_action 13 | from . import ishortcut_action 14 | from . import civitai_gallery_action 15 | 16 | def on_shortcut_input_change(shortcut_input): 17 | if not shortcut_input: 18 | return gr.update(visible=False), gr.update(selected=None), gr.update(visible=False) 19 | return shortcut_input, gr.update(selected="Shortcut"), None 20 | 21 | def on_ui(recipe_input, shortcut_input, civitai_tabs): 22 | with gr.Row(visible=False): 23 | 24 | sc_modelid = gr.Textbox() 25 | update_informations = gr.Textbox() 26 | current_information_tabs = gr.State(0) 27 | refresh_NSFW = gr.Textbox() 28 | 29 | with gr.Column(scale=setting.shortcut_browser_screen_split_ratio): 30 | with gr.Tabs() as civitai_shortcut_tabs: 31 | with gr.TabItem("Register Model"): 32 | with gr.Row(visible=False): 33 | register_information_only = gr.Checkbox(label="Register only model information", value=False) 34 | with gr.Row(): 35 | with gr.Column(): 36 | gr.Markdown(value="Using the model URL from the Civitai site, you register the information of the model. You can click and drag the Civitai Model's URL or drag and drop a saved internet shortcut. Additionally, you can select multiple internet shortcuts and drop them all at once.", visible=True) 37 | civitai_internet_url_txt = gr.Textbox(placeholder="Copy & Paste or Drag & Drop Civitai Model Url", show_label=False, interactive=True) 38 | civitai_internet_url = gr.File(label="Civitai Internet Shortcut", file_count="multiple", file_types=[".url"]) 39 | # update_modelfolder_btn = gr.Button(value="Update Downloaded Model Information", variant="primary") 40 | # gr.Markdown(value="If you have made direct modifications(e.g. moving or renaming a folder) to the downloaded model during runtime, please execute the \"Update Downloaded Model Information\" function, which rescans the downloaded model and updates its information accordingly. ", visible=True) 41 | 42 | with gr.TabItem("Model Browser"): 43 | with gr.Row(): 44 | with gr.Column(): 45 | sc_gallery, refresh_sc_browser, refresh_sc_gallery = sc_browser_page.on_ui() 46 | 47 | with gr.TabItem("Scan New Version"): 48 | with gr.Row(): 49 | with gr.Column(): 50 | shortcut_new_version_type = gr.Dropdown(label='Filter Model type', multiselect=True, choices=[k for k in setting.ui_typenames], interactive=True) 51 | scan_new_version_btn = gr.Button(value="Scan new version model", variant="primary") 52 | sc_new_version_gallery = gr.Gallery(label="SC New Version Gallery", elem_id="sc_new_version_gallery", show_label=False, columns=setting.shortcut_column, height="fit", object_fit=setting.gallery_thumbnail_image_style) 53 | gr.Markdown(value="The feature is to search for new versions of models on Civitai among the downloaded ones.", visible=True) 54 | with gr.TabItem("NSFW Filter"): 55 | with gr.Row(): 56 | with gr.Column(): 57 | nsfw_filter_enable = gr.Dropdown(value='On', choices=['On','Off'], label='NSFW Filtering', interactive=True) 58 | nsfw_level = gr.Dropdown(value=setting.NSFW_level_user, choices=setting.NSFW_levels, label='NSFW Filtering Level', visible=True, interactive=True) 59 | nsfw_save_btn = gr.Button(value="Save NSFW Setting", variant="primary", visible=True) 60 | 61 | with gr.Column(scale=(setting.shortcut_browser_screen_split_ratio_max-setting.shortcut_browser_screen_split_ratio)): 62 | with gr.Tabs() as civitai_information_tabs: 63 | with gr.TabItem("Model Information" , id="civitai_info"): 64 | with gr.Row(): 65 | shortcut_modelid, refresh_civitai_information = ishortcut_action.on_ui(refresh_sc_browser, recipe_input) 66 | 67 | with gr.TabItem("Civitai User Gallery" , id="gallery_info"): 68 | with gr.Row(): 69 | gallery_modelid, refresh_gallery_information = civitai_gallery_action.on_ui(recipe_input) 70 | 71 | with gr.TabItem("Downloaded Model Information" , id="download_info"): 72 | with gr.Row(): 73 | downloadinfo_modelid , refresh_download_information = model_action.on_ui() 74 | 75 | # NSFW Filter Setting Refresh 76 | refresh_NSFW.change( 77 | fn=on_refresh_NSFW_change, 78 | inputs=None, 79 | outputs=[ 80 | nsfw_filter_enable, 81 | nsfw_level 82 | ] 83 | ) 84 | 85 | nsfw_filter_enable.select( 86 | fn=on_nsfw_filter, 87 | inputs=[ 88 | nsfw_filter_enable, 89 | nsfw_level 90 | ], 91 | outputs=[ 92 | nsfw_level, 93 | refresh_civitai_information, 94 | refresh_gallery_information 95 | ] 96 | ) 97 | 98 | nsfw_level.select( 99 | fn=on_nsfw_filter, 100 | inputs=[ 101 | nsfw_filter_enable, 102 | nsfw_level 103 | ], 104 | outputs=[ 105 | nsfw_level, 106 | refresh_civitai_information, 107 | refresh_gallery_information 108 | ] 109 | ) 110 | 111 | nsfw_save_btn.click(fn=on_nsfw_save_btn_click) 112 | 113 | # shortcut information 에서 넘어올때는 새로운 레시피를 만든다. 114 | shortcut_input.change( 115 | fn=on_shortcut_input_change, 116 | inputs=[ 117 | shortcut_input 118 | ], 119 | outputs=[ 120 | sc_modelid, 121 | civitai_tabs, 122 | shortcut_input 123 | ], show_progress=False 124 | ) 125 | 126 | scan_new_version_btn.click(on_scan_new_version_btn,shortcut_new_version_type,sc_new_version_gallery) 127 | sc_gallery.select(on_sc_gallery_select, None, [sc_modelid], show_progress=False) 128 | sc_new_version_gallery.select(on_sc_gallery_select, None, [sc_modelid], show_progress=False) 129 | # update_modelfolder_btn.click(on_update_modelfolder_btn_click,None,refresh_sc_browser) 130 | civitai_shortcut_tabs.select(on_civitai_shortcut_tabs_select,None,[refresh_sc_browser,refresh_NSFW],show_progress=False) 131 | 132 | update_informations.change( 133 | fn=on_sc_modelid_change, 134 | inputs=[ 135 | sc_modelid, 136 | current_information_tabs 137 | ], 138 | outputs=[ 139 | shortcut_modelid, 140 | gallery_modelid, 141 | downloadinfo_modelid 142 | ] 143 | ) 144 | 145 | sc_modelid.change( 146 | fn=on_sc_modelid_change, 147 | inputs=[ 148 | sc_modelid, 149 | current_information_tabs 150 | ], 151 | outputs=[ 152 | shortcut_modelid, 153 | gallery_modelid, 154 | downloadinfo_modelid 155 | ] 156 | ) 157 | 158 | civitai_information_tabs.select( 159 | fn=on_civitai_information_tabs_select, 160 | inputs=None, 161 | outputs=[ 162 | current_information_tabs, 163 | update_informations 164 | ] 165 | ) 166 | 167 | civitai_internet_url.upload( 168 | fn=on_civitai_internet_url_upload, 169 | inputs=[ 170 | civitai_internet_url, 171 | register_information_only, 172 | ], 173 | outputs=[ 174 | sc_modelid, 175 | refresh_sc_browser, 176 | civitai_internet_url 177 | ] 178 | ) 179 | 180 | civitai_internet_url_txt.change( 181 | fn=on_civitai_internet_url_txt_upload, 182 | inputs=[ 183 | civitai_internet_url_txt, 184 | register_information_only, 185 | ], 186 | outputs=[ 187 | sc_modelid, 188 | refresh_sc_browser, 189 | civitai_internet_url_txt 190 | ] 191 | ) 192 | 193 | return refresh_sc_browser, refresh_civitai_information 194 | 195 | def on_refresh_NSFW_change(): 196 | if setting.NSFW_filtering_enable: 197 | return gr.update(value="On") , gr.update(visible=True, value=setting.NSFW_level_user) 198 | else: 199 | return gr.update(value="Off") , gr.update(visible=False, value=setting.NSFW_level_user) 200 | 201 | def on_nsfw_filter(enable, level): 202 | current_time = datetime.datetime.now() 203 | setting.set_NSFW(True if enable == "On" else False , level) 204 | 205 | return gr.update(visible=True if enable == "On" else False, value=level), current_time, current_time 206 | 207 | def on_nsfw_save_btn_click(): 208 | setting.save_NSFW() 209 | 210 | def on_civitai_shortcut_tabs_select(evt: gr.SelectData): 211 | if evt.index == 1: 212 | current_time = datetime.datetime.now() 213 | return current_time,gr.update(visible=False) 214 | elif evt.index == 3: 215 | current_time = datetime.datetime.now() 216 | return gr.update(visible=False), current_time 217 | return gr.update(visible=False),gr.update(visible=False) 218 | 219 | def on_civitai_information_tabs_select(evt: gr.SelectData): 220 | current_time = datetime.datetime.now() 221 | return evt.index, current_time 222 | 223 | ##### sc_gallery 함수 정의 ##### 224 | def on_sc_gallery_select(evt : gr.SelectData): 225 | 226 | if evt.value: 227 | shortcut = evt.value 228 | sc_model_id = setting.get_modelid_from_shortcutname(shortcut) #shortcut[0:shortcut.find(':')] 229 | 230 | # 최신버전이 있으면 업데이트한다. 백그라운드에서 수행되므로 다음에 번에 반영된다. 231 | # update_shortcut_thread(sc_model_id) 232 | return sc_model_id 233 | 234 | def on_sc_modelid_change(sc_model_id, current_information_tabs): 235 | 236 | if current_information_tabs == setting.civitai_information_tab: 237 | return sc_model_id, gr.update(visible=False), gr.update(visible=False) 238 | 239 | if current_information_tabs == setting.usergal_information_tab: 240 | return gr.update(visible=False), sc_model_id, gr.update(visible=False) 241 | 242 | if current_information_tabs == setting.download_information_tab: 243 | return gr.update(visible=False), gr.update(visible=False), sc_model_id 244 | 245 | return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) 246 | 247 | def on_civitai_internet_url_upload(files, register_information_only, progress=gr.Progress()): 248 | model_id = None 249 | if files: 250 | modelids = ishortcut_action.upload_shortcut_by_files(files, register_information_only, progress) 251 | if len(modelids) > 0: 252 | model_id = modelids[0] 253 | 254 | current_time = datetime.datetime.now() 255 | 256 | if not model_id: 257 | return gr.update(visible=False), gr.update(visible=False), None 258 | return model_id, current_time, None 259 | 260 | def on_civitai_internet_url_txt_upload(url, register_information_only, progress=gr.Progress()): 261 | model_id = None 262 | if url: 263 | if len(url.strip()) > 0: 264 | modelids = ishortcut_action.upload_shortcut_by_urls([url], register_information_only, progress) 265 | if len(modelids) > 0: 266 | model_id = modelids[0] 267 | 268 | current_time = datetime.datetime.now() 269 | 270 | if not model_id: 271 | return gr.update(visible=False), gr.update(visible=False), None 272 | return model_id, current_time, None 273 | 274 | return gr.update(visible=False), None, gr.update(visible=True) 275 | 276 | def on_update_modelfolder_btn_click(): 277 | model.update_downloaded_model() 278 | current_time = datetime.datetime.now() 279 | return current_time 280 | 281 | # 새 버전이 있는지 스캔한다 282 | def on_scan_new_version_btn(sc_types, progress=gr.Progress()): 283 | model.update_downloaded_model() 284 | 285 | result = None 286 | scan_list = None 287 | shortlist = get_shortcut_list(sc_types,True) 288 | 289 | if shortlist: 290 | for short in progress.tqdm(shortlist, desc="Scanning new version model"): 291 | if not is_latest(str(short['id'])): 292 | if not scan_list: 293 | scan_list = list() 294 | scan_list.append(short) 295 | 296 | # util.printD(scan_list) 297 | if scan_list: 298 | result = list() 299 | for v in scan_list: 300 | if v: 301 | if ishortcut.is_sc_image(v['id']): 302 | result.append((os.path.join(setting.shortcut_thumbnail_folder,f"{v['id']}{setting.preview_image_ext}"),setting.set_shortcutname(v['name'],v['id']))) 303 | else: 304 | result.append((setting.no_card_preview_image,setting.set_shortcutname(v['name'],v['id']))) 305 | 306 | return gr.update(value=result) 307 | 308 | def get_shortcut_list(shortcut_types=None, downloaded_sc=False): 309 | 310 | shortcut_list = ishortcut.get_image_list(shortcut_types, None, None, None) 311 | 312 | if not shortcut_list: 313 | return None 314 | 315 | if downloaded_sc: 316 | if model.Downloaded_Models: 317 | downloaded_list = list() 318 | for short in shortcut_list: 319 | mid = short['id'] 320 | if str(mid) in model.Downloaded_Models.keys(): 321 | downloaded_list.append(short) 322 | shortcut_list = downloaded_list 323 | else: 324 | shortcut_list = None 325 | 326 | return shortcut_list 327 | 328 | def is_latest(modelid:str)->bool: 329 | if not modelid: 330 | return False 331 | 332 | if str(modelid) in model.Downloaded_Models.keys(): 333 | # civitai 에서 최신 모델 정보를 가져온다. 334 | version_info = civitai.get_latest_version_info_by_model_id(str(modelid)) 335 | if version_info: 336 | latest_versionid = str(version_info['id']).strip() 337 | # util.printD(latest_versionid) 338 | # 현재 가지고 있는 버전들을 가져온다. 339 | dnver_list = list() 340 | for vid, version_paths in model.Downloaded_Models[str(modelid)]: 341 | dnver_list.append(str(vid).strip()) 342 | 343 | # util.printD(dnver_list) 344 | if latest_versionid in dnver_list: 345 | # util.printD("True") 346 | return True 347 | return False 348 | 349 | # def update_shortcut_information(modelid): 350 | # if not modelid: 351 | # return 352 | # try: 353 | # if setting.shortcut_auto_update: 354 | # ishortcut.write_model_information(modelid, False, None) 355 | # except: 356 | # return 357 | # return 358 | 359 | # def update_shortcut_thread(modelid): 360 | # try: 361 | # thread = threading.Thread(target=update_shortcut_information, args=(modelid,)) 362 | # thread.start() 363 | # except Exception as e: 364 | # util.printD(e) 365 | # pass -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/classification.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from . import util 5 | from . import setting 6 | #============================================================ 7 | #=======================wrap================================= 8 | def get_classification_names_by_modelid(modelid): 9 | c_name_list = list() 10 | if not modelid: 11 | return 12 | 13 | CISC = load() 14 | if CISC: 15 | c_name_list = [k for k , v in CISC.items() if str(modelid) in v['shortcuts']] 16 | 17 | return c_name_list 18 | 19 | def clean_classification_shortcut(modelid): 20 | CISC = load() 21 | if CISC: 22 | for cis in CISC.values(): 23 | if str(modelid) in cis['shortcuts']: 24 | cis['shortcuts'].remove(str(modelid)) 25 | 26 | save(CISC) 27 | return True 28 | return False 29 | 30 | def add_classification_shortcut(name, modelid): 31 | if name and len(name.strip()) > 0: 32 | CISC = load() 33 | if CISC: 34 | if name in CISC: 35 | CISC[name]['shortcuts'].append(str(modelid)) 36 | save(CISC) 37 | return True 38 | return False 39 | 40 | def update_classification_shortcut(s_name, shortcuts): 41 | if not s_name: 42 | return 43 | 44 | CISC = load() 45 | CISC = update_shortcut(CISC,s_name, shortcuts) 46 | 47 | save(CISC) 48 | 49 | return True 50 | 51 | def update_classification(s_name, name, info): 52 | if not s_name: 53 | return 54 | 55 | if not name: 56 | return 57 | 58 | name = name.strip() 59 | 60 | CISC = load() 61 | CISC = update(CISC, s_name, name, info) 62 | 63 | save(CISC) 64 | 65 | if CISC: 66 | if name in CISC: 67 | return True 68 | 69 | return False 70 | 71 | def get_classification_shortcuts(s_name): 72 | if not s_name: 73 | return None 74 | 75 | CISC = load() 76 | if s_name in CISC: 77 | return CISC[s_name]['shortcuts'] 78 | 79 | return None 80 | 81 | def create_classification(name, info): 82 | if name and len(name.strip()) > 0: 83 | CISC = load() 84 | 85 | if CISC: 86 | if name in CISC: 87 | return False 88 | 89 | CISC = create(CISC, name.strip(), info) 90 | save(CISC) 91 | 92 | if CISC: 93 | if name in CISC: 94 | return True 95 | return False 96 | 97 | def delete_classification(s_name): 98 | if not s_name: 99 | return 100 | 101 | CISC = load() 102 | CISC = delete(CISC,s_name) 103 | save(CISC) 104 | 105 | 106 | def get_classification(s_name): 107 | if not s_name: 108 | return None 109 | 110 | CISC = load() 111 | if s_name in CISC: 112 | return CISC[s_name] 113 | 114 | return None 115 | 116 | def get_classification_info(s_name): 117 | if not s_name: 118 | return None 119 | 120 | CISC = load() 121 | if s_name in CISC: 122 | return CISC[s_name]['info'] 123 | 124 | return None 125 | 126 | def get_list(): 127 | 128 | CISC = load() 129 | 130 | tmp_cisc_name = [] 131 | if CISC: 132 | tmp_cisc_name = [k for k in CISC.keys()] 133 | 134 | return tmp_cisc_name 135 | #========================================================= 136 | 137 | 138 | 139 | #========================================================= 140 | #================= raw =================================== 141 | 142 | def get_shortcut_list(CISC:dict, classification): 143 | if not CISC: 144 | return None 145 | 146 | if not classification: 147 | return None 148 | 149 | classification = classification.strip() 150 | 151 | if classification not in CISC: 152 | return None 153 | 154 | return CISC[classification]['shortcuts'] 155 | 156 | def update_shortcut(CISC:dict, classification, modelids): 157 | 158 | if not CISC: 159 | CISC = dict() 160 | 161 | if not classification: 162 | return CISC 163 | 164 | classification = classification.strip() 165 | 166 | if classification not in CISC: 167 | CISC = create(CISC,classification,None) 168 | 169 | if not modelids: 170 | CISC[classification]['shortcuts'] = list() 171 | else: 172 | CISC[classification]['shortcuts'] = modelids 173 | 174 | return CISC 175 | 176 | def remove_shortcut(CISC:dict, classification, modelid): 177 | 178 | if not CISC: 179 | return CISC 180 | 181 | if not classification: 182 | return CISC 183 | 184 | if not modelid: 185 | return CISC 186 | 187 | if classification not in CISC: 188 | return CISC 189 | 190 | classification = classification.strip() 191 | 192 | if str(modelid) in CISC[classification]['shortcuts']: 193 | CISC[classification]['shortcuts'].remove(str(modelid)) 194 | 195 | return CISC 196 | 197 | def clear_shortcut(CISC:dict, classification): 198 | 199 | if not CISC: 200 | return CISC 201 | 202 | if not classification: 203 | return CISC 204 | 205 | if classification not in CISC: 206 | return CISC 207 | 208 | classification = classification.strip() 209 | 210 | CISC[classification]['shortcuts'].clear() 211 | 212 | return CISC 213 | 214 | def create(CISC:dict, classification, info=None)->dict: 215 | 216 | if not classification: 217 | return CISC 218 | 219 | if not CISC: 220 | CISC = dict() 221 | 222 | classification = classification.strip() 223 | if len(classification) > 0: 224 | if classification not in CISC: 225 | CISC[classification] = { 226 | "info":info , 227 | "shortcuts":[] 228 | } 229 | 230 | return CISC 231 | 232 | def delete(CISC:dict, classification)->dict: 233 | if not classification: 234 | return CISC 235 | 236 | if not CISC: 237 | return CISC 238 | 239 | sc = CISC.pop(classification,None) 240 | 241 | return CISC 242 | 243 | def update(CISC:dict, classification, name, info): 244 | 245 | if not CISC: 246 | return CISC 247 | 248 | if not classification: 249 | return CISC 250 | 251 | if classification not in CISC: 252 | return CISC 253 | 254 | if not name: 255 | return CISC 256 | 257 | name = name.strip() 258 | 259 | if classification == name: 260 | CISC[classification]['info'] = info 261 | else: 262 | if name not in CISC: 263 | sc = CISC.pop(classification,None) 264 | sc['info'] = info 265 | CISC[name] = sc 266 | 267 | return CISC 268 | 269 | def save(CISC:dict): 270 | output = "" 271 | 272 | #write to file 273 | try: 274 | with open(setting.shortcut_classification, 'w') as f: 275 | json.dump(CISC, f, indent=4) 276 | except Exception as e: 277 | util.printD("Error when writing file:"+setting.shortcut_classification) 278 | return output 279 | 280 | output = "Civitai Internet Shortcut Classification saved to: " + setting.shortcut_classification 281 | #util.printD(output) 282 | 283 | return output 284 | 285 | def load()->dict: 286 | if not os.path.isfile(setting.shortcut_classification): 287 | save({}) 288 | return 289 | 290 | json_data = None 291 | try: 292 | with open(setting.shortcut_classification, 'r') as f: 293 | json_data = json.load(f) 294 | except: 295 | return None 296 | 297 | # check error 298 | if not json_data: 299 | return None 300 | 301 | # check for new key 302 | return json_data 303 | #========================================================================= -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/downloader.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import time 4 | import requests 5 | import threading 6 | import shutil 7 | import json 8 | 9 | from . import util 10 | from . import setting 11 | from . import civitai 12 | 13 | from tqdm import tqdm 14 | 15 | def add_number_to_duplicate_files(filenames)->dict: 16 | counts = {} 17 | dup_file = {} 18 | 19 | for file in filenames: 20 | file_info = file.split(":", 1) 21 | if len(file_info) > 1: 22 | if file_info[1] in counts: 23 | name, ext = os.path.splitext(file_info[1]) 24 | counts[file_info[1]] += 1 25 | file_info[1] = f"{name} ({counts[file_info[1]]}){ext}" 26 | else: 27 | counts[file_info[1]] = 0 28 | dup_file[file_info[0]] = file_info[1] 29 | return dup_file 30 | 31 | def get_save_base_name(version_info): 32 | # 이미지 파일명도 primary 이름으로 저장한다. 33 | 34 | base = None 35 | primary_file = civitai.get_primary_file_by_version_info(version_info) 36 | if not primary_file: 37 | base = setting.generate_version_foldername(version_info['model']['name'],version_info['name'],version_info['id']) 38 | else: 39 | base, ext = os.path.splitext(primary_file['name']) 40 | return base 41 | 42 | def download_file_thread(file_name, version_id, ms_folder, vs_folder, vs_foldername, cs_foldername, ms_foldername): 43 | 44 | if not file_name or not version_id: 45 | return 46 | 47 | version_info = civitai.get_version_info_by_version_id(version_id) 48 | 49 | if not version_info: 50 | return 51 | 52 | download_files = civitai.get_files_by_version_info(version_info) 53 | 54 | if not download_files: 55 | return 56 | 57 | model_folder = util.make_download_model_folder(version_info, ms_folder, vs_folder, vs_foldername, cs_foldername, ms_foldername) 58 | 59 | if not model_folder: 60 | return 61 | 62 | savefile_base = None 63 | 64 | # version_info 에서 파일부분을 가져온다. 65 | # 파일명이 변경되었을때 정보를 수정한다. 66 | if "files" in version_info: 67 | info_files = version_info["files"] 68 | 69 | dup_names = add_number_to_duplicate_files(file_name) 70 | 71 | for fid, file in dup_names.items(): 72 | try: 73 | #모델 파일 저장 74 | path_dl_file = os.path.join(model_folder, file) 75 | thread = threading.Thread(target=download_file,args=(download_files[str(fid)]['downloadUrl'], path_dl_file)) 76 | thread.start() 77 | 78 | # 파일 아이디에 해당하는 파일명을 변경한다. 79 | # 실제 다운 로드 되는 파일명으로 변경한다. 80 | # 베이스 파일명도 얻어온다. 81 | if not info_files: 82 | continue 83 | 84 | for info_file in info_files: 85 | if str(info_file['id']) == str(fid): 86 | info_file['name'] = file 87 | 88 | if savefile_base: 89 | continue 90 | 91 | if 'primary' in info_file.keys(): 92 | if info_file['primary']: 93 | savefile_base , ext = os.path.splitext(file) 94 | 95 | except Exception as e: 96 | util.printD(e) 97 | # finally: 98 | # pass 99 | 100 | # savefile_base 이름이 없다는 것은 primary 파일이 아닌것이다. 101 | # 다운로드할 파일 목록중에 primary 파일이 있을때만 버전 인포파일과 프리뷰 이미지를 다운로드한다. 102 | # 프리뷰 파일이 아닐때는 단순히 파일만 다운로드한다. 103 | if savefile_base: 104 | path_file = os.path.join(model_folder, f"{util.replace_filename(savefile_base)}{setting.info_suffix}{setting.info_ext}") 105 | info_file = civitai.write_version_info(path_file, version_info) 106 | if info_file: 107 | util.printD(f"Wrote version info : {path_file}") 108 | 109 | path_img = os.path.join(model_folder, f"{util.replace_filename(savefile_base)}{setting.preview_image_suffix}{setting.preview_image_ext}") 110 | preview_file = download_preview_image(path_img, version_info) 111 | if preview_file: 112 | util.printD(f"Wrote preview image : {path_img}") 113 | 114 | # LoRa_metadata_file 을 생성한다. 115 | path_file = os.path.join(model_folder, f"{util.replace_filename(savefile_base)}.json") 116 | LoRa_metadata_file = civitai.write_LoRa_metadata(path_file, version_info) 117 | # LoRa_metadata_file = generate_LoRa_metadata(path_file, version_info) 118 | if LoRa_metadata_file: 119 | util.printD(f"Wrote LoRa metadata : {path_file}") 120 | 121 | # savefile_base 이름이 없다면 모델인포에서 프라이머리 파일을 찾는다. 122 | # if not savefile_base: 123 | # savefile_base = get_save_base_name(version_info) 124 | 125 | # path_file = os.path.join(model_folder, f"{util.replace_filename(savefile_base)}{setting.info_suffix}{setting.info_ext}") 126 | # info_file = civitai.write_version_info(path_file, version_info) 127 | # if info_file: 128 | # util.printD(f"Wrote version info : {path_file}") 129 | 130 | # path_img = os.path.join(model_folder, f"{util.replace_filename(savefile_base)}{setting.preview_image_suffix}{setting.preview_image_ext}") 131 | # preview_file = download_preview_image(path_img, version_info) 132 | # if preview_file: 133 | # util.printD(f"Wrote preview image : {path_img}") 134 | 135 | return f"Download started" 136 | 137 | def download_preview_image(filepath, version_info): 138 | if not version_info: 139 | return False 140 | # save preview 141 | if "images" in version_info.keys(): 142 | try: 143 | img_dict = version_info["images"][0] 144 | if "url" in img_dict: 145 | img_url = img_dict["url"] 146 | if "width" in img_dict: 147 | if img_dict["width"]: 148 | img_url = util.change_width_from_image_url(img_url, img_dict["width"]) 149 | # get image 150 | with requests.get(img_url, stream=True) as img_r: 151 | if not img_r.ok: 152 | util.printD("Get error code: " + str(img_r.status_code)) 153 | return False 154 | 155 | with open(filepath, 'wb') as f: 156 | img_r.raw.decode_content = True 157 | shutil.copyfileobj(img_r.raw, f) 158 | except Exception as e: 159 | pass 160 | 161 | return True 162 | 163 | # def generate_LoRa_metadata(filepath, version_info): 164 | 165 | # LoRa_metadata = { 166 | # "description": None, 167 | # "sd version": None, 168 | # "activation text": None, 169 | # "preferred weight": 0, 170 | # "notes": None 171 | # } 172 | 173 | # if not version_info: 174 | # return False 175 | 176 | # if os.path.isfile(filepath): 177 | # return False 178 | # # try: 179 | # # with open(filepath, 'r') as f: 180 | # # LoRa_metadata = json.load(f) 181 | # # except: 182 | # # pass 183 | 184 | # if "description" in version_info.keys(): 185 | # LoRa_metadata['description'] = version_info["description"] 186 | 187 | # if "baseModel" in version_info.keys(): 188 | # baseModel = version_info["baseModel"] 189 | # if baseModel in setting.model_basemodels.keys(): 190 | # LoRa_metadata['sd version'] = setting.model_basemodels[baseModel] 191 | # else: 192 | # LoRa_metadata['sd version'] = 'Unknown' 193 | 194 | # if "trainedWords" in version_info.keys(): 195 | # LoRa_metadata['activation text'] = ", ".join(version_info['trainedWords']) 196 | 197 | # notes = list() 198 | # if "modelId" in version_info.keys(): 199 | # notes.append(f"https://civitai.com/models/{version_info['modelId']}") 200 | 201 | # if "downloadUrl" in version_info.keys(): 202 | # notes.append(version_info['downloadUrl']) 203 | 204 | # if len(notes) > 0: 205 | # LoRa_metadata['notes'] = ", ".join(notes) 206 | 207 | # try: 208 | # with open(filepath, 'w') as f: 209 | # json.dump(LoRa_metadata, f, indent=4) 210 | # except Exception as e: 211 | # return False 212 | 213 | # return True 214 | 215 | def download_image_file(model_name, image_urls, progress_gr=None): 216 | if not model_name: 217 | return 218 | 219 | model_folder = util.make_download_image_folder(model_name) 220 | 221 | if not model_folder: 222 | return 223 | 224 | save_folder = os.path.join(model_folder, "images") 225 | 226 | if not os.path.exists(save_folder): 227 | os.makedirs(save_folder) 228 | 229 | if image_urls and len(image_urls) > 0: 230 | for image_count, img_url in enumerate(tqdm(image_urls, desc=f"Download images"), start=0): 231 | 232 | result = util.is_url_or_filepath(img_url) 233 | if result == "filepath": 234 | if os.path.basename(img_url) != setting.no_card_preview_image: 235 | description_img = os.path.join(save_folder,os.path.basename(img_url)) 236 | shutil.copyfile(img_url,description_img) 237 | elif result == "url": 238 | try: 239 | # get image 240 | with requests.get(img_url, stream=True) as img_r: 241 | if not img_r.ok: 242 | util.printD("Get error code: " + str(img_r.status_code) + ": proceed to the next file") 243 | else: 244 | # write to file 245 | image_id, ext = os.path.splitext(os.path.basename(img_url)) 246 | description_img = os.path.join(save_folder,f'{image_id}{setting.preview_image_suffix}{setting.preview_image_ext}') 247 | with open(description_img, 'wb') as f: 248 | img_r.raw.decode_content = True 249 | shutil.copyfileobj(img_r.raw, f) 250 | except Exception as e: 251 | pass 252 | return 253 | 254 | def download_file(url, file_name): 255 | # Maximum number of retries 256 | max_retries = 5 257 | 258 | # Delay between retries (in seconds) 259 | retry_delay = 10 260 | 261 | while True: 262 | # Check if the file has already been partially downloaded 263 | if os.path.exists(file_name): 264 | # Get the size of the downloaded file 265 | downloaded_size = os.path.getsize(file_name) 266 | 267 | # Set the range of the request to start from the current size of the downloaded file 268 | headers = {"Range": f"bytes={downloaded_size}-"} 269 | else: 270 | downloaded_size = 0 271 | headers = {} 272 | 273 | headers["Authorization"] = f"Bearer {setting.civitai_api_key}" 274 | 275 | # Split filename from included path 276 | tokens = re.split(re.escape('\\'), file_name) 277 | file_name_display = tokens[-1] 278 | 279 | # Initialize the progress bar 280 | progress = tqdm(total=1000000000, unit="B", unit_scale=True, 281 | desc=f"Downloading {file_name_display}", initial=downloaded_size, leave=False) 282 | 283 | # Open a local file to save the download 284 | with open(file_name, "ab") as f: 285 | while True: 286 | try: 287 | # Send a GET request to the URL and save the response to the local file 288 | response = requests.get(url, headers=headers, stream=True) 289 | 290 | # Get the total size of the file 291 | total_size = int(response.headers.get("Content-Length", 0)) 292 | 293 | # Update the total size of the progress bar if the `Content-Length` header is present 294 | if total_size == 0: 295 | total_size = downloaded_size 296 | progress.total = total_size 297 | 298 | # Write the response to the local file and update the progress bar 299 | for chunk in response.iter_content(chunk_size=1024): 300 | if chunk: # filter out keep-alive new chunks 301 | f.write(chunk) 302 | progress.update(len(chunk)) 303 | 304 | downloaded_size = os.path.getsize(file_name) 305 | # Break out of the loop if the download is successful 306 | break 307 | except ConnectionError as e: 308 | # Decrement the number of retries 309 | max_retries -= 1 310 | 311 | # If there are no more retries, raise the exception 312 | if max_retries == 0: 313 | raise e 314 | 315 | # Wait for the specified delay before retrying 316 | time.sleep(retry_delay) 317 | 318 | # Close the progress bar 319 | progress.close() 320 | downloaded_size = os.path.getsize(file_name) 321 | # Check if the download was successful 322 | if downloaded_size >= total_size: 323 | print(f"{file_name_display} successfully downloaded.") 324 | break 325 | else: 326 | print(f"Error: File download failed. Retrying... {file_name_display}") 327 | 328 | # 테스트중이다. download_file를 대체할것... 거의 같다. 329 | def download_file_gr(url, file_name, progress_gr=None): 330 | # Maximum number of retries 331 | max_retries = 5 332 | 333 | # Delay between retries (in seconds) 334 | retry_delay = 10 335 | 336 | while True: 337 | # Check if the file has already been partially downloaded 338 | if os.path.exists(file_name): 339 | # Get the size of the downloaded file 340 | downloaded_size = os.path.getsize(file_name) 341 | 342 | # Set the range of the request to start from the current size of the downloaded file 343 | headers = {"Range": f"bytes={downloaded_size}-"} 344 | else: 345 | downloaded_size = 0 346 | headers = {} 347 | 348 | # Split filename from included path 349 | tokens = re.split(re.escape('\\'), file_name) 350 | file_name_display = tokens[-1] 351 | 352 | # Open a local file to save the download 353 | with open(file_name, "ab") as f: 354 | while True: 355 | try: 356 | # Send a GET request to the URL and save the response to the local file 357 | response = requests.get(url, headers=headers, stream=True) 358 | 359 | # Get the total size of the file 360 | total_size = int(response.headers.get("Content-Length", 0)) 361 | 362 | # Update the total size of the progress bar if the `Content-Length` header is present 363 | if total_size == 0: 364 | total_size = downloaded_size 365 | 366 | # Initialize the progress bar 367 | progress = tqdm(range(total_size), total=total_size, unit="B", unit_scale=True, desc=f"Downloading {file_name_display}", initial=downloaded_size, leave=False) 368 | 369 | # Write the response to the local file and update the progress bar 370 | for chunk in response.iter_content(chunk_size=1024): 371 | if chunk: # filter out keep-alive new chunks 372 | f.write(chunk) 373 | progress.update(len(chunk)) 374 | 375 | downloaded_size = os.path.getsize(file_name) 376 | # Break out of the loop if the download is successful 377 | 378 | # Close the progress bar 379 | progress.close() 380 | break 381 | except ConnectionError as e: 382 | # Decrement the number of retries 383 | max_retries -= 1 384 | 385 | # If there are no more retries, raise the exception 386 | if max_retries == 0: 387 | raise e 388 | 389 | # Wait for the specified delay before retrying 390 | time.sleep(retry_delay) 391 | 392 | downloaded_size = os.path.getsize(file_name) 393 | # Check if the download was successful 394 | if downloaded_size >= total_size: 395 | print(f"{file_name_display} successfully downloaded.") 396 | break 397 | else: 398 | print(f"Error: File download failed. Retrying... {file_name_display}") 399 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/model.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from . import util 4 | from . import setting 5 | 6 | # 이 모듈은 다운로드 받은 정보를 관리한다. 7 | # civitai 와의 연결은 최소화하고 local의 관리를 목표로 한다. 8 | 9 | Downloaded_Models = dict() # modelid : [vid:path...] #현재 가지고 있는 모델을 저장한다. 대표 경로가 저장되어 있다. 10 | Downloaded_InfoPath = dict() # infoPath : vid #경로를 기준으로 저장한다. info 파일 하나당 버전 하나 / 버전 아이디로 저장된 경로파일을 찾을수 있다. 11 | # get_infopaths 해당버전의 중복된 모든 경로를 구할수 있다 12 | 13 | def Test_Models(): 14 | if Downloaded_Models: 15 | for mid, vidpath in Downloaded_Models.items(): 16 | util.printD(f"{mid} :\n") 17 | # for vid, path in vidpath: 18 | # print(f"{vid} : {path}\n") 19 | 20 | def update_downloaded_model(): 21 | global Downloaded_Models 22 | global Downloaded_InfoPath 23 | 24 | Downloaded_Models, Downloaded_InfoPath = get_model_path() 25 | 26 | def get_default_model_folder(mid): 27 | if mid: 28 | path = None 29 | if str(mid) in Downloaded_Models.keys(): 30 | for vid, version_paths in Downloaded_Models[str(mid)]: 31 | path = version_paths 32 | break 33 | 34 | if path: 35 | vfolder , vfile = os.path.split(path) 36 | return vfolder 37 | 38 | return None 39 | 40 | def get_default_version_folder(vid): 41 | if vid: 42 | 43 | paths = get_infopaths(vid) 44 | 45 | if not paths: 46 | return None 47 | 48 | for path in paths.keys(): 49 | vfolder , vfile = os.path.split(path) 50 | return vfolder 51 | 52 | return None 53 | 54 | def get_default_version_infopath(vid): 55 | if vid: 56 | 57 | paths = get_infopaths(vid) 58 | 59 | if not paths: 60 | return None 61 | 62 | for path in paths.keys(): 63 | return path 64 | 65 | return None 66 | 67 | def get_model_downloaded_versions(modelid:str): 68 | 69 | if not modelid: 70 | return None 71 | 72 | if not Downloaded_Models: 73 | return None 74 | 75 | downloaded_version = dict() 76 | 77 | if str(modelid) in Downloaded_Models.keys(): 78 | for vid, version_paths in Downloaded_Models[str(modelid)]: 79 | vinfo = util.read_json(version_paths) 80 | if vinfo: 81 | downloaded_version[str(vinfo['id'])] = vinfo['name'] 82 | 83 | return downloaded_version if len(downloaded_version) > 0 else None 84 | 85 | def get_infopaths( versionid ): 86 | if not Downloaded_InfoPath: 87 | return 88 | result = {path : vid for path, vid in Downloaded_InfoPath.items() if str(vid) == str(versionid)} 89 | return result if len(result) > 0 else None 90 | 91 | # modelid를 키로 modelid가 같은 version_info의 File Path를 list로 묶어 반환한다. 92 | def get_model_path()->dict: 93 | root_dirs = list(set(setting.get_model_folders())) 94 | file_list = util.search_file(root_dirs,None,[setting.info_ext]) 95 | 96 | models = dict() 97 | infopaths = dict() 98 | 99 | if not file_list: 100 | return None,None 101 | 102 | for file_path in file_list: 103 | try: 104 | with open(file_path, 'r') as f: 105 | json_data = json.load(f) 106 | if "modelId" in json_data.keys(): 107 | mid = str(json_data['modelId']).strip() 108 | vid = str(json_data['id']).strip() 109 | 110 | infopaths[file_path] = vid 111 | 112 | if mid not in models.keys(): 113 | models[mid] = list() 114 | 115 | models[mid].append([vid, file_path]) 116 | except: 117 | pass 118 | 119 | if len(models) > 0: 120 | return models, infopaths 121 | 122 | return None,None 123 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/model_action.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import gradio as gr 4 | import datetime 5 | 6 | from . import util 7 | from . import model 8 | from . import setting 9 | from . import ishortcut 10 | # from . import civitai 11 | 12 | def on_ui(): 13 | 14 | with gr.Column(scale=1): 15 | with gr.Accordion("", open=True) as model_title_name: 16 | update_modelfolder_btn = gr.Button(value="Reload Model Information", variant="primary" , visible=True) 17 | with gr.Row(): 18 | download_imagefolder = gr.Button(value="Open Download Image Folder", variant="primary" , visible=True) 19 | saved_infofolder = gr.Button(value="Open Saved Information Folder", variant="primary" , visible=True) 20 | 21 | downloaded_information = gr.DataFrame( 22 | headers=["Version ID", "Version Name","Location"], 23 | datatype=["str", "str","str"], 24 | col_count=(3,"fixed"), 25 | interactive=False, 26 | type="array" 27 | ) 28 | 29 | with gr.Accordion("", open=True, visible=False) as version_title_name: 30 | version_location = gr.Textbox(label="Version file location", interactive=False) 31 | downloaded_files = gr.DataFrame( 32 | headers=["File Id","Filename","Type","Downloaded"], 33 | datatype=["str","str","str","str"], 34 | col_count=(4,"fixed"), 35 | interactive=False, 36 | type="array" 37 | ) 38 | download_openfolder = gr.Button(value="Open Folder",variant="primary" , visible=True) 39 | version_info = gr.JSON(label="Version information") 40 | 41 | with gr.Row(visible=False): 42 | selected_model_id = gr.Textbox() 43 | refresh_information = gr.Textbox() 44 | 45 | update_modelfolder_btn.click( 46 | fn=on_update_modelfolder_btn_click, 47 | inputs=None, 48 | outputs=refresh_information 49 | ) 50 | 51 | downloaded_information.select( 52 | fn=on_downloaded_information_select, 53 | inputs=[ 54 | downloaded_information 55 | ], 56 | outputs=[ 57 | version_title_name, 58 | version_location, 59 | downloaded_files, 60 | version_info 61 | ] 62 | ) 63 | 64 | selected_model_id.change( 65 | fn=on_load_model, 66 | inputs=[ 67 | selected_model_id, 68 | ], 69 | outputs=[ 70 | model_title_name, 71 | downloaded_information, 72 | version_title_name, 73 | version_location, 74 | downloaded_files, 75 | version_info 76 | ] 77 | ) 78 | 79 | refresh_information.change( 80 | fn=on_load_model, 81 | inputs=[ 82 | selected_model_id, 83 | ], 84 | outputs=[ 85 | model_title_name, 86 | downloaded_information, 87 | version_title_name, 88 | version_location, 89 | downloaded_files, 90 | version_info 91 | ], 92 | show_progress=False 93 | ) 94 | 95 | download_openfolder.click(on_download_openfolder_click,[version_location],None) 96 | download_imagefolder.click(on_download_imagefolder_click,[selected_model_id],None) 97 | saved_infofolder.click(on_saved_infofolder_click,[selected_model_id],None) 98 | 99 | return selected_model_id, refresh_information 100 | 101 | 102 | def on_update_modelfolder_btn_click(): 103 | model.update_downloaded_model() 104 | current_time = datetime.datetime.now() 105 | return current_time 106 | 107 | def on_download_imagefolder_click(modelid): 108 | if modelid: 109 | # model_info = civitai.get_model_info(modelid) 110 | model_info = ishortcut.get_model_info(modelid) 111 | if model_info: 112 | model_name = model_info['name'] 113 | image_folder = util.get_download_image_folder(model_name) 114 | if image_folder: 115 | util.open_folder(image_folder) 116 | 117 | def on_saved_infofolder_click(modelid): 118 | if modelid: 119 | model_path = os.path.join(setting.shortcut_info_folder, modelid) 120 | if model_path: 121 | if os.path.exists(model_path): 122 | util.open_folder(model_path) 123 | 124 | def on_download_openfolder_click(vlocation): 125 | if vlocation: 126 | path = os.path.dirname(vlocation) 127 | if path: 128 | util.open_folder(path) 129 | 130 | def on_downloaded_information_select(evt: gr.SelectData, df): 131 | # util.printD(evt.index) 132 | vname = None 133 | vlocation = None 134 | contents = None 135 | file_list = None 136 | base_folder = None 137 | 138 | if df: 139 | vlocation = df[evt.index[0]][2] 140 | vname = df[evt.index[0]][1] 141 | base_folder = os.path.dirname(vlocation) 142 | if os.path.isfile(vlocation): 143 | try: 144 | with open(vlocation, 'r') as f: 145 | contents = json.load(f) 146 | if 'id' not in contents.keys(): 147 | contents = None 148 | except: 149 | pass 150 | 151 | if contents: 152 | file_list = list() 153 | if "files" in contents: 154 | for file in contents['files']: 155 | file_list.append([file['id'],file['name'],file['type'],"Downloaded" if os.path.isfile(os.path.join(base_folder,file['name'])) else ""]) 156 | 157 | if vlocation: 158 | return gr.update(label=vname, visible=True), vlocation, file_list if len(file_list) > 0 else None, contents 159 | else: 160 | return gr.update(label=vname, visible=False), vlocation, None ,contents 161 | 162 | def on_load_model(modelid=None): 163 | title_name = None 164 | data_list = list() 165 | 166 | if modelid: 167 | model.update_downloaded_model() 168 | title_name, data_list = get_model_information(modelid) 169 | 170 | return gr.update(label=title_name), gr.update(value=data_list), gr.update(label=None, visible=False), None, None, None 171 | 172 | def get_model_information(modelid:str=None): 173 | if modelid: 174 | # model_info = civitai.get_model_info(modelid) 175 | model_info = ishortcut.get_model_info(modelid) 176 | if model_info: 177 | model_type = model_info['type'] 178 | title_name = f"{model_info['name']}" 179 | 180 | downloaded_versions = model.get_model_downloaded_versions(modelid) 181 | versions_list = list() 182 | if downloaded_versions: 183 | for vid, name in downloaded_versions.items(): 184 | info_list = model.get_infopaths(str(vid)) 185 | if info_list: 186 | for path in info_list: 187 | versions_list.append([vid,name,path]) 188 | # util.printD(f"{vid} : {name} : {path}") 189 | return title_name, versions_list if len(versions_list) > 0 else None 190 | return None, None -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/prompt.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | 4 | 5 | # def parse_data(data): 6 | # parsed_data = {} 7 | 8 | # # Split the data into lines 9 | # lines = data.split('\n') 10 | 11 | # # Parse the positive prompt 12 | # if not lines[0].startswith('Negative prompt:') and not lines[0].startswith('Steps:'): 13 | # parsed_data['prompt'] = lines[0] 14 | 15 | # # Check if negative prompt is present 16 | # if len(lines) > 1 and lines[1].startswith('Negative prompt:'): 17 | # negative_prompt_match = re.search(r'Negative prompt:\s*(.+)', lines[1]) 18 | # if negative_prompt_match: 19 | # parsed_data['negativePrompt'] = negative_prompt_match.group(1) 20 | 21 | # # Parse the other data using the parse_steps_data function 22 | # steps_data = lines[-1] 23 | # parsed_steps_data = parse_option_data(steps_data) 24 | # if parsed_steps_data: 25 | # parsed_data['options'] = parsed_steps_data 26 | # return parsed_data 27 | 28 | # def parse_data(data): 29 | # parsed_data = {} 30 | 31 | # # Split the data into lines 32 | # lines = data.split('\n') 33 | 34 | # # Parse the positive prompt 35 | # count = 0 36 | # for line in lines: 37 | # if not line.startswith('Negative prompt:') and not line.startswith('Steps:'): 38 | # if 'prompt' in parsed_data: 39 | # parsed_data['prompt'] = parsed_data['prompt'] + line 40 | # else: 41 | # parsed_data['prompt'] = line 42 | # count = count + 1 43 | # else: 44 | # break 45 | 46 | # if len(lines) >= count: 47 | # lines = lines[count:] 48 | 49 | # # Check if negative prompt is present 50 | # if len(lines) > 0 and lines[0].startswith('Negative prompt:'): 51 | # negative_prompt_match = re.search(r'Negative prompt:\s*(.+)', lines[0]) 52 | # if negative_prompt_match: 53 | # parsed_data['negativePrompt'] = negative_prompt_match.group(1) 54 | 55 | # # Parse the other data using the parse_steps_data function 56 | # steps_data = lines[-1] 57 | # if steps_data and steps_data.startswith('Steps:'): 58 | # parsed_steps_data = parse_option_data(steps_data) 59 | # if parsed_steps_data: 60 | # parsed_data['options'] = parsed_steps_data 61 | 62 | # return parsed_data 63 | 64 | def parse_data(data): 65 | parsed_data = {} 66 | 67 | # Split the data into lines 68 | lines = data.split('\n') 69 | 70 | # Parse the positive prompt 71 | count = 0 72 | for line in lines: 73 | if not line.startswith('Negative prompt:') and not line.startswith('Steps:'): 74 | if 'prompt' in parsed_data: 75 | parsed_data['prompt'] = parsed_data['prompt'] + line 76 | else: 77 | parsed_data['prompt'] = line 78 | count = count + 1 79 | else: 80 | break 81 | 82 | if len(lines) >= count: 83 | lines = lines[count:] 84 | 85 | # Check if negative prompt is present 86 | for line in lines: 87 | if not line.startswith('Steps:'): 88 | if line.startswith('Negative prompt:'): 89 | negative_prompt_match = re.search(r'Negative prompt:\s*(.+)', line) 90 | if negative_prompt_match: 91 | parsed_data['negativePrompt'] = negative_prompt_match.group(1) 92 | else: 93 | if 'negativePrompt' in parsed_data: 94 | parsed_data['negativePrompt'] = parsed_data['negativePrompt'] + line 95 | else: 96 | parsed_data['negativePrompt'] = line 97 | else: 98 | break 99 | 100 | # Parse the other data using the parse_steps_data function 101 | steps_data = lines[-1] 102 | if steps_data and steps_data.startswith('Steps:'): 103 | parsed_steps_data = parse_option_data(steps_data) 104 | if parsed_steps_data: 105 | parsed_data['options'] = parsed_steps_data 106 | 107 | return parsed_data 108 | 109 | def parse_option_data(option_data): 110 | parsed_data = {} 111 | 112 | if option_data: 113 | # Split the data by comma and colon 114 | entries = re.split(r',\s*|\s*:\s*', option_data) 115 | 116 | # Extract key-value pairs 117 | for i in range(0, len(entries), 2): 118 | key = entries[i].strip() 119 | if i + 1 < len(entries): 120 | value = entries[i + 1].strip() 121 | parsed_data[key] = value 122 | 123 | return parsed_data 124 | 125 | def parse_detail_prompt(prompt_data): 126 | details = re.split(r',\s*|\s*,\s*', prompt_data) 127 | details = [detail.strip() for detail in details if detail.strip()] 128 | return details 129 | 130 | # # Example usage 131 | # data = '''Best quality, masterpiece, ultra high res, (photorealistic:1.4),girl, beautiful_face, detailed skin,upper body, 132 | # Negative prompt: ng_deepnegative_v1_75t, badhandv4 (worst quality:2), (low quality:2), (normal quality:2), lowres, bad anatomy, bad hands, normal quality, ((monochrome)), ((grayscale)), ng_deepnegative_v1_75t, badhandv4 (worst quality:2), (low quality:2), (normal quality:2), lowres, bad anatomy, bad hands, normal quality, ((monochrome)), ((grayscale)), 133 | # Steps: 28, Sampler: DPM++ 2M Karras, CFG scale: 11, Seed: 2508416159, Size: 640x384, Model hash: 7af26c6c98, Model: 真人_xsmix_V04很好看, Denoising strength: 0.53, Hires upscale: 2, Hires steps: 20, Hires upscaler: 4x-UltraSharp, Dynamic thresholding enabled: True, Mimic scale: 7, Threshold percentile: 100''' 134 | 135 | # data = '''Best quality, masterpiece, ultra high res, (photorealistic:1.4), beautiful_face, detailed skin,upper body,,1boy, 136 | # 137 | # Negative prompt: ng_deepnegative_v1_75t, badhandv4 (worst quality:2), (low quality:2), (normal quality:2), lowres, bad anatomy, bad hands, normal quality, ((monochrome)), ((grayscale)), ng_deepnegative_v1_75t, badhandv4 (worst quality:2), (low quality:2), (normal quality:2), lowres, bad anatomy, bad hands, normal quality, ((monochrome)), ((grayscale)), 138 | # Steps: 28, Sampler: DPM++ 2M Karras, CFG scale: 11, Seed: 2899275, Size: 640x384, Model hash: 69bc54fc2c, Model: 2.8D_majicmixRealistic_v3, Denoising strength: 0.53, Hires upscale: 2, Hires steps: 20, Hires upscaler: 4x-UltraSharp, Dynamic thresholding enabled: True, Mimic scale: 7, Threshold percentile: 100''' 139 | 140 | # data = '''Best quality, masterpiece, ultra high res, (photorealistic:1.4), beautiful_face, detailed skin,upper body,,1boy, 141 | # Steps: 28, Sampler: DPM++ 2M Karras, CFG scale: 11, Seed: 2899275, Size: 640x384, Model hash: 69bc54fc2c, Model: 2.8D_majicmixRealistic_v3, Denoising strength: 0.53, Hires upscale: 2, Hires steps: 20, Hires upscaler: 4x-UltraSharp, Dynamic thresholding enabled: True, Mimic scale: 7, Threshold percentile: 100''' 142 | 143 | # data = '''Best quality, masterpiece, ultra high res, (photorealistic:1.4), beautiful_face, detailed skin,upper body,,1boy, 144 | # Negative prompt: ng_deepnegative_v1_75t, badhandv4 (worst quality:2), (low quality:2), (normal quality:2), lowres, bad anatomy, bad hands, normal quality, ((monochrome)), ((grayscale)), ng_deepnegative_v1_75t, badhandv4 (worst quality:2), (low quality:2), (normal quality:2), lowres, bad anatomy, bad hands, normal quality, ((monochrome)), ((grayscale)),''' 145 | 146 | 147 | # data = ''', woman, (wearing kimono_clothes:1.3), holding umbrella, 148 | # good hand,4k, high-res, masterpiece, best quality, head:1.3,((Hasselblad photography)), finely detailed skin, sharp focus, (cinematic lighting), night, soft lighting, dynamic angle, [:(detailed face:1.2):0.2], medium breasts, outside, 149 | # Negative prompt: NG_DeepNagetive_V1_75T,(greyscale:1.2), 150 | # paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, glans 151 | # Steps: 30, Sampler: DPM++ 2M Karras, CFG scale: 7, Seed: 2181989112, Face restoration: CodeFormer, Size: 512x768, Model hash: 3e9211917c, Model: CheckpointYesmix_v16Original''' 152 | 153 | # parsed_data = parse_data(data) 154 | # print(parsed_data) 155 | 156 | # output_file = 'parsed_prompt.json' 157 | # with open(output_file, 'w') as f: 158 | # json.dump(parsed_data, f, indent=4) -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/prompt_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | 3 | import modules.shared as shared 4 | from modules.sd_samplers import samplers, samplers_for_img2img 5 | 6 | from . import prompt 7 | from . import util 8 | 9 | def on_option_change(option): 10 | parameters = None 11 | 12 | sampler = None 13 | steps = 1 14 | faces = False 15 | upscaler = None 16 | enable_hr = False 17 | size = None 18 | size_width = 0 19 | size_height = 0 20 | 21 | hr_steps = 0 22 | hr_denoising = 0.7 23 | hr_upscale = 2 24 | hr_resize = None 25 | hr_resize_width = 0 26 | hr_resize_height = 0 27 | 28 | cfg_scale = 7.0 29 | others = None 30 | 31 | try: 32 | parameters = prompt.parse_option_data(option) 33 | except: 34 | pass 35 | 36 | if parameters: 37 | 38 | sampler = parameters.pop('Sampler',None) 39 | steps = parameters.pop('Steps', 1) 40 | faces = parameters.pop('Face restoration',False) 41 | 42 | size = parameters.pop('Size',None) 43 | 44 | if size: 45 | try: 46 | size_width, size_height = map(int, size.split("x")) 47 | except: 48 | pass 49 | 50 | cfg_scale = parameters.pop('CFG scale',7.0) 51 | upscaler = parameters.pop('Hires upscaler', None) 52 | if upscaler: 53 | enable_hr = True 54 | hr_steps = parameters.pop('Hires steps',0) 55 | hr_denoising = parameters.pop('Denoising strength',0.7) 56 | hr_upscale = parameters.pop('Hires upscale',2) 57 | hr_resize = parameters.pop('Hires resize', None) 58 | if hr_resize: 59 | try: 60 | hr_resize_width, hr_resize_height = map(int, hr_resize.split("x")) 61 | except: 62 | pass 63 | 64 | others = [f"{k}:{v}" for k, v in parameters.items()] 65 | if others: 66 | others = ", ".join(others) 67 | 68 | return steps, sampler, faces , \ 69 | enable_hr, gr.update(visible=True if enable_hr else False), upscaler, hr_steps, hr_denoising, hr_upscale, hr_resize_width, hr_resize_height, \ 70 | size_width, size_height, cfg_scale, others 71 | 72 | def on_make_parameters(steps, sampler, faces , cfg_scale, size_width, size_height , enable_hr, upscaler, hr_steps, hr_denoising, hr_upscale, hr_resize_width, hr_resize_height, others): 73 | 74 | parameters_string = f"Steps:{steps}" 75 | 76 | if sampler: 77 | parameters_string = parameters_string + f", Sampler:{sampler}" 78 | 79 | if faces: 80 | parameters_string = parameters_string + f", Face restoration:CodeFormer" 81 | 82 | if cfg_scale: 83 | parameters_string = parameters_string + f", CFG scale:{cfg_scale}" 84 | 85 | if size_width and size_height: 86 | size = f"{size_width}x{size_height}" 87 | parameters_string = parameters_string + f", Size:{size}" 88 | 89 | if enable_hr: 90 | if upscaler: 91 | parameters_string = parameters_string + f", Hires upscaler:{upscaler}" 92 | 93 | if hr_steps: 94 | parameters_string = parameters_string + f", Hires steps:{hr_steps}" 95 | 96 | if hr_denoising: 97 | parameters_string = parameters_string + f", Denoising strength:{hr_denoising}" 98 | 99 | if hr_upscale: 100 | parameters_string = parameters_string + f", Hires upscale:{hr_upscale}" 101 | 102 | if hr_resize_width and hr_resize_height: 103 | hr_resize = f"{hr_resize_width}x{hr_resize_height}" 104 | parameters_string = parameters_string + f", Hires resize:{hr_resize}" 105 | 106 | if others: 107 | parameters_string = parameters_string + f", {others}" 108 | 109 | return parameters_string 110 | 111 | def on_enable_hr_change(steps, sampler, faces , cfg_scale, size_width, size_height , enable_hr, upscaler, hr_steps, hr_denoising, hr_upscale, hr_resize_width, hr_resize_height, others): 112 | parameter_string = on_make_parameters(steps, sampler, faces , cfg_scale, size_width, size_height , enable_hr, upscaler, hr_steps, hr_denoising, hr_upscale, hr_resize_width, hr_resize_height, others) 113 | return gr.update(visible=enable_hr), parameter_string 114 | 115 | def ui(option): 116 | with gr.Row(): 117 | parameters = gr.Textbox(label="Parameters", lines=3 ,interactive=True, container=True) 118 | with gr.Row(): 119 | sampler = gr.Dropdown(label="Sampling method", choices=[x.name for x in samplers], interactive=True) 120 | steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling steps", value=20, interactive=True) 121 | with gr.Row(Variant="compact"): 122 | restore_faces = gr.Checkbox(label='Restore faces', value=False, interactive=True) 123 | # tiling = gr.Checkbox(label='Tiling', value=False, interactive=True) 124 | enable_hr = gr.Checkbox(label='Hires. fix', value=False, interactive=True) 125 | with gr.Row(visible=False) as hr: 126 | with gr.Column(): 127 | with gr.Row(variant="compact"): 128 | hr_upscaler = gr.Dropdown(label="Upscaler", choices=[*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]], interactive=True) 129 | hr_second_pass_steps = gr.Slider(minimum=0, maximum=150, step=1, label='Hires steps', value=0, interactive=True) 130 | denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.7, interactive=True) 131 | with gr.Row(variant="compact"): 132 | hr_upscale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, interactive=True) 133 | hr_resize_x = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize width to", value=0, interactive=True) 134 | hr_resize_y = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize height to", value=0, interactive=True) 135 | with gr.Row(): 136 | with gr.Column(): 137 | width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, interactive=True) 138 | height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, interactive=True) 139 | cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, interactive=True) 140 | with gr.Row( visible=True): 141 | others = gr.Textbox(label="other", visible=True) 142 | 143 | option.change( 144 | fn=on_option_change, 145 | inputs=option, 146 | outputs=[ 147 | steps, 148 | sampler, 149 | restore_faces, 150 | enable_hr, 151 | hr, 152 | hr_upscaler, 153 | hr_second_pass_steps, 154 | denoising_strength, 155 | hr_upscale, 156 | hr_resize_x, 157 | hr_resize_y, 158 | width, 159 | height, 160 | cfg_scale, 161 | others 162 | ] 163 | ) 164 | 165 | sampler.change( 166 | fn=on_make_parameters, 167 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 168 | outputs=[parameters] 169 | ) 170 | 171 | enable_hr.change( 172 | fn=on_enable_hr_change, 173 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 174 | outputs=[hr , parameters] 175 | ) 176 | 177 | steps.change( 178 | fn=on_make_parameters, 179 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 180 | outputs=[parameters] 181 | ) 182 | 183 | restore_faces.change( 184 | fn=on_make_parameters, 185 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 186 | outputs=[parameters] 187 | ) 188 | 189 | hr_upscaler.change( 190 | fn=on_make_parameters, 191 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 192 | outputs=[parameters] 193 | ) 194 | 195 | hr_second_pass_steps.change( 196 | fn=on_make_parameters, 197 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 198 | outputs=[parameters] 199 | ) 200 | 201 | denoising_strength.change( 202 | fn=on_make_parameters, 203 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 204 | outputs=[parameters] 205 | ) 206 | 207 | hr_upscale.change( 208 | fn=on_make_parameters, 209 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 210 | outputs=[parameters] 211 | ) 212 | 213 | hr_resize_x.change( 214 | fn=on_make_parameters, 215 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 216 | outputs=[parameters] 217 | ) 218 | 219 | hr_resize_y.change( 220 | fn=on_make_parameters, 221 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 222 | outputs=[parameters] 223 | ) 224 | 225 | width.change( 226 | fn=on_make_parameters, 227 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 228 | outputs=[parameters] 229 | ) 230 | 231 | height.change( 232 | fn=on_make_parameters, 233 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 234 | outputs=[parameters] 235 | ) 236 | 237 | cfg_scale.change( 238 | fn=on_make_parameters, 239 | inputs=[steps,sampler,restore_faces,cfg_scale,width,height,enable_hr,hr_upscaler,hr_second_pass_steps,denoising_strength,hr_upscale,hr_resize_x,hr_resize_y,others], 240 | outputs=[parameters] 241 | ) 242 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/recipe.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from . import util 5 | from . import setting 6 | 7 | def get_list(search=None, classification=None, shortcuts=None): 8 | 9 | RecipeCollection = load() 10 | if not RecipeCollection: 11 | return 12 | 13 | result_list = dict() 14 | 15 | keys, descs, notes = util.get_search_keyword(search) 16 | 17 | # filtering classification 18 | for name, v in RecipeCollection.items(): 19 | if classification: 20 | if v['classification'] and classification == v['classification']: 21 | result_list[name] = v 22 | else: 23 | result_list[name] = v 24 | 25 | # filtering shortcuts 26 | if shortcuts: 27 | shortcut_list = dict() 28 | for name, v in result_list.items(): 29 | if 'shortcuts' in v.keys(): 30 | different_shortcuts = set(shortcuts) - set(v["shortcuts"]) 31 | if not different_shortcuts: 32 | shortcut_list[name]= v 33 | 34 | result_list = shortcut_list 35 | 36 | # filtering key 37 | if keys: 38 | key_list = dict() 39 | for name, v in result_list.items(): 40 | for key in keys: 41 | if key in name.lower(): 42 | key_list[name] = v 43 | break 44 | result_list = key_list 45 | 46 | # filtering descs 47 | if descs: 48 | desc_list = dict() 49 | for name, v in result_list.items(): 50 | if not v['description']: 51 | continue 52 | 53 | for desc in descs: 54 | if desc in v['description'].lower(): 55 | desc_list[name] = v 56 | break 57 | result_list = desc_list 58 | 59 | # 필요한것으로 변환 60 | recipelist = list() 61 | for name in result_list.keys(): 62 | recipelist.append(name) 63 | 64 | return recipelist 65 | 66 | def get_reference_shortcuts(): 67 | RecipeCollection = load() 68 | reference_shortcuts = list() 69 | 70 | if not RecipeCollection: 71 | return reference_shortcuts 72 | 73 | for v in RecipeCollection.values(): 74 | if 'shortcuts' in v.keys(): 75 | reference_shortcuts.extend(v['shortcuts']) 76 | 77 | reference_shortcuts = list(set(reference_shortcuts)) 78 | 79 | return reference_shortcuts 80 | 81 | def get_classifications(): 82 | RecipeCollection = load() 83 | classifications = list() 84 | 85 | if not RecipeCollection: 86 | return classifications 87 | 88 | for v in RecipeCollection.values(): 89 | if v['classification']: 90 | classifications.append(v['classification']) 91 | 92 | classifications = list(set(classifications)) 93 | 94 | return classifications 95 | 96 | def is_classifications(classification): 97 | RecipeCollection = load() 98 | 99 | if not RecipeCollection: 100 | return False 101 | 102 | try: 103 | for v in RecipeCollection.values(): 104 | if v['classification'] == classification: 105 | return True 106 | except: 107 | pass 108 | 109 | return False 110 | 111 | def get_recipe_shortcuts(recipe): 112 | if not recipe: 113 | return None 114 | 115 | RecipeCollection = load() 116 | if recipe in RecipeCollection: 117 | if 'shortcuts' in RecipeCollection[recipe]: 118 | return RecipeCollection[recipe]['shortcuts'] 119 | 120 | return None 121 | 122 | def update_recipe_shortcuts(recipe, shortcuts:list): 123 | if not recipe: 124 | return 125 | 126 | RecipeCollection = load() 127 | RecipeCollection = update_shortcuts(RecipeCollection, recipe, shortcuts) 128 | save(RecipeCollection) 129 | 130 | if RecipeCollection: 131 | if recipe in RecipeCollection: 132 | return True 133 | 134 | return False 135 | 136 | def update_recipe_image(recipe, image): 137 | if not recipe: 138 | return 139 | 140 | RecipeCollection = load() 141 | RecipeCollection = update_image(RecipeCollection, recipe, image) 142 | save(RecipeCollection) 143 | 144 | if RecipeCollection: 145 | if recipe in RecipeCollection: 146 | return True 147 | 148 | return False 149 | 150 | def delete_recipe(s_name): 151 | if not s_name: 152 | return 153 | 154 | RecipeCollection = load() 155 | RecipeCollection = delete(RecipeCollection,s_name) 156 | save(RecipeCollection) 157 | 158 | def update_recipe(recipe, name, desc, prompt=None, classification=None): 159 | if not recipe: 160 | return 161 | 162 | if not name: 163 | return 164 | 165 | name = name.strip() 166 | 167 | RecipeCollection = load() 168 | RecipeCollection = update(RecipeCollection, recipe, name, desc, prompt, classification) 169 | 170 | save(RecipeCollection) 171 | 172 | if RecipeCollection: 173 | if name in RecipeCollection: 174 | return True 175 | 176 | return False 177 | 178 | def create_recipe(recipe, desc, prompt=None, classification=None): 179 | if recipe and len(recipe.strip()) > 0: 180 | recipe = recipe.strip() 181 | RecipeCollection = load() 182 | if not RecipeCollection: 183 | RecipeCollection = dict() 184 | else: 185 | if recipe in RecipeCollection: 186 | return False 187 | 188 | RecipeCollection = create(RecipeCollection, recipe, desc, prompt, classification) 189 | 190 | save(RecipeCollection) 191 | 192 | if RecipeCollection: 193 | if recipe in RecipeCollection: 194 | return True 195 | return False 196 | 197 | def get_recipe(s_name): 198 | if not s_name: 199 | return None 200 | 201 | RecipeCollection = load() 202 | if s_name in RecipeCollection: 203 | return RecipeCollection[s_name] 204 | 205 | return None 206 | 207 | #================= raw =================================== 208 | def update_shortcuts(RecipeCollection:dict, recipe, shortcuts:list): 209 | 210 | if not RecipeCollection: 211 | return RecipeCollection 212 | 213 | if not recipe: 214 | return RecipeCollection 215 | 216 | if recipe not in RecipeCollection: 217 | return RecipeCollection 218 | 219 | if not shortcuts: 220 | return RecipeCollection 221 | 222 | RecipeCollection[recipe]['shortcuts'] = shortcuts 223 | 224 | return RecipeCollection 225 | 226 | def update_image(RecipeCollection:dict, recipe, image): 227 | 228 | if not RecipeCollection: 229 | return RecipeCollection 230 | 231 | if not recipe: 232 | return RecipeCollection 233 | 234 | if recipe not in RecipeCollection: 235 | return RecipeCollection 236 | 237 | try: 238 | pre_image = RecipeCollection[recipe]['image'] 239 | if image == pre_image: 240 | return RecipeCollection 241 | 242 | recipe_imgfile = os.path.join(setting.shortcut_recipe_folder,pre_image) 243 | if os.path.isfile(recipe_imgfile): 244 | os.remove(recipe_imgfile) 245 | except: 246 | pass 247 | 248 | RecipeCollection[recipe]['image'] = image 249 | 250 | return RecipeCollection 251 | 252 | def update_classification(RecipeCollection:dict, recipe, classification): 253 | 254 | if not RecipeCollection: 255 | return RecipeCollection 256 | 257 | if not recipe: 258 | return RecipeCollection 259 | 260 | if recipe not in RecipeCollection: 261 | return RecipeCollection 262 | 263 | if classification: 264 | classification = classification.strip() 265 | 266 | RecipeCollection[recipe]['classification'] = classification 267 | 268 | return RecipeCollection 269 | 270 | def update_prompt(RecipeCollection:dict, recipe, prompt): 271 | 272 | if not RecipeCollection: 273 | return RecipeCollection 274 | 275 | if not recipe: 276 | return RecipeCollection 277 | 278 | if recipe not in RecipeCollection: 279 | return RecipeCollection 280 | 281 | RecipeCollection[recipe]['generate'] = prompt 282 | 283 | return RecipeCollection 284 | 285 | def delete(RecipeCollection:dict, recipe)->dict: 286 | if not recipe: 287 | return RecipeCollection 288 | 289 | if not RecipeCollection: 290 | return RecipeCollection 291 | 292 | rc = RecipeCollection.pop(recipe,None) 293 | 294 | try: 295 | pre_image = rc['image'] 296 | recipe_imgfile = os.path.join(setting.shortcut_recipe_folder,pre_image) 297 | if os.path.isfile(recipe_imgfile): 298 | os.remove(recipe_imgfile) 299 | except: 300 | pass 301 | 302 | return RecipeCollection 303 | 304 | def create(RecipeCollection:dict, recipe, desc, prompt=None, classification=None): 305 | 306 | if not recipe: 307 | return RecipeCollection 308 | 309 | if not RecipeCollection: 310 | RecipeCollection = dict() 311 | 312 | recipe = recipe.strip() 313 | 314 | if classification: 315 | classification = classification.strip() 316 | 317 | if len(recipe) > 0: 318 | if recipe not in RecipeCollection: 319 | RecipeCollection[recipe] = { 320 | "description": desc, 321 | "generate": prompt, 322 | "classification": classification, 323 | "image": None, 324 | "shortcuts":[] 325 | } 326 | 327 | return RecipeCollection 328 | 329 | def update(RecipeCollection:dict, recipe, name, desc, prompt=None, classification=None): 330 | 331 | if not RecipeCollection: 332 | return RecipeCollection 333 | 334 | if not recipe: 335 | return RecipeCollection 336 | 337 | if recipe not in RecipeCollection: 338 | return RecipeCollection 339 | 340 | if not name: 341 | return RecipeCollection 342 | 343 | name = name.strip() 344 | 345 | if classification: 346 | classification = classification.strip() 347 | 348 | if recipe == name: 349 | RecipeCollection[recipe]['description'] = desc 350 | RecipeCollection[recipe]['generate'] = prompt 351 | RecipeCollection[recipe]['classification'] = classification 352 | else: 353 | if name not in RecipeCollection: 354 | sc = RecipeCollection.pop(recipe,None) 355 | sc = { 356 | "description": desc, 357 | "generate": prompt, 358 | "classification": classification 359 | # "image": None 360 | } 361 | RecipeCollection[name] = sc 362 | 363 | return RecipeCollection 364 | 365 | def save(RecipeCollection:dict): 366 | output = "" 367 | 368 | #write to file 369 | try: 370 | with open(setting.shortcut_recipe, 'w') as f: 371 | json.dump(RecipeCollection, f, indent=4) 372 | except Exception as e: 373 | util.printD("Error when writing file:"+setting.shortcut_recipe) 374 | return output 375 | 376 | output = "Recipe saved to: " + setting.shortcut_recipe 377 | #util.printD(output) 378 | 379 | return output 380 | 381 | def load()->dict: 382 | if not os.path.isfile(setting.shortcut_recipe): 383 | save({}) 384 | return 385 | 386 | json_data = None 387 | try: 388 | with open(setting.shortcut_recipe, 'r') as f: 389 | json_data = json.load(f) 390 | except: 391 | return None 392 | 393 | # check error 394 | if not json_data: 395 | return None 396 | 397 | # check for new key 398 | return json_data 399 | #========================================================================= -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/recipe_browser_page.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import math 3 | import os 4 | import datetime 5 | 6 | from . import util 7 | from . import setting 8 | from . import recipe 9 | from . import ishortcut 10 | 11 | from PIL import Image 12 | 13 | def on_ui(): 14 | 15 | thumb_list, thumb_totals, thumb_max_page = get_recipe_list(None,None,None,1) 16 | reference_list, reference_totals, reference_max_page = get_recipe_reference_list(1) 17 | 18 | recipe_gallery_page = gr.Slider(minimum=1, maximum=thumb_max_page, value=1, step=1, label=f"Total {thumb_max_page} Pages", interactive=True, visible=True) 19 | with gr.Row(): 20 | recipe_prevPage_btn = gr.Button(value="Prev",scale=1) 21 | recipe_nextPage_btn = gr.Button(value="Next",scale=1) 22 | 23 | recipe_gallery = gr.Gallery(value=thumb_list, columns=setting.prompt_shortcut_column, height="100%", object_fit=setting.gallery_thumbnail_image_style, preview=False, allow_preview=False, show_label=False) 24 | 25 | with gr.Accordion(label="Search Recipe", open=True): 26 | recipe_search = gr.Textbox(label="Search", value="", placeholder="Search name, #description ....",interactive=True, lines=1) 27 | recipe_classification_list = gr.Dropdown(label="Filter Recipe Classification", choices=[setting.PLACEHOLDER] + recipe.get_classifications(), value=setting.PLACEHOLDER, interactive=True, multiselect=False) 28 | 29 | with gr.Accordion(label="Filter Reference Shortcut Items", open=False): 30 | recipe_reference_select_gallery = gr.Gallery(label="Filter Reference Models", columns=setting.prompt_shortcut_column, height="auto", object_fit=setting.gallery_thumbnail_image_style, preview=False, allow_preview=False) 31 | recipe_reference_gallery_page = gr.Slider(minimum=1, maximum=reference_max_page, value=1, step=1, label=f"Total {reference_max_page} Pages", interactive=True, visible=True) 32 | with gr.Row(): 33 | recipe_reference_prevPage_btn = gr.Button(value="Prev",scale=1) 34 | recipe_reference_nextPage_btn = gr.Button(value="Next",scale=1) 35 | 36 | recipe_reference_gallery = gr.Gallery(value=reference_list, show_label=False, columns=setting.prompt_shortcut_column, height="100%", object_fit=setting.gallery_thumbnail_image_style, preview=False, allow_preview=False) 37 | 38 | with gr.Row(visible=False): 39 | # recipe_browser 갱신 트리거 40 | refresh_recipe_browser = gr.Textbox() 41 | 42 | # 강제 검색 트리거 43 | refresh_recipe_search = gr.Textbox() 44 | 45 | # recipe_reference_gallery 갱신 트리거 46 | refresh_recipe_reference_select_gallery = gr.Textbox() 47 | 48 | # 현재 선택된 리퍼런스를 저장하는 곳 49 | recipe_reference_select = gr.State() 50 | 51 | # recipe_reference_select_shortcuts_gallery 에서 선택할때 작용 52 | recipe_reference_select_gallery.select( 53 | fn=on_recipe_reference_select_gallery_select, 54 | inputs=[ 55 | recipe_reference_select 56 | ], 57 | outputs=[ 58 | recipe_reference_select, 59 | recipe_reference_select_gallery, # 이거는 None으로 할 필요는 gallery를 미선택으로 만드는 방법을 몰라서 일단 이렇게 해보자 60 | refresh_recipe_reference_select_gallery, 61 | ], 62 | show_progress=False 63 | ) 64 | 65 | # recipe_reference_select_gallery를 recipe_reference_select 값에서 갱신 66 | refresh_recipe_reference_select_gallery.change( 67 | fn=on_recipe_reference_select_gallery_loading, 68 | inputs=[ 69 | recipe_reference_select, 70 | ], 71 | outputs=[ 72 | recipe_reference_select_gallery, 73 | refresh_recipe_search 74 | ], 75 | show_progress=False 76 | ) 77 | 78 | # recipe_reference_shortcuts_gallery를 선택할때 작용 79 | recipe_reference_gallery.select( 80 | fn=on_recipe_reference_gallery_select, 81 | inputs=[ 82 | recipe_reference_select 83 | ], 84 | outputs=[ 85 | recipe_reference_select, 86 | refresh_recipe_reference_select_gallery, 87 | ], 88 | show_progress=False 89 | ) 90 | 91 | recipe_reference_gallery_page.release( 92 | fn = on_recipe_reference_gallery_page, 93 | inputs = [ 94 | recipe_reference_gallery_page 95 | ], 96 | outputs=[ 97 | recipe_reference_gallery, 98 | ] 99 | ) 100 | 101 | recipe_gallery_page.release( 102 | fn = on_recipe_gallery_page, 103 | inputs = [ 104 | recipe_search, 105 | recipe_classification_list, 106 | recipe_reference_select, 107 | recipe_gallery_page 108 | ], 109 | outputs=[ 110 | recipe_gallery 111 | ] 112 | ) 113 | 114 | refresh_recipe_search.change( 115 | fn=on_recipe_list_search, 116 | inputs=[ 117 | recipe_search, 118 | recipe_classification_list, 119 | recipe_reference_select 120 | ], 121 | outputs=[ 122 | recipe_gallery, 123 | recipe_gallery_page 124 | ] 125 | ) 126 | 127 | refresh_recipe_browser.change( 128 | fn=on_refresh_recipe_browser_change, 129 | inputs= [ 130 | recipe_search, 131 | recipe_classification_list, 132 | recipe_reference_select, 133 | recipe_gallery_page, 134 | recipe_reference_gallery_page 135 | ], 136 | outputs=[ 137 | recipe_classification_list, 138 | recipe_gallery, 139 | recipe_gallery_page, 140 | recipe_reference_gallery, 141 | recipe_reference_gallery_page 142 | ], 143 | show_progress=False 144 | ) 145 | 146 | recipe_search.submit( 147 | fn=on_recipe_list_search, 148 | inputs=[ 149 | recipe_search, 150 | recipe_classification_list, 151 | recipe_reference_select 152 | ], 153 | outputs=[ 154 | recipe_gallery, 155 | recipe_gallery_page 156 | ] 157 | ) 158 | 159 | recipe_classification_list.change( 160 | fn=on_recipe_list_search, 161 | inputs=[ 162 | recipe_search, 163 | recipe_classification_list, 164 | recipe_reference_select 165 | ], 166 | outputs=[ 167 | recipe_gallery, 168 | recipe_gallery_page 169 | ] 170 | ) 171 | 172 | recipe_prevPage_btn.click( 173 | fn = on_recipe_prevPage_btn_click, 174 | inputs = [ 175 | recipe_search, 176 | recipe_classification_list, 177 | recipe_reference_select, 178 | recipe_gallery_page 179 | ], 180 | outputs=[ 181 | recipe_gallery, 182 | recipe_gallery_page 183 | ], 184 | show_progress=False 185 | ) 186 | 187 | recipe_nextPage_btn.click( 188 | fn = on_recipe_nextPage_btn_click, 189 | inputs = [ 190 | recipe_search, 191 | recipe_classification_list, 192 | recipe_reference_select, 193 | recipe_gallery_page 194 | ], 195 | outputs=[ 196 | recipe_gallery, 197 | recipe_gallery_page 198 | ], 199 | show_progress=False 200 | ) 201 | 202 | recipe_reference_prevPage_btn.click( 203 | fn = on_recipe_reference_prevPage_btn_click, 204 | inputs = [ 205 | recipe_reference_gallery_page 206 | ], 207 | outputs=[ 208 | recipe_reference_gallery, 209 | recipe_reference_gallery_page 210 | ], 211 | show_progress=False 212 | ) 213 | 214 | recipe_reference_nextPage_btn.click( 215 | fn = on_recipe_reference_nextPage_btn_click, 216 | inputs = [ 217 | recipe_reference_gallery_page 218 | ], 219 | outputs=[ 220 | recipe_reference_gallery, 221 | recipe_reference_gallery_page 222 | ], 223 | show_progress=False 224 | ) 225 | 226 | return recipe_gallery, refresh_recipe_browser 227 | 228 | def on_recipe_reference_gallery_page(page): 229 | reference_list, reference_totals, reference_max_page = get_recipe_reference_list(page) 230 | return gr.update(value=reference_list) 231 | 232 | def on_recipe_reference_nextPage_btn_click(page): 233 | page = page + 1 234 | reference_list, reference_totals, reference_max_page = get_recipe_reference_list(page) 235 | if page > reference_max_page: 236 | page = reference_max_page 237 | return gr.update(value=reference_list),page 238 | 239 | def on_recipe_reference_prevPage_btn_click(page): 240 | page = page - 1 241 | if page < 1: 242 | page = 1 243 | reference_list, reference_totals, reference_max_page = get_recipe_reference_list(page) 244 | return gr.update(value=reference_list),page 245 | 246 | def on_recipe_gallery_page(search, classification, shortcut, page = 0): 247 | thumb_list , thumb_totals, thumb_max_page = get_recipe_list(search, classification, shortcut, page) 248 | return gr.update(value=thumb_list) 249 | 250 | def on_recipe_nextPage_btn_click(search, classification, shortcut, page = 0): 251 | page = page + 1 252 | thumb_list , thumb_totals, thumb_max_page = get_recipe_list(search, classification, shortcut, page) 253 | if page > thumb_max_page: 254 | page = thumb_max_page 255 | 256 | return gr.update(value=thumb_list), page 257 | 258 | def on_recipe_prevPage_btn_click(search, classification, shortcut, page = 0): 259 | page = page - 1 260 | if page < 1: 261 | page = 1 262 | thumb_list , thumb_totals, thumb_max_page = get_recipe_list(search, classification, shortcut, page) 263 | return gr.update(value=thumb_list), page 264 | 265 | def get_shortcut_by_modelid(ISC, modelid): 266 | if ISC and modelid: 267 | try: 268 | return ISC[str(modelid)] 269 | except: 270 | pass 271 | return None 272 | 273 | def get_recipe_reference_list(page = 0): 274 | 275 | total = 0 276 | max_page = 1 277 | shortlist = None 278 | result = None 279 | 280 | reference_list = recipe.get_reference_shortcuts() 281 | 282 | if not reference_list: 283 | return None, total, max_page 284 | 285 | if reference_list: 286 | total = len(reference_list) 287 | shortlist = reference_list 288 | 289 | if total > 0: 290 | # page 즉 페이징이 아닌 전체가 필요할때도 총페이지 수를 구할때도 있으므로.. 291 | # page == 0 은 전체 리스트를 반환한다 292 | shortcut_count_per_page = setting.prompt_shortcut_column * setting.prompt_shortcut_rows_per_page 293 | 294 | if shortcut_count_per_page > 0: 295 | max_page = math.ceil(total / shortcut_count_per_page) 296 | 297 | if page > max_page: 298 | page = max_page 299 | 300 | if page > 0 and shortcut_count_per_page > 0: 301 | item_start = shortcut_count_per_page * (page - 1) 302 | item_end = (shortcut_count_per_page * page) 303 | if total < item_end: 304 | item_end = total 305 | 306 | shortlist = reference_list[item_start:item_end] 307 | 308 | if shortlist: 309 | result = list() 310 | ISC = ishortcut.load() 311 | for shortcut in shortlist: 312 | # v = ishortcut.get_shortcut_model(str(shortcut)) 313 | v = get_shortcut_by_modelid(ISC,str(shortcut)) 314 | if v: 315 | if ishortcut.is_sc_image(v['id']): 316 | if 'nsfw' in v.keys() and bool(v['nsfw']) and setting.NSFW_filtering_enable: 317 | result.append((setting.nsfw_disable_image,setting.set_shortcutname(v['name'],v['id']))) 318 | else: 319 | result.append((os.path.join(setting.shortcut_thumbnail_folder,f"{v['id']}{setting.preview_image_ext}"), setting.set_shortcutname(v['name'],v['id']))) 320 | else: 321 | result.append((setting.no_card_preview_image,setting.set_shortcutname(v['name'],v['id']))) 322 | else: 323 | result.append((setting.no_card_preview_image,setting.set_shortcutname("delete",shortcut))) 324 | 325 | # util.printD(shortlist) 326 | # util.printD(result) 327 | return result, total, max_page 328 | 329 | def get_recipe(RC, s_name): 330 | 331 | if not RC: 332 | return None 333 | 334 | if not s_name: 335 | return None 336 | 337 | if s_name in RC: 338 | return RC[s_name] 339 | 340 | return None 341 | 342 | def get_recipe_list(search=None, classification=None, shortcut=None, page = 0): 343 | 344 | total = 0 345 | max_page = 1 346 | shortlist = None 347 | result = None 348 | 349 | if classification == setting.PLACEHOLDER: 350 | classification = None 351 | 352 | recipe_list = recipe.get_list(search, classification, shortcut) 353 | 354 | if not recipe_list: 355 | return None, total, max_page 356 | 357 | if recipe_list: 358 | total = len(recipe_list) 359 | shortlist = recipe_list 360 | 361 | if total > 0: 362 | # page 즉 페이징이 아닌 전체가 필요할때도 총페이지 수를 구할때도 있으므로.. 363 | # page == 0 은 전체 리스트를 반환한다 364 | shortcut_count_per_page = setting.prompt_shortcut_column * setting.prompt_shortcut_rows_per_page 365 | 366 | if shortcut_count_per_page > 0: 367 | max_page = math.ceil(total / shortcut_count_per_page) 368 | 369 | if page > max_page: 370 | page = max_page 371 | 372 | if page > 0 and shortcut_count_per_page > 0: 373 | item_start = shortcut_count_per_page * (page - 1) 374 | item_end = (shortcut_count_per_page * page) 375 | if total < item_end: 376 | item_end = total 377 | shortlist = recipe_list[item_start:item_end] 378 | 379 | if shortlist: 380 | result = list() 381 | RecipeCollection = recipe.load() 382 | for shortcut in shortlist: 383 | # re = recipe.get_recipe(shortcut) 384 | re = get_recipe(RecipeCollection, shortcut) 385 | if re: 386 | if re["image"]: 387 | dpimage = os.path.join(setting.shortcut_recipe_folder,f"{re['image']}") 388 | 389 | if os.path.isfile(dpimage): 390 | result.append((dpimage,shortcut)) 391 | else: 392 | result.append((setting.no_card_preview_image,shortcut)) 393 | else: 394 | result.append((setting.no_card_preview_image,shortcut)) 395 | else: 396 | result.append((setting.no_card_preview_image,shortcut)) 397 | 398 | return result, total, max_page 399 | 400 | def on_recipe_list_search(search, classification, shortcut): 401 | thumb_list = None 402 | thumb_totals = 0 403 | thumb_max_page = 1 404 | 405 | thumb_list , thumb_totals, thumb_max_page = get_recipe_list(search, classification, shortcut,1) 406 | 407 | return gr.update(value=thumb_list), gr.update(minimum=1, maximum=thumb_max_page, value=1, step=1, label=f"Total {thumb_max_page} Pages") 408 | 409 | def on_refresh_recipe_browser_change(search, classification, shortcut, sc_page, rs_page): 410 | thumb_list = None 411 | thumb_totals = 0 412 | thumb_max_page = 1 413 | 414 | thumb_list , thumb_totals, thumb_max_page = get_recipe_list(search, classification, shortcut, sc_page) 415 | 416 | if not recipe.is_classifications(classification): 417 | classification = setting.PLACEHOLDER 418 | 419 | reference_list, reference_totals, reference_max_page = get_recipe_reference_list(rs_page) 420 | return gr.update(choices=[setting.PLACEHOLDER] + recipe.get_classifications(), value=classification), \ 421 | gr.update(value=thumb_list), gr.update(minimum=1, maximum=thumb_max_page, value=sc_page, step=1, label=f"Total {thumb_max_page} Pages"), \ 422 | gr.update(value=reference_list), gr.update(minimum=1, maximum=reference_max_page, value=rs_page, step=1, label=f"Total {reference_max_page} Pages") 423 | 424 | def on_recipe_reference_select_gallery_select(evt: gr.SelectData, shortcuts): 425 | if evt.value: 426 | shortcut = evt.value 427 | sc_model_id = setting.get_modelid_from_shortcutname(shortcut) 428 | current_time = datetime.datetime.now() 429 | 430 | if not shortcuts: 431 | shortcuts = list() 432 | 433 | if sc_model_id in shortcuts: 434 | shortcuts.remove(sc_model_id) 435 | 436 | return shortcuts, None, current_time 437 | return shortcuts, None, gr.update(visible=False) 438 | 439 | def on_recipe_reference_select_gallery_loading(shortcuts): 440 | ISC = ishortcut.load() 441 | if not ISC: 442 | return None, gr.update(visible=False) 443 | 444 | result_list = None 445 | 446 | if shortcuts: 447 | result_list = list() 448 | for mid in shortcuts: 449 | if str(mid) in ISC.keys(): 450 | v = ISC[str(mid)] 451 | if ishortcut.is_sc_image(v['id']): 452 | if bool(v['nsfw']) and setting.NSFW_filtering_enable: 453 | result_list.append((setting.nsfw_disable_image,setting.set_shortcutname(v['name'],v['id']))) 454 | else: 455 | result_list.append((os.path.join(setting.shortcut_thumbnail_folder,f"{v['id']}{setting.preview_image_ext}"),setting.set_shortcutname(v['name'],v['id']))) 456 | else: 457 | result_list.append((setting.no_card_preview_image,setting.set_shortcutname(v['name'],v['id']))) 458 | else: 459 | result_list.append((setting.no_card_preview_image,setting.set_shortcutname("delete",mid))) 460 | 461 | current_time = datetime.datetime.now() 462 | 463 | return result_list, current_time 464 | 465 | def on_recipe_reference_gallery_select(evt: gr.SelectData, shortcuts): 466 | current_time = datetime.datetime.now() 467 | 468 | if evt.value: 469 | 470 | shortcut = evt.value 471 | sc_model_id = setting.get_modelid_from_shortcutname(shortcut) 472 | 473 | if not shortcuts: 474 | shortcuts = list() 475 | 476 | if sc_model_id not in shortcuts: 477 | shortcuts.append(str(sc_model_id)) 478 | 479 | return shortcuts, current_time 480 | return shortcuts, gr.update(visible=False) 481 | 482 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/sc_browser_page.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import math 3 | import os 4 | import datetime 5 | 6 | from . import util 7 | from . import setting 8 | from . import model 9 | from . import classification 10 | from . import ishortcut 11 | 12 | DOWNLOADED_MODEL = "Downloaded" 13 | NOT_DOWNLOADED_MODEL = "Not Downloaded" 14 | ALL_DOWNLOADED_MODEL = "All" 15 | 16 | def on_ui(search_open=True,user_shortcut_browser_search_up=None,user_shortcut_column=None, user_shortcut_rows_per_page=None): 17 | shortcut_browser_search_up = setting.shortcut_browser_search_up 18 | shortcut_column = setting.shortcut_column 19 | shortcut_rows_per_page = setting.shortcut_rows_per_page 20 | 21 | if user_shortcut_browser_search_up: 22 | if user_shortcut_browser_search_up == "UP": 23 | shortcut_browser_search_up = True 24 | elif user_shortcut_browser_search_up == "DOWN": 25 | shortcut_browser_search_up = False 26 | 27 | if user_shortcut_column: 28 | shortcut_column = user_shortcut_column 29 | 30 | if user_shortcut_rows_per_page: 31 | shortcut_rows_per_page = user_shortcut_rows_per_page 32 | 33 | thumb_list , thumb_totals, thumb_max_page = get_thumbnail_list(None,False,None,None,None,1,shortcut_column,shortcut_rows_per_page) 34 | 35 | show_downloaded_sc = gr.Dropdown(label='Filter Downloaded', multiselect=False, choices=[ALL_DOWNLOADED_MODEL,DOWNLOADED_MODEL,NOT_DOWNLOADED_MODEL], value=ALL_DOWNLOADED_MODEL, interactive=True) 36 | 37 | # sc_list = list() 38 | # for i in range(0, shortcut_column): 39 | # sc_list.append(gr.Button(value="tttt",scale=1)) 40 | 41 | if shortcut_browser_search_up: 42 | with gr.Accordion("Search", open=search_open): 43 | shortcut_type = gr.Dropdown(label='Filter Model Type', multiselect=True, choices=[k for k in setting.ui_typenames], interactive=True) 44 | sc_search = gr.Textbox(label="Search", value="", placeholder="Search name, #tags, @personal note ....",interactive=True, lines=1) 45 | sc_classification_list = gr.Dropdown(label='Classification',info="The selection options of classification are subject to the AND operation.", multiselect=True, choices=classification.get_list(), interactive=True) 46 | shortcut_basemodel = gr.Dropdown(label='Filter Model BaseModel', multiselect=True, choices=[k for k in setting.model_basemodels.keys()], interactive=True) 47 | reset_filter_btn = gr.Button(value="Reset Filter",variant="primary") 48 | 49 | sc_gallery_page = gr.Slider(minimum=1, maximum=thumb_max_page, value=1, step=1, label=f"Total {thumb_max_page} Pages", interactive=True, visible=True if shortcut_rows_per_page > 0 else False) 50 | with gr.Row(): 51 | sc_prevPage_btn = gr.Button(value="Prev",scale=1, visible=True if shortcut_rows_per_page > 0 else False) 52 | sc_nextPage_btn = gr.Button(value="Next",scale=1, visible=True if shortcut_rows_per_page > 0 else False) 53 | 54 | sc_gallery = gr.Gallery(show_label=False, columns=shortcut_column, height="auto", object_fit=setting.gallery_thumbnail_image_style, allow_preview=False, value=thumb_list) 55 | else: 56 | sc_gallery_page = gr.Slider(minimum=1, maximum=thumb_max_page, value=1, step=1, label=f"Total {thumb_max_page} Pages", interactive=True, visible=True if shortcut_rows_per_page > 0 else False) 57 | with gr.Row(): 58 | sc_prevPage_btn = gr.Button(value="Prev",scale=1, visible=True if shortcut_rows_per_page > 0 else False) 59 | sc_nextPage_btn = gr.Button(value="Next",scale=1, visible=True if shortcut_rows_per_page > 0 else False) 60 | 61 | sc_gallery = gr.Gallery(show_label=False, columns=shortcut_column, height="auto", object_fit=setting.gallery_thumbnail_image_style, allow_preview=False, value=thumb_list) 62 | 63 | with gr.Accordion("Search", open=search_open): 64 | shortcut_type = gr.Dropdown(label='Filter Model Type', multiselect=True, choices=[k for k in setting.ui_typenames], interactive=True) 65 | sc_search = gr.Textbox(label="Search", value="", placeholder="Search name, #tags, @personal note ....",interactive=True, lines=1) 66 | sc_classification_list = gr.Dropdown(label='Classification',info="The selection options of classification are subject to the AND operation.", multiselect=True, choices=classification.get_list(), interactive=True) 67 | shortcut_basemodel = gr.Dropdown(label='Filter Model BaseModel', multiselect=True, choices=[k for k in setting.model_basemodels.keys()], interactive=True) 68 | reset_filter_btn = gr.Button(value="Reset Filter",variant="primary") 69 | 70 | with gr.Row(visible=False): 71 | refresh_sc_browser = gr.Textbox() 72 | refresh_sc_gallery = gr.Textbox() 73 | sc_gallery_result = gr.State(thumb_list) 74 | sc_shortcut_column = gr.State(shortcut_column) 75 | sc_shortcut_rows_per_page = gr.State(shortcut_rows_per_page) 76 | 77 | refresh_sc_gallery.change(lambda x:x, sc_gallery_result, sc_gallery, show_progress=False) 78 | 79 | sc_gallery_page.release( 80 | fn = on_sc_gallery_page, 81 | inputs = [ 82 | shortcut_type, 83 | sc_search, 84 | shortcut_basemodel, 85 | sc_classification_list, 86 | show_downloaded_sc, 87 | sc_gallery_page, 88 | 89 | sc_shortcut_column, 90 | sc_shortcut_rows_per_page 91 | ], 92 | outputs=[ 93 | sc_gallery, 94 | sc_gallery_result 95 | ], 96 | show_progress=False 97 | ) 98 | 99 | refresh_sc_browser.change( 100 | fn=on_refresh_sc_list_change, 101 | inputs= [ 102 | shortcut_type, 103 | sc_search, 104 | shortcut_basemodel, 105 | sc_classification_list, 106 | show_downloaded_sc, 107 | sc_gallery_page, 108 | 109 | sc_shortcut_column, 110 | sc_shortcut_rows_per_page 111 | ], 112 | outputs=[ 113 | sc_gallery, 114 | sc_classification_list, 115 | sc_gallery_page, 116 | sc_gallery_result 117 | ], 118 | show_progress=False 119 | ) 120 | 121 | shortcut_type.change( 122 | fn=on_shortcut_gallery_refresh, 123 | inputs=[ 124 | shortcut_type, 125 | sc_search, 126 | shortcut_basemodel, 127 | sc_classification_list, 128 | show_downloaded_sc, 129 | 130 | sc_shortcut_column, 131 | sc_shortcut_rows_per_page 132 | ], 133 | outputs=[ 134 | sc_gallery, 135 | sc_gallery_page, 136 | sc_gallery_result 137 | ] 138 | ) 139 | 140 | sc_search.submit( 141 | fn=on_shortcut_gallery_refresh, 142 | inputs=[ 143 | shortcut_type, 144 | sc_search, 145 | shortcut_basemodel, 146 | sc_classification_list, 147 | show_downloaded_sc, 148 | 149 | sc_shortcut_column, 150 | sc_shortcut_rows_per_page 151 | ], 152 | outputs=[ 153 | sc_gallery, 154 | sc_gallery_page, 155 | sc_gallery_result 156 | ] 157 | ) 158 | 159 | shortcut_basemodel.change( 160 | fn=on_shortcut_gallery_refresh, 161 | inputs=[ 162 | shortcut_type, 163 | sc_search, 164 | shortcut_basemodel, 165 | sc_classification_list, 166 | show_downloaded_sc, 167 | 168 | sc_shortcut_column, 169 | sc_shortcut_rows_per_page 170 | ], 171 | outputs=[ 172 | sc_gallery, 173 | sc_gallery_page, 174 | sc_gallery_result 175 | ] 176 | ) 177 | 178 | show_downloaded_sc.change( 179 | fn=on_shortcut_gallery_refresh, 180 | inputs=[ 181 | shortcut_type, 182 | sc_search, 183 | shortcut_basemodel, 184 | sc_classification_list, 185 | show_downloaded_sc, 186 | 187 | sc_shortcut_column, 188 | sc_shortcut_rows_per_page 189 | ], 190 | outputs=[ 191 | sc_gallery, 192 | sc_gallery_page, 193 | sc_gallery_result 194 | ] 195 | ) 196 | 197 | sc_classification_list.change( 198 | fn=on_shortcut_gallery_refresh, 199 | inputs=[ 200 | shortcut_type, 201 | sc_search, 202 | shortcut_basemodel, 203 | sc_classification_list, 204 | show_downloaded_sc, 205 | 206 | sc_shortcut_column, 207 | sc_shortcut_rows_per_page 208 | ], 209 | outputs=[ 210 | sc_gallery, 211 | sc_gallery_page, 212 | sc_gallery_result 213 | ] 214 | ) 215 | 216 | reset_filter_btn.click( 217 | fn=on_reset_filter_btn_click, 218 | inputs=None, 219 | outputs=[ 220 | shortcut_type, 221 | sc_search, 222 | shortcut_basemodel, 223 | sc_classification_list, 224 | show_downloaded_sc, 225 | sc_gallery_page, 226 | 227 | refresh_sc_browser 228 | ], 229 | show_progress=False 230 | ) 231 | 232 | sc_prevPage_btn.click( 233 | fn = on_sc_prevPage_btn_click, 234 | inputs = [ 235 | shortcut_type, 236 | sc_search, 237 | shortcut_basemodel, 238 | sc_classification_list, 239 | show_downloaded_sc, 240 | sc_gallery_page, 241 | 242 | sc_shortcut_column, 243 | sc_shortcut_rows_per_page 244 | ], 245 | outputs=[ 246 | sc_gallery, 247 | sc_gallery_page, 248 | sc_gallery_result 249 | ], 250 | show_progress=False 251 | ) 252 | 253 | sc_nextPage_btn.click( 254 | fn = on_sc_nextPage_btn_click, 255 | inputs = [ 256 | shortcut_type, 257 | sc_search, 258 | shortcut_basemodel, 259 | sc_classification_list, 260 | show_downloaded_sc, 261 | sc_gallery_page, 262 | 263 | sc_shortcut_column, 264 | sc_shortcut_rows_per_page 265 | ], 266 | outputs=[ 267 | sc_gallery, 268 | sc_gallery_page, 269 | sc_gallery_result 270 | ], 271 | show_progress=False 272 | ) 273 | 274 | return sc_gallery, refresh_sc_browser, refresh_sc_gallery 275 | 276 | def on_reset_filter_btn_click(): 277 | current_time = datetime.datetime.now() 278 | return gr.update(value=[]), gr.update(value=None), gr.update(value=[]), gr.update(value=[]), gr.update(value=ALL_DOWNLOADED_MODEL),gr.update(value=1), current_time 279 | 280 | def get_thumbnail_list(shortcut_types=None, downloaded_sc=False, search=None, shortcut_basemodels=None, sc_classifications=None, page = 0, columns=0, rows=0): 281 | 282 | total = 0 283 | max_page = 1 284 | shortcut_list = ishortcut.get_image_list(shortcut_types, search, shortcut_basemodels, sc_classifications) 285 | shortlist = None 286 | result = None 287 | 288 | if not shortcut_list: 289 | return None, total, max_page 290 | 291 | if downloaded_sc == DOWNLOADED_MODEL: 292 | if model.Downloaded_Models: 293 | downloaded_list = list() 294 | for short in shortcut_list: 295 | mid = short['id'] 296 | if str(mid) in model.Downloaded_Models.keys(): 297 | downloaded_list.append(short) 298 | shortcut_list = downloaded_list 299 | else: 300 | shortcut_list = None 301 | elif downloaded_sc == NOT_DOWNLOADED_MODEL: 302 | if model.Downloaded_Models: 303 | downloaded_list = list() 304 | for short in shortcut_list: 305 | mid = short['id'] 306 | if str(mid) not in model.Downloaded_Models.keys(): 307 | downloaded_list.append(short) 308 | shortcut_list = downloaded_list 309 | 310 | # if downloaded_sc: 311 | # if model.Downloaded_Models: 312 | # downloaded_list = list() 313 | # for short in shortcut_list: 314 | # mid = short['id'] 315 | # if str(mid) in model.Downloaded_Models.keys(): 316 | # downloaded_list.append(short) 317 | # shortcut_list = downloaded_list 318 | # else: 319 | # shortcut_list = None 320 | 321 | if shortcut_list: 322 | total = len(shortcut_list) 323 | 324 | # name 기준으로 정렬 325 | shortcut_list = sorted(shortcut_list, key=lambda x: x["name"].lower().strip(), reverse=False) 326 | 327 | # 등록일 기준으로 정렬 328 | # strptime str을 datetime 형식으로 변환(스트링과 형식일치해야함) 329 | # strftime datetime 형식을 str로 변환(스트링과 형식이 일치할필요는 없다.) 330 | # shortcut_list = sorted(shortcut_list, key=lambda x: datetime.datetime.strptime(x["date"], '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d'), reverse=True) 331 | # shortcut_list = sorted(shortcut_list, key=lambda x: x["date"], reverse=True) 332 | # shortcut_list = sorted(shortcut_list, key=lambda x: (x["date"], x['name']) , reverse=True) 333 | 334 | shortlist = shortcut_list 335 | 336 | if total > 0: 337 | # page 즉 페이징이 아닌 전체가 필요할때도 총페이지 수를 구할때도 있으므로.. 338 | # page == 0 은 전체 리스트를 반환한다 339 | shortcut_count_per_page = columns * rows 340 | if shortcut_count_per_page > 0: 341 | max_page = math.ceil(total / shortcut_count_per_page) 342 | 343 | if page > max_page: 344 | page = max_page 345 | 346 | if page > 0 and shortcut_count_per_page > 0: 347 | item_start = shortcut_count_per_page * (page - 1) 348 | item_end = (shortcut_count_per_page * page) 349 | if total < item_end: 350 | item_end = total 351 | shortlist = shortcut_list[item_start:item_end] 352 | 353 | if shortlist: 354 | result = list() 355 | # 썸네일이 있는지 판단해서 대체 이미지 작업 356 | for v in shortlist: 357 | if v: 358 | if ishortcut.is_sc_image(v['id']): 359 | if 'nsfw' in v.keys() and bool(v['nsfw']) and setting.NSFW_filtering_enable: 360 | result.append((setting.nsfw_disable_image,setting.set_shortcutname(v['name'],v['id']))) 361 | else: 362 | result.append((os.path.join(setting.shortcut_thumbnail_folder,f"{v['id']}{setting.preview_image_ext}"),setting.set_shortcutname(v['name'],v['id']))) 363 | else: 364 | result.append((setting.no_card_preview_image,setting.set_shortcutname(v['name'],v['id']))) 365 | 366 | return result, total, max_page 367 | 368 | def on_refresh_sc_list_change(sc_types, sc_search, sc_basemodels, sc_classifications, show_downloaded_sc, sc_page, columns, rows): 369 | 370 | thumb_list , thumb_totals, thumb_max_page = get_thumbnail_list(sc_types,show_downloaded_sc,sc_search,sc_basemodels,sc_classifications,sc_page, columns, rows) 371 | 372 | # 현재 페이지가 최대 페이지보다 크면 (최대 페이지를 현재 페이지로 넣고)다시한번 리스트를 구한다. 373 | if thumb_max_page < sc_page: 374 | sc_page = thumb_max_page 375 | thumb_list , thumb_totals, thumb_max_page = get_thumbnail_list(sc_types,show_downloaded_sc,sc_search,sc_basemodels,sc_classifications,sc_page, columns, rows) 376 | 377 | return gr.update(value=thumb_list),gr.update(choices=classification.get_list()),gr.update(minimum=1, maximum=thumb_max_page, value=sc_page, step=1, label=f"Total {thumb_max_page} Pages"),thumb_list 378 | 379 | def on_shortcut_gallery_refresh(sc_types, sc_search, sc_basemodels, sc_classifications, show_downloaded_sc, columns, rows): 380 | thumb_list , thumb_totals, thumb_max_page = get_thumbnail_list(sc_types,show_downloaded_sc,sc_search,sc_basemodels,sc_classifications,1, columns, rows) 381 | return gr.update(value=thumb_list),gr.update(minimum=1, maximum=thumb_max_page, value=1, step=1, label=f"Total {thumb_max_page} Pages"),thumb_list 382 | 383 | def on_sc_gallery_page(sc_types, sc_search, sc_basemodels, sc_classifications, show_downloaded_sc, sc_page, columns, rows): 384 | thumb_list , thumb_totals, thumb_max_page = get_thumbnail_list(sc_types,show_downloaded_sc,sc_search,sc_basemodels,sc_classifications,sc_page, columns, rows) 385 | return gr.update(value=thumb_list),thumb_list 386 | 387 | def on_sc_nextPage_btn_click(sc_types, sc_search, sc_basemodels, sc_classifications, show_downloaded_sc, sc_page, columns, rows): 388 | sc_page = sc_page + 1 389 | thumb_list , thumb_totals, thumb_max_page = get_thumbnail_list(sc_types,show_downloaded_sc,sc_search,sc_basemodels,sc_classifications,sc_page, columns, rows) 390 | 391 | if sc_page > thumb_max_page: 392 | sc_page = thumb_max_page 393 | 394 | return gr.update(value=thumb_list),sc_page,thumb_list 395 | 396 | def on_sc_prevPage_btn_click(sc_types, sc_search, sc_basemodels, sc_classifications, show_downloaded_sc, sc_page, columns, rows): 397 | sc_page = sc_page - 1 398 | if sc_page < 1: 399 | sc_page = 1 400 | thumb_list , thumb_totals, thumb_max_page = get_thumbnail_list(sc_types,show_downloaded_sc,sc_search,sc_basemodels,sc_classifications,sc_page, columns, rows) 401 | return gr.update(value=thumb_list),sc_page,thumb_list -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/scan_action.py: -------------------------------------------------------------------------------- 1 | import os 2 | import gradio as gr 3 | import datetime 4 | import requests 5 | import shutil 6 | import json 7 | 8 | from . import util 9 | from . import model 10 | from . import setting 11 | from . import civitai 12 | 13 | from . import ishortcut 14 | from . import ishortcut_action 15 | 16 | def on_scan_ui(): 17 | with gr.Column(): 18 | with gr.Row(): 19 | with gr.Accordion("Scan models for Civitai", open=True): 20 | with gr.Row(): 21 | with gr.Column(): 22 | fix_information_filename = gr.Checkbox(label="Fix version information filename", value=False , visible=False) 23 | scan_models_btn = gr.Button(value="Scan Models",variant="primary") 24 | gr.Markdown(value="This feature targets models that do not have information files available in the saved models. It calculates the hash value and searches for the model in Civitai, registering it as a shortcut. Calculating the hash value can take a significant amount of time.", visible=True) 25 | with gr.Box(elem_classes="cs_box", visible=False) as scanned_result: 26 | with gr.Column(): 27 | scan_models_result = gr.CheckboxGroup(visible=True, container=True, label="Scanned Model List") 28 | with gr.Row(): 29 | unselect_scan_models_result_btn = gr.Button(value="Unselect All",variant="primary") 30 | clear_scan_models_result_btn = gr.Button(value="Clear Results",variant="primary") 31 | with gr.Row(visible=False) as update_information: 32 | with gr.Column(): 33 | with gr.Row(): 34 | with gr.Column(scale=1): 35 | scan_register_shortcut = gr.Checkbox(label="Register a shortcut when creating the model information file.", value=True) 36 | with gr.Column(scale=1): 37 | with gr.Row(): 38 | scan_save_modelfolder = gr.Checkbox(label="Create a model folder corresponding to the model type.", value=False) 39 | scan_save_vsfolder = gr.Checkbox(label="Create individual model version folder.", value=False, interactive=False) 40 | with gr.Row(): 41 | with gr.Column(): 42 | create_models_info_btn = gr.Button(value="Create Model Information",variant="primary") 43 | with gr.Row(): 44 | with gr.Accordion("Update Shortcuts", open=True): 45 | with gr.Row(): 46 | with gr.Column(): 47 | scan_to_shortcut_btn = gr.Button(value="Scan downloaded models for shortcut registration",variant="primary") 48 | scan_progress = gr.Markdown(value="This feature scans for models that have information files available and registers a shortcut for them, downloading any necessary images in the process. If there is no information available for a particular model, please use the 'Scan Models' feature.", visible=True) 49 | 50 | with gr.Row(): 51 | with gr.Column(): 52 | update_all_shortcuts_btn = gr.Button(value="Update the model information for the shortcut",variant="primary") 53 | update_progress = gr.Markdown(value="This feature updates registered shortcuts with the latest information and downloads any new images if available.", visible=True) 54 | with gr.Row(): 55 | with gr.Accordion("Update Downloaded Model", open=True): 56 | with gr.Row(): 57 | with gr.Column(): 58 | update_lora_meta_for_downloaded_model_btn = gr.Button(value="Create a Lora metadata file for a downloaded model without Lora metadata file.",variant="primary") 59 | update_lora_meta_progress = gr.Markdown(value="This feature generates a Lora metadata file for a downloaded model without Lora metadata file.", visible=True) 60 | 61 | unselect_scan_models_result_btn.click( 62 | fn=on_unselect_scan_models_result_btn_click, 63 | inputs=None, 64 | outputs=[ 65 | scan_models_result 66 | ] 67 | ) 68 | 69 | clear_scan_models_result_btn.click( 70 | fn=on_clear_scan_models_result_btn_click, 71 | inputs=None, 72 | outputs=[ 73 | scan_models_result, 74 | scanned_result, 75 | update_information, 76 | ] 77 | ) 78 | 79 | scan_save_modelfolder.change( 80 | fn=on_scan_save_modelfolder_change, 81 | inputs=[ 82 | scan_save_modelfolder 83 | ], 84 | outputs=[ 85 | scan_save_vsfolder 86 | ] 87 | ) 88 | 89 | create_models_info_btn.click( 90 | fn=on_create_models_info_btn_click, 91 | inputs=[ 92 | scan_models_result, 93 | scan_save_modelfolder, 94 | scan_save_vsfolder, 95 | scan_register_shortcut 96 | ], 97 | outputs=[ 98 | scan_models_result, 99 | scanned_result, 100 | update_information 101 | ] 102 | ) 103 | 104 | scan_models_btn.click( 105 | fn=on_scan_models_btn_click, 106 | inputs=[fix_information_filename], 107 | outputs=[ 108 | scan_models_result, 109 | scanned_result, 110 | update_information, 111 | scan_save_modelfolder, 112 | scan_save_vsfolder 113 | ] 114 | ) 115 | 116 | update_all_shortcuts_btn.click( 117 | fn=on_update_all_shortcuts_btn_click, 118 | inputs=None, 119 | outputs=[ 120 | update_progress, 121 | ] 122 | ) 123 | 124 | scan_to_shortcut_btn.click( 125 | fn=on_scan_to_shortcut_click, 126 | inputs=None, 127 | outputs=[ 128 | scan_progress, 129 | ] 130 | ) 131 | 132 | update_lora_meta_for_downloaded_model_btn.click( 133 | fn=on_update_lora_meta_for_downloaded_model_btn_click, 134 | inputs=None, 135 | outputs=[update_lora_meta_progress] 136 | ) 137 | 138 | def on_unselect_scan_models_result_btn_click(): 139 | return gr.update(value=[], interactive=True) 140 | 141 | def on_clear_scan_models_result_btn_click(): 142 | return gr.update(choices=[], value=[], interactive=True),gr.update(visible=False),gr.update(visible=False) 143 | 144 | def create_models_information(files, mfolder, vs_folder, register_shortcut, progress=gr.Progress()): 145 | 146 | non_list = list() 147 | if not files: 148 | return None 149 | 150 | for file_path in progress.tqdm(files, desc=f"Create Models Information"): 151 | if os.path.isfile(file_path): 152 | util.printD(f"Generate SHA256: {file_path}") 153 | hash = util.calculate_sha256(file_path) 154 | version_info = civitai.get_version_info_by_hash(hash) 155 | 156 | if not version_info: 157 | # These models are not registered with Civitai. 158 | non_list.append(file_path) 159 | continue 160 | 161 | vfolder , vfile = os.path.split(file_path) 162 | basename , ext = os.path.splitext(vfile) 163 | 164 | # 저장할 폴더 생성 165 | if mfolder: 166 | model_folder = util.make_download_model_folder(version_info, True, vs_folder) 167 | # 다정하면 임의의 분류뒤에 모델폴더를 생성하고 그뒤에 버전까지 생성가능 168 | # model_folder = make_download_model_folder(version_info, ms_folder=True, vs_folder=True, vs_foldername=None, cs_foldername=None): 169 | # model_folder = util.make_version_folder(version_info, vs_folder) 170 | else: 171 | model_folder = vfolder 172 | 173 | # version info file name 으로 교체시 174 | # savefile_base = downloader.get_save_base_name(version_info) 175 | # basename = savefile_base 176 | # destination = os.path.join(model_folder, f"{basename}{ext}") 177 | 178 | # save info 179 | info_path = os.path.join(model_folder, f"{basename}{setting.info_suffix}{setting.info_ext}") 180 | result = civitai.write_version_info(info_path, version_info) 181 | if result: 182 | util.printD(f"Wrote version info : {info_path}") 183 | 184 | # save preview 185 | if "images" in version_info.keys(): 186 | description_img = os.path.join(model_folder, f"{basename}{setting.preview_image_suffix}{setting.preview_image_ext}") 187 | try: 188 | img_dict = version_info["images"][0] 189 | if "url" in img_dict: 190 | img_url = img_dict["url"] 191 | if "width" in img_dict: 192 | if img_dict["width"]: 193 | img_url = util.change_width_from_image_url(img_url, img_dict["width"]) 194 | # get image 195 | with requests.get(img_url, stream=True) as img_r: 196 | if not img_r.ok: 197 | util.printD("Get error code: " + str(img_r.status_code)) 198 | return 199 | 200 | with open(description_img, 'wb') as f: 201 | img_r.raw.decode_content = True 202 | shutil.copyfileobj(img_r.raw, f) 203 | util.printD(f"Downloaded preview image : {description_img}") 204 | except Exception as e: 205 | pass 206 | 207 | # 파일 이동 208 | if mfolder: 209 | destination = os.path.join(model_folder, vfile) 210 | if file_path != destination: 211 | if not os.path.isfile(destination): 212 | os.rename(file_path, destination) 213 | else: 214 | util.printD(f"The target file already exists : target {destination}") 215 | 216 | # 숏컷 추가 217 | if register_shortcut: 218 | if version_info['modelId']: 219 | ishortcut.update_shortcut(version_info['modelId'], progress) 220 | model.update_downloaded_model() 221 | 222 | return non_list 223 | 224 | def is_filename_in_version_info_in_directory(directory, filename): 225 | 226 | file_list = [] 227 | for file in os.listdir(directory): 228 | if file.endswith(f"{setting.info_suffix}{setting.info_ext}"): 229 | file_list.append(os.path.join(directory,file)) 230 | 231 | if not file_list: 232 | return False 233 | 234 | for file in file_list: 235 | try: 236 | with open(file, 'r') as f: 237 | json_data = json.load(f) 238 | if "files" in json_data.keys(): 239 | files = json_data['files'] 240 | for file in files: 241 | if file['name'] == filename: 242 | return True 243 | except: 244 | pass 245 | 246 | return False 247 | 248 | def scan_models(fix_information_filename, progress=gr.Progress()): 249 | root_dirs = list(set(setting.model_folders.values())) 250 | file_list = util.search_file(root_dirs,None,setting.model_exts) 251 | 252 | result = list() 253 | 254 | if fix_information_filename: 255 | # fix_version_information_filename() 256 | pass 257 | 258 | for file_path in progress.tqdm(file_list, desc=f"Scan Models for Civitai"): 259 | 260 | vfolder , vfile = os.path.split(file_path) 261 | basename , ext = os.path.splitext(vfile) 262 | info = os.path.join(vfolder, f"{basename}{setting.info_suffix}{setting.info_ext}") 263 | 264 | if not os.path.isfile(info): 265 | # result.append(file_path) 266 | if not is_filename_in_version_info_in_directory(vfolder, vfile): 267 | # util.printD(f"{file_path} : {vfile}: no info") 268 | result.append(file_path) 269 | 270 | return result 271 | 272 | # def fix_version_information_filename(): 273 | # root_dirs = list(set(setting.model_folders.values())) 274 | # file_list = util.search_file(root_dirs,None,[setting.info_ext]) 275 | 276 | # version_info = None 277 | # if not file_list: 278 | # return 279 | 280 | # for file_path in file_list: 281 | 282 | # try: 283 | # with open(file_path, 'r') as f: 284 | # json_data = json.load(f) 285 | 286 | # if 'id' in json_data.keys(): 287 | # version_info = json_data 288 | 289 | # file_path = file_path.strip() 290 | # vfolder , vfile = os.path.split(file_path) 291 | # savefile_base = downloader.get_save_base_name(version_info) 292 | # info_file = os.path.join(vfolder, f"{util.replace_filename(savefile_base)}{setting.info_suffix}{setting.info_ext}") 293 | 294 | # if file_path != info_file: 295 | # if not os.path.isfile(info_file): 296 | # os.rename(file_path, info_file) 297 | 298 | # except: 299 | # pass 300 | 301 | def on_create_models_info_btn_click(files, mfolder, vsfolder, register_shortcut, progress=gr.Progress()): 302 | remain_files = create_models_information(files,mfolder,vsfolder,register_shortcut, progress) 303 | if remain_files and len(remain_files) > 0: 304 | return gr.update(choices=remain_files, value=remain_files, interactive=True, label="These models are not registered with Civitai."),gr.update(visible=True),gr.update(visible=True) 305 | return gr.update(choices=[], value=[], interactive=True),gr.update(visible=False),gr.update(visible=False) 306 | 307 | def on_scan_models_btn_click(fix_information_filename, progress=gr.Progress()): 308 | files = scan_models(fix_information_filename, progress) 309 | return gr.update(choices=files,value=files,interactive=True,label="Scanned Model List"),gr.update(visible=True),gr.update(visible=True),gr.update(value=False, interactive=True),gr.update(value=False, interactive=False) 310 | 311 | def on_scan_to_shortcut_click(progress=gr.Progress()): 312 | model.update_downloaded_model() 313 | ishortcut_action.scan_downloadedmodel_to_shortcut(progress) 314 | return gr.update(visible=True) 315 | 316 | def on_update_all_shortcuts_btn_click(progress=gr.Progress()): 317 | ishortcut.update_all_shortcut_informations(progress) 318 | return gr.update(visible=True) 319 | 320 | def on_scan_save_modelfolder_change(scan_save_modelfolder): 321 | if scan_save_modelfolder: 322 | return gr.update(interactive=True) 323 | return gr.update(value=False, interactive=False) 324 | 325 | def on_update_lora_meta_for_downloaded_model_btn_click(progress=gr.Progress()): 326 | model.update_downloaded_model() 327 | update_lora_meta(progress) 328 | return gr.update(visible=True) 329 | 330 | def update_lora_meta(progress=gr.Progress()): 331 | 332 | for file_path, version_id in progress.tqdm(model.Downloaded_InfoPath.items(), desc=f"Create Lora metadata file for Downloaded Model"): 333 | vfolder , vfile = os.path.split(file_path) 334 | basename , ext = os.path.splitext(vfile) 335 | basename , ext = os.path.splitext(basename) 336 | metafile = os.path.join(vfolder, f"{basename}.json") 337 | 338 | if not os.path.isfile(metafile): 339 | civitai.write_LoRa_metadata_by_version_id(metafile, str(version_id)) 340 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/setting.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import shutil 4 | 5 | from modules import scripts, script_callbacks, shared 6 | from . import util 7 | 8 | root_path = os.getcwd() 9 | extension_base = scripts.basedir() 10 | # extension_base = os.path.join("extensions","civitai-shortcut") 11 | 12 | headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.68', 13 | "Authorization": ""} 14 | 15 | civitai_api_key = "" 16 | Extensions_Name = "Civitai Shortcut" 17 | Extensions_Version = "v1.6.7" 18 | 19 | PLACEHOLDER = "[No Select]" 20 | NORESULT = "[No Result]" 21 | NEWRECIPE = "[New Prompt Recipe]" 22 | NEWCLASSIFICATION = "[New Classification]" 23 | 24 | CREATE_MODEL_FOLDER = "Create a model folder to download the model" 25 | # CREATE_MODEL_FOLDER = "Create a model folder with the model name" 26 | 27 | model_exts = (".bin", ".pt", ".safetensors", ".ckpt") 28 | 29 | # sd_version = ['SD1', 'SD2', 'SDXL', 'Unknown'] 30 | model_basemodels = { 31 | "SD 1.4":"SD1", 32 | "SD 1.5":"SD1", 33 | "SD 2.0":"SD2", 34 | "SD 2.0 768":"SD2", 35 | "SD 2.1":"SD2", 36 | "SD 2.1 768":"SD2", 37 | "SD 2.1 Unclip":"SD2", 38 | "SDXL 0.9":"SDXL", 39 | "SDXL 1.0":"SDXL", 40 | 41 | "SDXL 1.0 LCM":"SDXL", 42 | "SDXL Distilled":"SDXL", 43 | "SDXL Turbo":"SDXL", 44 | "SDXL Lightning":"SDXL", 45 | "Pony":"Pony", 46 | "SVD":"SVD", 47 | "SVD XT":"SVD", 48 | "Stable Cascade":"SC", 49 | "Playground V2":"PGV2", 50 | "PixArt A":"PixArtA", 51 | 52 | "Other":"Unknown" 53 | } 54 | 55 | # civitai model type -> folder path 56 | model_folders = { 57 | 'Checkpoint': os.path.join("models","Stable-diffusion"), 58 | 'LORA': os.path.join("models","Lora"), 59 | 'LoCon': os.path.join("models","LyCORIS"), 60 | 'TextualInversion': os.path.join("embeddings"), 61 | 'Hypernetwork': os.path.join("models","hypernetworks"), 62 | 'AestheticGradient': os.path.join("extensions","stable-diffusion-webui-aesthetic-gradients","aesthetic_embeddings"), 63 | 'Controlnet': os.path.join("models","ControlNet"), 64 | 'Poses': os.path.join("models","Poses"), 65 | 'Wildcards': os.path.join("extensions","sd-dynamic-prompts","wildcards"), 66 | 'Other': os.path.join("models","Other"), 67 | 68 | 'VAE': os.path.join("models","VAE"), 69 | 'ANLORA': os.path.join("extensions","sd-webui-additional-networks","models","lora"), 70 | 'Unknown': os.path.join("models","Unkonwn"), 71 | } 72 | 73 | # UI 쪽에서 변환할때 쓰인다. 74 | # UI model type -> civitai model type 75 | 76 | # UI type 하나에 다중의 civitai type을 대입할때 대상이 되는것은 get_ui_typename 함수와 ishortcut->get_image_list 와 get_list 뿐이다. 77 | # 나머지는 key로만 쓰이기 때문에 value 값이 배열이라 해도문제가 안될듯한다. 78 | # ishortcut 부분은 여기를 79 | # tmp_types.append(setting.ui_typenames[sc_type]) 80 | # -> 81 | # for type_name in setting.ui_typenames[sc_type]: 82 | # tmp_types.append(type_name) 83 | # 이리 하면 될듯 84 | 85 | # get_ui_typename는 이렇게 수정해도 문제 없을것 같다. 대신 모두 "" : ["",""] 형식으로 바꿔야 할듯(안해도 되나?) 86 | # def get_ui_typename(model_type): 87 | # for k,v in ui_typenames.items(): 88 | # if model_type in v: 89 | # return k 90 | # return model_type 91 | 92 | ui_typenames = { 93 | "Checkpoint" : 'Checkpoint', 94 | "LoRA" : 'LORA', 95 | "LyCORIS" : 'LoCon', 96 | "Textual Inversion" : 'TextualInversion', 97 | "Hypernetwork" : 'Hypernetwork', 98 | "Aesthetic Gradient" : 'AestheticGradient', 99 | "Controlnet" : 'Controlnet', 100 | "Poses" : 'Poses', 101 | "Wildcards" : 'Wildcards', 102 | "Other" : 'Other', 103 | } 104 | 105 | #information tab 106 | civitai_information_tab = 0 107 | usergal_information_tab = 1 108 | download_information_tab = 2 109 | 110 | # civitai helper 호환성 111 | info_ext = ".info" 112 | info_suffix = ".civitai" 113 | 114 | triger_ext = ".txt" 115 | triger_suffix = ".triger" 116 | 117 | preview_image_ext = ".png" 118 | preview_image_suffix = ".preview" 119 | 120 | # 임시설정 121 | 122 | # 갤러리 height 설정 123 | information_gallery_height = "auto" # auto , fit 124 | 125 | # 화면 분할 비율 126 | shortcut_browser_screen_split_ratio = 3 127 | shortcut_browser_screen_split_ratio_max = 10 128 | 129 | shortcut_browser_search_up = False 130 | 131 | # 갤러리 ui설정 132 | # model browser 설정 133 | shortcut_column = 5 134 | shortcut_rows_per_page = 4 135 | gallery_column = 7 136 | 137 | # 유저 갤러리 설정 138 | usergallery_images_column = 6 139 | usergallery_images_rows_per_page = 2 140 | 141 | # prompt recipe 설정 142 | prompt_shortcut_column = 5 143 | prompt_shortcut_rows_per_page = 4 144 | prompt_reference_shortcut_column = 8 145 | prompt_reference_shortcut_rows_per_page = 4 146 | 147 | # classification 설정 148 | classification_shortcut_column = 5 149 | classification_shortcut_rows_per_page = 4 150 | 151 | classification_gallery_column = 8 152 | classification_gallery_rows_per_page = 4 153 | 154 | shortcut_max_download_image_per_version = 0 # 버전당 최대 다운로드 이미지 수 , 0이면 전체다운 받는다 155 | gallery_thumbnail_image_style = "scale-down" 156 | 157 | # 다운로드 설정 158 | download_images_folder = os.path.join("outputs","download-images") 159 | 160 | # background thread 설정 161 | # shortcut_auto_update = True 162 | shortcut_update_when_start = True 163 | usergallery_preloading = False 164 | 165 | # 생성되는 폴더 및 파일 166 | shortcut = "CivitaiShortCut.json" 167 | shortcut_setting = "CivitaiShortCutSetting.json" 168 | shortcut_classification = "CivitaiShortCutClassification.json" 169 | shortcut_civitai_internet_shortcut_url = "CivitaiShortCutBackupUrl.json" 170 | shortcut_recipe = "CivitaiShortCutRecipeCollection.json" 171 | 172 | # shortcut_thumbnail_folder = "sc_thumb" 173 | shortcut_thumbnail_folder = "sc_thumb_images" 174 | shortcut_recipe_folder = "sc_recipes" 175 | shortcut_info_folder = "sc_infos" 176 | shortcut_gallery_folder = "sc_gallery" 177 | 178 | no_card_preview_image = os.path.join(extension_base,"img","card-no-preview.png") 179 | nsfw_disable_image = os.path.join(extension_base,"img","nsfw-no-preview.png") 180 | 181 | NSFW_filtering_enable = True 182 | # NSFW_level = { "None":True, "Soft":False, "Mature":False, "X":False } # None, Soft, Mature, X 183 | NSFW_levels = ("None","Soft","Mature","X","XX") # None, Soft, Mature, X 184 | NSFW_level_user = "None" 185 | 186 | shortcut_env = dict() 187 | 188 | def set_NSFW(enable, level="None"): 189 | # global NSFW_level 190 | global NSFW_filtering_enable 191 | global NSFW_level_user 192 | 193 | NSFW_filtering_enable = enable 194 | NSFW_level_user = level 195 | 196 | def save_NSFW(): 197 | global NSFW_filtering_enable 198 | global NSFW_level_user 199 | 200 | environment = load() 201 | if not environment: 202 | environment = dict() 203 | 204 | nsfw_filter = dict() 205 | nsfw_filter['nsfw_filter_enable'] = NSFW_filtering_enable 206 | nsfw_filter['nsfw_level'] = NSFW_level_user 207 | environment['NSFW_filter'] = nsfw_filter 208 | 209 | save(environment) 210 | 211 | def init(): 212 | global extension_base 213 | 214 | global shortcut 215 | global shortcut_setting 216 | global shortcut_classification 217 | global shortcut_civitai_internet_shortcut_url 218 | global shortcut_recipe 219 | 220 | global shortcut_thumbnail_folder 221 | global shortcut_recipe_folder 222 | global shortcut_info_folder 223 | global shortcut_gallery_folder 224 | 225 | shortcut = os.path.join(extension_base,shortcut) 226 | shortcut_setting = os.path.join(extension_base,shortcut_setting) 227 | shortcut_classification = os.path.join(extension_base,shortcut_classification) 228 | shortcut_recipe = os.path.join(extension_base,shortcut_recipe) 229 | shortcut_civitai_internet_shortcut_url = os.path.join(extension_base,shortcut_civitai_internet_shortcut_url) 230 | 231 | shortcut_thumbnail_folder = os.path.join(extension_base,shortcut_thumbnail_folder) 232 | shortcut_recipe_folder = os.path.join(extension_base,shortcut_recipe_folder) 233 | shortcut_info_folder = os.path.join(extension_base,shortcut_info_folder) 234 | shortcut_gallery_folder = os.path.join(extension_base,shortcut_gallery_folder) 235 | 236 | load_data() 237 | 238 | def load_data(): 239 | global model_folders 240 | 241 | global shortcut_column 242 | global shortcut_rows_per_page 243 | global gallery_column 244 | global classification_shortcut_column 245 | global classification_shortcut_rows_per_page 246 | global classification_gallery_column 247 | global classification_gallery_rows_per_page 248 | global usergallery_images_column 249 | global usergallery_images_rows_per_page 250 | 251 | global prompt_shortcut_column 252 | global prompt_shortcut_rows_per_page 253 | global prompt_reference_shortcut_column 254 | global prompt_reference_shortcut_rows_per_page 255 | 256 | global shortcut_max_download_image_per_version 257 | global gallery_thumbnail_image_style 258 | global shortcut_browser_search_up 259 | 260 | global download_images_folder 261 | global shortcut_browser_screen_split_ratio 262 | global information_gallery_height 263 | 264 | global shortcut_update_when_start 265 | global civitai_api_key 266 | 267 | if shared.cmd_opts.embeddings_dir: 268 | model_folders['TextualInversion'] = shared.cmd_opts.embeddings_dir 269 | 270 | if shared.cmd_opts.hypernetwork_dir : 271 | model_folders['Hypernetwork'] = shared.cmd_opts.hypernetwork_dir 272 | 273 | if shared.cmd_opts.ckpt_dir: 274 | model_folders['Checkpoint'] = shared.cmd_opts.ckpt_dir 275 | 276 | if shared.cmd_opts.lora_dir: 277 | model_folders['LORA'] = shared.cmd_opts.lora_dir 278 | 279 | environment = load() 280 | if environment: 281 | if "NSFW_filter" in environment.keys(): 282 | nsfw_filter = environment['NSFW_filter'] 283 | filtering_enable = True 284 | if 'nsfw_filter_enable' in nsfw_filter.keys(): 285 | filtering_enable = bool(nsfw_filter['nsfw_filter_enable']) 286 | 287 | if 'nsfw_level' in nsfw_filter.keys(): 288 | set_NSFW(filtering_enable, nsfw_filter['nsfw_level']) 289 | 290 | if "application_allow" in environment.keys(): 291 | application_allow = environment['application_allow'] 292 | 293 | if "civitai_api_key" in application_allow.keys(): 294 | civitai_api_key = application_allow['civitai_api_key'] 295 | if "shortcut_update_when_start" in application_allow.keys(): 296 | shortcut_update_when_start = bool(application_allow['shortcut_update_when_start']) 297 | if "shortcut_max_download_image_per_version" in application_allow.keys(): 298 | shortcut_max_download_image_per_version = int(application_allow['shortcut_max_download_image_per_version']) 299 | 300 | if "screen_style" in environment.keys(): 301 | screen_style = environment['screen_style'] 302 | 303 | if "shortcut_browser_screen_split_ratio" in screen_style.keys(): 304 | shortcut_browser_screen_split_ratio = int(screen_style['shortcut_browser_screen_split_ratio']) 305 | if "information_gallery_height" in screen_style.keys(): 306 | if screen_style['information_gallery_height'].strip(): 307 | information_gallery_height = screen_style['information_gallery_height'] 308 | if "gallery_thumbnail_image_style" in screen_style.keys(): 309 | gallery_thumbnail_image_style = screen_style['gallery_thumbnail_image_style'] 310 | if "shortcut_browser_search_up" in screen_style.keys(): 311 | shortcut_browser_search_up = bool(screen_style['shortcut_browser_search_up']) 312 | 313 | if "image_style" in environment.keys(): 314 | image_style = environment['image_style'] 315 | 316 | if "shortcut_column" in image_style.keys(): 317 | shortcut_column = int(image_style['shortcut_column']) 318 | if "shortcut_rows_per_page" in image_style.keys(): 319 | shortcut_rows_per_page = int(image_style['shortcut_rows_per_page']) 320 | 321 | if "gallery_column" in image_style.keys(): 322 | gallery_column = int(image_style['gallery_column']) 323 | 324 | if "classification_shortcut_column" in image_style.keys(): 325 | classification_shortcut_column = int(image_style['classification_shortcut_column']) 326 | if "classification_shortcut_rows_per_page" in image_style.keys(): 327 | classification_shortcut_rows_per_page = int(image_style['classification_shortcut_rows_per_page']) 328 | if "classification_gallery_column" in image_style.keys(): 329 | classification_gallery_column = int(image_style['classification_gallery_column']) 330 | if "classification_gallery_rows_per_page" in image_style.keys(): 331 | classification_gallery_rows_per_page = int(image_style['classification_gallery_rows_per_page']) 332 | 333 | if "usergallery_images_column" in image_style.keys(): 334 | usergallery_images_column = int(image_style['usergallery_images_column']) 335 | if "usergallery_images_rows_per_page" in image_style.keys(): 336 | usergallery_images_rows_per_page = int(image_style['usergallery_images_rows_per_page']) 337 | 338 | if "prompt_shortcut_column" in image_style.keys(): 339 | prompt_shortcut_column = int(image_style['prompt_shortcut_column']) 340 | if "prompt_shortcut_rows_per_page" in image_style.keys(): 341 | prompt_shortcut_rows_per_page = int(image_style['prompt_shortcut_rows_per_page']) 342 | if "prompt_reference_shortcut_column" in image_style.keys(): 343 | prompt_reference_shortcut_column = int(image_style['prompt_reference_shortcut_column']) 344 | if "prompt_reference_shortcut_rows_per_page" in image_style.keys(): 345 | prompt_reference_shortcut_rows_per_page = int(image_style['prompt_reference_shortcut_rows_per_page']) 346 | 347 | if "model_folders" in environment.keys(): 348 | 349 | user_folders = environment['model_folders'] 350 | 351 | if 'LoCon' in user_folders.keys(): 352 | model_folders['LoCon'] = user_folders['LoCon'] 353 | 354 | if 'Wildcards' in user_folders.keys(): 355 | model_folders['Wildcards'] = user_folders['Wildcards'] 356 | 357 | if 'Controlnet' in user_folders.keys(): 358 | model_folders['Controlnet'] = user_folders['Controlnet'] 359 | 360 | if 'AestheticGradient' in user_folders.keys(): 361 | model_folders['AestheticGradient'] = user_folders['AestheticGradient'] 362 | 363 | if 'Poses' in user_folders.keys(): 364 | model_folders['Poses'] = user_folders['Poses'] 365 | 366 | if 'Other' in user_folders.keys(): 367 | model_folders['Other'] = user_folders['Other'] 368 | 369 | if "download_folders" in environment.keys(): 370 | 371 | download_folders = environment['download_folders'] 372 | if 'download_images' in download_folders.keys(): 373 | download_images_folder = download_folders['download_images'] 374 | 375 | if "temporary" in environment.keys(): 376 | temporary = environment['temporary'] 377 | 378 | def generate_type_basefolder(content_type): 379 | 380 | if content_type in model_folders.keys(): 381 | model_folder = model_folders[content_type] 382 | elif content_type: 383 | model_folder = os.path.join(model_folders['Unknown'], util.replace_dirname(content_type)) 384 | else: 385 | model_folder = os.path.join(model_folders['Unknown']) 386 | 387 | return model_folder 388 | 389 | def generate_version_foldername(model_name,ver_name,ver_id): 390 | return f"{model_name}-{ver_name}" 391 | 392 | def get_model_folders(): 393 | return model_folders.values() 394 | 395 | def get_ui_typename(model_type): 396 | for k,v in ui_typenames.items(): 397 | if v == model_type: 398 | return k 399 | return model_type 400 | 401 | def get_imagefn_and_shortcutid_from_recipe_image(recipe_image): 402 | if recipe_image: 403 | result = recipe_image.split(":", 1) 404 | if len(result) > 1: 405 | return result[0], result[1] 406 | return None, None 407 | 408 | def set_imagefn_and_shortcutid_for_recipe_image( shortcutid,image_fn): 409 | if image_fn and shortcutid: 410 | return f"{shortcutid}:{image_fn}" 411 | 412 | def get_modelid_from_shortcutname(sc_name): 413 | if sc_name: 414 | return sc_name[sc_name.rfind(':') + 1:] 415 | 416 | def set_shortcutname(modelname,modelid): 417 | if modelname and modelid: 418 | return f"{modelname}:{modelid}" 419 | 420 | def get_image_url_to_shortcut_file(modelid, versionid, image_url): 421 | if image_url: 422 | version_image_prefix = f"{versionid}-" 423 | model_path = os.path.join(shortcut_info_folder, str(modelid)) 424 | image_id, ext = os.path.splitext(os.path.basename(image_url)) 425 | description_img = os.path.join(model_path, f"{version_image_prefix}{image_id}{preview_image_ext}") 426 | return description_img 427 | return None 428 | 429 | def get_image_url_to_gallery_file(image_url): 430 | if image_url: 431 | image_id, ext = os.path.splitext(os.path.basename(image_url)) 432 | description_img = os.path.join(shortcut_gallery_folder, f"{image_id}{preview_image_ext}") 433 | return description_img 434 | return None 435 | 436 | def save(env): 437 | try: 438 | with open(shortcut_setting, 'w') as f: 439 | json.dump(env, f, indent=4) 440 | except Exception as e: 441 | return False 442 | 443 | return True 444 | 445 | def load(): 446 | if not os.path.isfile(shortcut_setting): 447 | save({}) 448 | return 449 | 450 | json_data = None 451 | try: 452 | with open(shortcut_setting, 'r') as f: 453 | json_data = json.load(f) 454 | except: 455 | pass 456 | 457 | return json_data 458 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/setting_action.py: -------------------------------------------------------------------------------- 1 | import os 2 | import gradio as gr 3 | import shutil 4 | 5 | from . import util 6 | from . import setting 7 | 8 | # import modules.scripts as scripts 9 | from modules import scripts, script_callbacks, shared 10 | 11 | def on_setting_ui(): 12 | with gr.Column(): 13 | with gr.Row(): 14 | with gr.Accordion("Option", open=False): 15 | with gr.Row(): 16 | civitai_api_key = gr.Textbox(value=setting.civitai_api_key, label="Civitai Api Key", info="To access the models that require you to be logged in. Please obtain the key from: https://civitai.com/user/account.", interactive=True) 17 | with gr.Row(): 18 | shortcut_update_when_start = gr.Checkbox(value=setting.shortcut_update_when_start, label="Startup : The program performs 'Update the model information for the shortcut' when it starts.",info="At program startup, the registered shortcuts are updated with the latest data. This process operates in the background. To update manually, you can uncheck that option and use the 'Scans and Model Updates -> Update the model information for the shortcut' feature.", interactive=True) 19 | shortcut_max_download_image_per_version = gr.Slider(minimum=0, maximum=30, value=setting.shortcut_max_download_image_per_version, step=1,info="When registering a shortcut of a model, you can specify the maximum number of images to download. \n This is the maximum per version, and setting it to 0 means unlimited downloads.", label='Maximum number of download images per version', interactive=True) 20 | with gr.Row(): 21 | with gr.Accordion("Screen Style", open=False): 22 | with gr.Row(): 23 | scbrowser_screen_split_ratio = gr.Slider(minimum=0, maximum=setting.shortcut_browser_screen_split_ratio_max, value=setting.shortcut_browser_screen_split_ratio, step=1, info="You can specify the size ratio between the shortcut browser and the information screen.", label='Model Browser screen ratio', interactive=True) 24 | with gr.Row(): 25 | info_gallery_height = gr.Dropdown(choices=["auto"], value=setting.information_gallery_height, allow_custom_value=True, interactive=True, info="You can also specify a specific size other than 'auto'" , label="Information Gallery Height") 26 | gallery_thumbnail_image_style = gr.Dropdown(choices=["scale-down","cover","contain","fill","none"], value=setting.gallery_thumbnail_image_style, interactive=True, info="This specifies the shape of the displayed thumbnail." , label="Gallery Thumbnail Image Style") 27 | shortcut_browser_search_up = gr.Dropdown(choices=["Up","Down"], value="Up" if setting.shortcut_browser_search_up else "Down", interactive=True, label="Set the position of the search bar in the shortcut browser.", info="If you select 'Up', the search bar will be placed above the thumbnail pane.") 28 | 29 | with gr.Row(): 30 | with gr.Tabs(): 31 | with gr.TabItem("Model Browser and Model Information"): 32 | with gr.Row(): 33 | shortcut_column = gr.Slider(minimum=1, maximum=14, value=setting.shortcut_column, step=1, label='Model Browser Thumbnail Counts per Row', interactive=True) 34 | shortcut_rows_per_page = gr.Slider(minimum=0, maximum=14, value=setting.shortcut_rows_per_page, step=1, label='Model Browser Thumbnails Rows per Page : setting it to 0 means displaying the entire list without a page.', interactive=True) 35 | with gr.Row(): 36 | gallery_column = gr.Slider(minimum=1, maximum=14, value=setting.gallery_column, step=1, label='Model Information Image Counts per Row', interactive=True) 37 | with gr.TabItem("Civitai User Gallery"): 38 | with gr.Row(): 39 | usergallery_images_column = gr.Slider(minimum=1, maximum=14, value=setting.usergallery_images_column, step=1, label='Civitai User Gallery Image Counts per Row', interactive=True) 40 | usergallery_images_rows_per_page = gr.Slider(minimum=1, maximum=14, value=setting.usergallery_images_rows_per_page, step=1, label='Civitai User Gallery Image Rows Per Page', interactive=True) 41 | with gr.Row(): 42 | usergallery_openfolder_btn = gr.Button(value="Open Civitai User Gallery Cache Folder", variant="primary") 43 | with gr.Accordion("Clean User Gallery Cache", open=False): 44 | usergallery_cleangallery_btn = gr.Button(value="Clean Civitai User Gallery Cache", variant="primary") 45 | 46 | with gr.Row(): 47 | with gr.Tabs(): 48 | with gr.TabItem("Prompt Recipe"): 49 | with gr.Row(): 50 | prompt_shortcut_column = gr.Slider(minimum=1, maximum=14, value=setting.prompt_shortcut_column, step=1, label='Prompt Recipe Browser Thumbnail Counts per Row', interactive=True) 51 | prompt_shortcut_rows_per_page = gr.Slider(minimum=1, maximum=14, value=setting.prompt_shortcut_rows_per_page, step=1, label='Prompt Recipe Browser Thumbnails Rows per Page', interactive=True) 52 | # with gr.TabItem("Shortcut Models for Reference"): 53 | with gr.Row(): 54 | prompt_reference_shortcut_column = gr.Slider(minimum=1, maximum=14, value=setting.prompt_reference_shortcut_column, step=1, label='Reference\'s Models Browser Thumbnail Counts per Row', interactive=True) 55 | prompt_reference_shortcut_rows_per_page = gr.Slider(minimum=1, maximum=14, value=setting.prompt_reference_shortcut_rows_per_page, step=1, label=' Reference\'s Models Browser Thumbnails Rows per Page', interactive=True) 56 | 57 | with gr.Row(): 58 | with gr.Tabs(): 59 | with gr.TabItem("Classification"): 60 | with gr.Row(): 61 | classification_shortcut_column = gr.Slider(minimum=1, maximum=14, value=setting.classification_shortcut_column, step=1, label='Classification\'s Model Browser Thumbnail Counts per Row', interactive=True) 62 | classification_shortcut_rows_per_page = gr.Slider(minimum=0, maximum=14, value=setting.classification_shortcut_rows_per_page, step=1, label='Classification\'s Model Browser Thumbnails Rows per Page : setting it to 0 means displaying the entire list without a page.', interactive=True) 63 | with gr.Row(): 64 | classification_gallery_column = gr.Slider(minimum=1, maximum=14, value=setting.classification_gallery_column, step=1, label='Classification Model Counts per Row', interactive=True) 65 | classification_gallery_rows_per_page = gr.Slider(minimum=0, maximum=14, value=setting.classification_gallery_rows_per_page, step=1, label='Classification Model Rows per Page : setting it to 0 means displaying the entire list without a page.', interactive=True) 66 | 67 | 68 | with gr.Row(): 69 | with gr.Accordion("Download Folder for Extensions", open=False): 70 | with gr.Column(): 71 | extension_locon_folder = gr.Textbox(value=setting.model_folders['LoCon'], label="LyCORIS", interactive=True) 72 | extension_wildcards_folder = gr.Textbox(value=setting.model_folders['Wildcards'], label="Wildcards", interactive=True) 73 | extension_controlnet_folder = gr.Textbox(value=setting.model_folders['Controlnet'], label="Controlnet", interactive=True) 74 | extension_aestheticgradient_folder = gr.Textbox(value=setting.model_folders['AestheticGradient'], label="Aesthetic Gradient", interactive=True) 75 | extension_poses_folder = gr.Textbox(value=setting.model_folders['Poses'], label="Poses", interactive=True) 76 | extension_other_folder = gr.Textbox(value=setting.model_folders['Other'], label="Other", interactive=True) 77 | download_images_folder = gr.Textbox(value=setting.download_images_folder, label="Download Images Folder", interactive=True) 78 | 79 | with gr.Row(): 80 | save_btn = gr.Button(value="Save Setting", variant="primary") 81 | reload_btn = gr.Button(value="Reload UI") 82 | refresh_setting = gr.Textbox(visible=False) 83 | 84 | refresh_setting.change( 85 | fn=on_refresh_setting_change, 86 | inputs=None, 87 | outputs=[ 88 | civitai_api_key, 89 | shortcut_update_when_start, 90 | scbrowser_screen_split_ratio, 91 | info_gallery_height, 92 | shortcut_column, 93 | shortcut_rows_per_page, 94 | gallery_column, 95 | classification_shortcut_column, 96 | classification_shortcut_rows_per_page, 97 | classification_gallery_column, 98 | classification_gallery_rows_per_page, 99 | usergallery_images_column, 100 | usergallery_images_rows_per_page, 101 | 102 | prompt_shortcut_column, 103 | prompt_shortcut_rows_per_page, 104 | prompt_reference_shortcut_column, 105 | prompt_reference_shortcut_rows_per_page, 106 | 107 | shortcut_max_download_image_per_version, 108 | gallery_thumbnail_image_style, 109 | shortcut_browser_search_up, 110 | extension_locon_folder, 111 | extension_wildcards_folder, 112 | extension_controlnet_folder, 113 | extension_aestheticgradient_folder, 114 | extension_poses_folder, 115 | extension_other_folder, 116 | download_images_folder 117 | ], 118 | show_progress=False 119 | ) 120 | 121 | # reload the page 122 | reload_btn.click(fn=on_reload_btn_click, _js='restart_reload', inputs=None, outputs=None) 123 | 124 | usergallery_openfolder_btn.click( 125 | fn=on_usergallery_openfolder_btn_click, 126 | inputs=None, 127 | outputs=None 128 | ) 129 | 130 | usergallery_cleangallery_btn.click( 131 | fn=on_usergallery_cleangallery_btn_click, 132 | inputs=None, 133 | outputs=None 134 | ) 135 | 136 | save_btn.click( 137 | fn=on_save_btn_click, 138 | inputs=[ 139 | civitai_api_key, 140 | shortcut_update_when_start, 141 | scbrowser_screen_split_ratio, 142 | info_gallery_height, 143 | shortcut_column, 144 | shortcut_rows_per_page, 145 | gallery_column, 146 | classification_shortcut_column, 147 | classification_shortcut_rows_per_page, 148 | classification_gallery_column, 149 | classification_gallery_rows_per_page, 150 | usergallery_images_column, 151 | usergallery_images_rows_per_page, 152 | 153 | prompt_shortcut_column, 154 | prompt_shortcut_rows_per_page, 155 | prompt_reference_shortcut_column, 156 | prompt_reference_shortcut_rows_per_page, 157 | 158 | shortcut_max_download_image_per_version, 159 | gallery_thumbnail_image_style, 160 | shortcut_browser_search_up, 161 | extension_locon_folder, 162 | extension_wildcards_folder, 163 | extension_controlnet_folder, 164 | extension_aestheticgradient_folder, 165 | extension_poses_folder, 166 | extension_other_folder, 167 | download_images_folder 168 | ], 169 | outputs=None 170 | ) 171 | 172 | return refresh_setting 173 | 174 | def on_save_btn_click(civitai_api_key, shortcut_update_when_start, 175 | scbrowser_screen_split_ratio, info_gallery_height, 176 | shortcut_column, shortcut_rows_per_page, gallery_column, 177 | classification_shortcut_column, classification_shortcut_rows_per_page, classification_gallery_column, classification_gallery_rows_per_page, 178 | usergallery_images_column, usergallery_images_rows_per_page, 179 | prompt_shortcut_column, prompt_shortcut_rows_per_page, prompt_reference_shortcut_column, prompt_reference_shortcut_rows_per_page, 180 | shortcut_max_download_image_per_version, 181 | gallery_thumbnail_image_style, 182 | shortcut_browser_search_up, 183 | locon,wildcards,controlnet,aestheticgradient,poses,other,download_images_folder 184 | ): 185 | 186 | save_setting(civitai_api_key, shortcut_update_when_start, 187 | scbrowser_screen_split_ratio, info_gallery_height, 188 | shortcut_column, shortcut_rows_per_page, gallery_column, 189 | classification_shortcut_column, classification_shortcut_rows_per_page, classification_gallery_column, classification_gallery_rows_per_page, 190 | usergallery_images_column, usergallery_images_rows_per_page, 191 | prompt_shortcut_column, prompt_shortcut_rows_per_page, prompt_reference_shortcut_column, prompt_reference_shortcut_rows_per_page, 192 | shortcut_max_download_image_per_version, 193 | gallery_thumbnail_image_style, 194 | shortcut_browser_search_up, 195 | locon,wildcards,controlnet,aestheticgradient,poses,other,download_images_folder 196 | ) 197 | 198 | def save_setting(civitai_api_key, shortcut_update_when_start, 199 | scbrowser_screen_split_ratio, info_gallery_height, 200 | shortcut_column, shortcut_rows_per_page, gallery_column, 201 | classification_shortcut_column, classification_shortcut_rows_per_page, classification_gallery_column, classification_gallery_rows_per_page, 202 | usergallery_images_column, usergallery_images_rows_per_page, 203 | prompt_shortcut_column, prompt_shortcut_rows_per_page, prompt_reference_shortcut_column, prompt_reference_shortcut_rows_per_page, 204 | shortcut_max_download_image_per_version, 205 | gallery_thumbnail_image_style, 206 | shortcut_browser_search_up, 207 | locon,wildcards,controlnet,aestheticgradient,poses,other,download_images_folder 208 | ): 209 | 210 | environment = setting.load() 211 | if not environment: 212 | environment = dict() 213 | 214 | application_allow = dict() 215 | application_allow['civitai_api_key'] = civitai_api_key 216 | application_allow['shortcut_update_when_start'] = shortcut_update_when_start 217 | application_allow['shortcut_max_download_image_per_version'] = shortcut_max_download_image_per_version 218 | environment['application_allow'] = application_allow 219 | 220 | screen_style = dict() 221 | screen_style['shortcut_browser_screen_split_ratio'] = scbrowser_screen_split_ratio 222 | screen_style['information_gallery_height'] = info_gallery_height 223 | screen_style['gallery_thumbnail_image_style'] = gallery_thumbnail_image_style 224 | screen_style['shortcut_browser_search_up'] = True if shortcut_browser_search_up == "Up" else False 225 | environment['screen_style'] = screen_style 226 | 227 | image_style = dict() 228 | image_style['shortcut_column'] = shortcut_column 229 | image_style['shortcut_rows_per_page'] = shortcut_rows_per_page 230 | 231 | image_style['gallery_column'] = gallery_column 232 | 233 | image_style['classification_shortcut_column'] = classification_shortcut_column 234 | image_style['classification_shortcut_rows_per_page'] = classification_shortcut_rows_per_page 235 | image_style['classification_gallery_column'] = classification_gallery_column 236 | image_style['classification_gallery_rows_per_page'] = classification_gallery_rows_per_page 237 | 238 | image_style['usergallery_images_column'] = usergallery_images_column 239 | image_style['usergallery_images_rows_per_page'] = usergallery_images_rows_per_page 240 | 241 | image_style['prompt_shortcut_column'] = prompt_shortcut_column 242 | image_style['prompt_shortcut_rows_per_page'] = prompt_shortcut_rows_per_page 243 | image_style['prompt_reference_shortcut_column'] = prompt_reference_shortcut_column 244 | image_style['prompt_reference_shortcut_rows_per_page'] = prompt_reference_shortcut_rows_per_page 245 | 246 | environment['image_style'] = image_style 247 | 248 | model_folders = dict() 249 | if locon: 250 | model_folders['LoCon'] = locon 251 | if wildcards: 252 | model_folders['Wildcards'] = wildcards 253 | if controlnet: 254 | model_folders['Controlnet'] = controlnet 255 | if aestheticgradient: 256 | model_folders['AestheticGradient'] = aestheticgradient 257 | if poses: 258 | model_folders['Poses'] = poses 259 | if other: 260 | model_folders['Other'] = other 261 | 262 | environment['model_folders'] = model_folders 263 | 264 | download_folders = dict() 265 | if download_images_folder: 266 | download_folders['download_images'] = download_images_folder 267 | 268 | environment['download_folders'] = download_folders 269 | 270 | temporary = dict() 271 | 272 | environment['temporary'] = temporary 273 | 274 | setting.save(environment) 275 | setting.load_data() 276 | 277 | util.printD("Save setting. Reload UI is needed") 278 | 279 | def on_usergallery_openfolder_btn_click(): 280 | if os.path.exists(setting.shortcut_gallery_folder): 281 | util.open_folder(setting.shortcut_gallery_folder) 282 | 283 | def on_usergallery_cleangallery_btn_click(): 284 | if os.path.exists(setting.shortcut_gallery_folder): 285 | shutil.rmtree(setting.shortcut_gallery_folder) 286 | 287 | def on_reload_btn_click(): 288 | request_restart() 289 | 290 | def request_restart(): 291 | shared.state.interrupt() 292 | shared.state.need_restart = True 293 | 294 | # def on_update_btn_click(): 295 | # git = os.environ.get('GIT', "git") 296 | 297 | # subdir = os.path.dirname(os.path.abspath(__file__)) 298 | 299 | # # perform git pull in the extension folder 300 | # output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash']) 301 | # print(output.decode('utf-8')) 302 | 303 | def on_refresh_setting_change(): 304 | return setting.civitai_api_key,\ 305 | setting.shortcut_update_when_start,\ 306 | setting.shortcut_browser_screen_split_ratio,\ 307 | setting.information_gallery_height,\ 308 | setting.shortcut_column,\ 309 | setting.shortcut_rows_per_page,\ 310 | setting.gallery_column,\ 311 | setting.classification_shortcut_column,\ 312 | setting.classification_shortcut_rows_per_page,\ 313 | setting.classification_gallery_column,\ 314 | setting.classification_gallery_rows_per_page,\ 315 | setting.usergallery_images_column,\ 316 | setting.usergallery_images_rows_per_page,\ 317 | setting.prompt_shortcut_column,\ 318 | setting.prompt_shortcut_rows_per_page,\ 319 | setting.prompt_reference_shortcut_column,\ 320 | setting.prompt_reference_shortcut_rows_per_page,\ 321 | setting.shortcut_max_download_image_per_version,\ 322 | setting.gallery_thumbnail_image_style,\ 323 | "Up" if setting.shortcut_browser_search_up else "Down",\ 324 | setting.model_folders['LoCon'],\ 325 | setting.model_folders['Wildcards'],\ 326 | setting.model_folders['Controlnet'],\ 327 | setting.model_folders['AestheticGradient'],\ 328 | setting.model_folders['Poses'],\ 329 | setting.model_folders['Other'],\ 330 | setting.download_images_folder 331 | -------------------------------------------------------------------------------- /scripts/civitai_manager_libs/util.py: -------------------------------------------------------------------------------- 1 | import io 2 | import re 3 | import os 4 | import json 5 | 6 | import hashlib 7 | import platform 8 | import subprocess 9 | 10 | from modules import scripts, script_callbacks, shared 11 | 12 | from . import setting 13 | from tqdm import tqdm 14 | 15 | def printD(msg): 16 | print(f"{setting.Extensions_Name}: {msg}") 17 | 18 | def calculate_sha256(filname): 19 | """ 20 | Calculate the SHA256 hash for a file. 21 | """ 22 | block_size = 1024 * 1024 # 1MB 23 | length = 0 24 | with open(filname, 'rb') as file: 25 | hash = hashlib.sha256() 26 | for chunk in tqdm(iter(lambda: file.read(block_size), b""), total=(os.path.getsize(filname)//block_size)+1): 27 | length += len(chunk) 28 | hash.update(chunk) 29 | 30 | hash_value = hash.hexdigest() 31 | printD("sha256: " + hash_value) 32 | printD("length: " + str(length)) 33 | return hash_value 34 | 35 | def is_url_or_filepath(input_string): 36 | if not input_string: 37 | return "unknown" 38 | 39 | if os.path.exists(input_string): 40 | return "filepath" 41 | elif input_string.lower().startswith("http://") or input_string.lower().startswith("https://"): 42 | return "url" 43 | else: 44 | return "unknown" 45 | 46 | def convert_civitai_meta_to_stable_meta(meta:dict): 47 | meta_string = "" 48 | different_key=['prompt', 'negativePrompt','steps','sampler','cfgScale','seed','resources','hashes'] 49 | if meta: 50 | if "prompt" in meta: 51 | meta_string = f"""{meta['prompt']}""" + "\n" 52 | if "negativePrompt" in meta: 53 | meta_string = meta_string + f"""Negative prompt:{meta['negativePrompt']}""" + "\n" 54 | if "steps" in meta: 55 | meta_string = meta_string + f",Steps:{meta['steps']}" 56 | if "sampler" in meta: 57 | meta_string = meta_string + f",Sampler:{meta['sampler']}" 58 | if "cfgScale" in meta: 59 | meta_string = meta_string + f",CFG scale:{meta['cfgScale']}" 60 | if "seed" in meta: 61 | meta_string = meta_string + f",Seed:{meta['seed']}" 62 | 63 | addistion_string = ','.join([f'{key}:{value}' for key, value in meta.items() if key not in different_key]) 64 | meta_string = meta_string + "," + addistion_string 65 | 66 | return meta_string 67 | 68 | def update_url(url, param_name, new_value): 69 | if param_name not in url: 70 | # If the parameter is not found in the URL, add it to the end with the new value 71 | if "?" in url: 72 | # If there are already other parameters in the URL, add the new parameter with "&" separator 73 | updated_url = url + "&" + param_name + "=" + str(new_value) 74 | else: 75 | # If there are no parameters in the URL, add the new parameter with "?" separator 76 | updated_url = url + "?" + param_name + "=" + str(new_value) 77 | else: 78 | # If the parameter is found in the URL, update its value with the new value 79 | prefix, suffix = url.split(param_name + "=") 80 | if "&" in suffix: 81 | current_value, remainder = suffix.split("&", 1) 82 | updated_suffix = param_name + "=" + str(new_value) + "&" + remainder 83 | else: 84 | updated_suffix = param_name + "=" + str(new_value) 85 | updated_url = prefix + updated_suffix 86 | 87 | return updated_url 88 | 89 | def add_number_to_duplicate_files(filenames)->dict: 90 | counts = {} 91 | for i, filename in enumerate(filenames): 92 | if filename in counts: 93 | name, ext = os.path.splitext(filename) 94 | counts[filename] += 1 95 | filenames[i] = f"{name} ({counts[filename]}){ext}" 96 | else: 97 | counts[filename] = 0 98 | 99 | # 굳이 안해도 dict가 되네???? 100 | # result = dict() 101 | # if filenames: 102 | # for filename in filenames: 103 | # file = filename[filename.lfind(':') + 1:] 104 | # id = filename[:filename.lfind(':')] 105 | # result[str(id)] = file 106 | # return result 107 | 108 | return filenames 109 | 110 | def open_folder(path): 111 | if os.path.exists(path): 112 | # Code from ui_common.py 113 | if not shared.cmd_opts.hide_ui_dir_config: 114 | if platform.system() == "Windows": 115 | os.startfile(path) 116 | elif platform.system() == "Darwin": 117 | subprocess.Popen(["open", path]) 118 | elif "microsoft-standard-WSL2" in platform.uname().release: 119 | subprocess.Popen(["wsl-open", path]) 120 | else: 121 | subprocess.Popen(["xdg-open", path]) 122 | 123 | def get_search_keyword(search:str): 124 | tags = [] 125 | keys = [] 126 | notes = [] 127 | 128 | if not search: 129 | return None, None, None 130 | 131 | for word in search.split(","): 132 | word = word.strip() 133 | if word.startswith("#"): 134 | if len(word) > 1: 135 | tag = word[1:].lower() 136 | if tag not in tags: 137 | tags.append(tag) 138 | elif word.startswith("@"): 139 | if len(word) > 1: 140 | note = word[1:] 141 | if note not in notes: 142 | notes.append(note) 143 | else: 144 | word = word.lower() 145 | if word not in keys: 146 | keys.append(word) 147 | 148 | return keys if len(keys) > 0 else None, tags if len(tags) > 0 else None, notes if len(notes) > 0 else None 149 | 150 | def read_json(path)->dict: 151 | contents = None 152 | if not path: 153 | return None 154 | try: 155 | with open(path, 'r') as f: 156 | contents = json.load(f) 157 | except: 158 | return None 159 | 160 | return contents 161 | 162 | def write_json(contents, path): 163 | if not path: 164 | return 165 | 166 | if not contents: 167 | return 168 | 169 | try: 170 | with open(path, 'w') as f: 171 | f.write(json.dumps(contents, indent=4)) 172 | except Exception as e: 173 | return 174 | 175 | def scan_folder_for_info(folder): 176 | info_list = search_file([folder],None,[setting.info_ext]) 177 | 178 | if not info_list: 179 | return None 180 | 181 | return info_list 182 | 183 | def get_download_image_folder(ms_foldername): 184 | 185 | if not ms_foldername: 186 | return 187 | 188 | if not setting.download_images_folder: 189 | return 190 | 191 | model_folder = os.path.join(setting.download_images_folder.strip(), replace_dirname(ms_foldername.strip())) 192 | 193 | if not os.path.exists(model_folder): 194 | return None 195 | 196 | return model_folder 197 | 198 | def make_download_image_folder(ms_foldername): 199 | 200 | if not ms_foldername: 201 | return 202 | 203 | if not setting.download_images_folder: 204 | return 205 | 206 | model_folder = os.path.join(setting.download_images_folder.strip(), replace_dirname(ms_foldername.strip())) 207 | 208 | if not os.path.exists(model_folder): 209 | os.makedirs(model_folder) 210 | 211 | return model_folder 212 | 213 | # 다정하면 임의의 분류뒤에 모델폴더를 생성하고 그뒤에 버전까지 생성가능 214 | def make_download_model_folder(version_info, ms_folder=True, vs_folder=True, vs_foldername=None, cs_foldername=None, ms_foldername=None): 215 | 216 | if not version_info: 217 | return 218 | 219 | if "model" not in version_info.keys(): 220 | return 221 | 222 | content_type = version_info['model']['type'] 223 | model_folder = setting.generate_type_basefolder(content_type) 224 | 225 | if not model_folder: 226 | return 227 | 228 | if not cs_foldername and not ms_folder: 229 | return 230 | 231 | if cs_foldername: 232 | model_folder = os.path.join(model_folder, replace_dirname(cs_foldername.strip())) 233 | 234 | if ms_folder: 235 | if not ms_foldername or len(ms_foldername.strip()) <= 0: 236 | ms_foldername = version_info['model']['name'] 237 | 238 | model_folder = os.path.join(model_folder, replace_dirname(ms_foldername.strip())) 239 | 240 | if vs_folder: 241 | if not vs_foldername or len(vs_foldername.strip()) <= 0: 242 | vs_foldername = setting.generate_version_foldername(ms_foldername, version_info['name'], version_info['id']) 243 | 244 | model_folder = os.path.join(model_folder, replace_dirname(vs_foldername.strip())) 245 | 246 | if not os.path.exists(model_folder): 247 | os.makedirs(model_folder) 248 | 249 | return model_folder 250 | 251 | def replace_filename(file_name): 252 | if file_name and len(file_name.strip()) > 0: 253 | return file_name.replace("*", "-").replace("?", "-").replace("\"", "-").replace("|", "-").replace(":", "-").replace("/", "-").replace("\\", "-").replace("<", "-").replace(">", "-") 254 | return None 255 | 256 | def replace_dirname(dir_name): 257 | if dir_name and len(dir_name.strip()) > 0: 258 | return dir_name.replace("*", "-").replace("?", "-").replace("\"", "-").replace("|", "-").replace(":", "-").replace("/", "-").replace("\\", "-").replace("<", "-").replace(">", "-") 259 | return None 260 | 261 | def write_InternetShortcut(path, url): 262 | try: 263 | with open(path, 'w', newline='\r\n') as f: 264 | f.write(f"[InternetShortcut]\nURL={url}") 265 | except: 266 | return False 267 | return True 268 | 269 | def load_InternetShortcut(path)->str: 270 | urls = list() 271 | try: 272 | with open(path, 'r') as f: 273 | content = f.read() 274 | # urls = re.findall("(?Phttps?://[^\s]+)", content) 275 | urls = re.findall('(?Phttps?://[^\s:"]+)', content) 276 | except Exception as e: 277 | printD(e) 278 | return 279 | # printD(urls) 280 | return urls 281 | 282 | # get image with full size 283 | # width is in number, not string 284 | # 파일 인포가 있는 원본 이미지 주소이다. 285 | def get_full_size_image_url(image_url, width): 286 | return re.sub('/width=\d+/', '/width=' + str(width) + '/', image_url) 287 | 288 | def change_width_from_image_url(image_url, width): 289 | return re.sub('/width=\d+/', '/width=' + str(width) + '/', image_url) 290 | 291 | def get_model_id_from_url(url): 292 | if not url: 293 | return "" 294 | 295 | if url.isnumeric(): 296 | return str(url) 297 | 298 | s = url.split("/") 299 | if len(s) < 2: 300 | return "" 301 | 302 | for i in range(len(s)): 303 | if s[i] == "models" and i < len(s)-1: 304 | id_str = s[i+1].split("?")[0] 305 | if id_str.isnumeric(): 306 | return id_str 307 | 308 | return "" 309 | 310 | def search_file(root_dirs:list,base:list,exts:list)->list: 311 | file_list = list() 312 | root_path = os.getcwd() 313 | 314 | for root_dir in root_dirs: 315 | for (root,dirs,files) in os.walk(os.path.join(root_path,root_dir)): 316 | if len(files) > 0: 317 | for file_name in files: 318 | b, e = os.path.splitext(file_name) 319 | if base and exts: 320 | if e in exts and b in base: 321 | file_list.append(os.path.join(root,file_name)) 322 | elif base: 323 | if b in base: 324 | file_list.append(os.path.join(root,file_name)) 325 | elif exts: 326 | if e in exts: 327 | file_list.append(os.path.join(root,file_name)) 328 | else: 329 | file_list.append(os.path.join(root,file_name)) 330 | 331 | return file_list if len(file_list) > 0 else None 332 | -------------------------------------------------------------------------------- /scripts/civitai_shortcut.py: -------------------------------------------------------------------------------- 1 | import os 2 | import datetime 3 | import gradio as gr 4 | import threading 5 | 6 | from modules import script_callbacks 7 | 8 | from scripts.civitai_manager_libs import model 9 | from scripts.civitai_manager_libs import setting 10 | from scripts.civitai_manager_libs import classification_action 11 | from scripts.civitai_manager_libs import civitai_shortcut_action 12 | from scripts.civitai_manager_libs import setting_action 13 | from scripts.civitai_manager_libs import scan_action 14 | from scripts.civitai_manager_libs import util 15 | from scripts.civitai_manager_libs import ishortcut 16 | from scripts.civitai_manager_libs import recipe_action 17 | 18 | def on_civitai_tabs_select(evt: gr.SelectData): 19 | current_time = datetime.datetime.now() 20 | if evt.index == 0: 21 | return current_time, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) 22 | elif evt.index == 1: 23 | return gr.update(visible=False), current_time, gr.update(visible=False), gr.update(visible=False) 24 | elif evt.index == 2: 25 | return gr.update(visible=False), gr.update(visible=False), current_time, gr.update(visible=False) 26 | elif evt.index == 3: 27 | return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), current_time 28 | 29 | return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) 30 | 31 | # def readmarkdown(): 32 | # path = os.path.join(setting.extension_base,"README.md") 33 | # markdown_text = None 34 | # try: 35 | # with open(path, 'r',encoding='UTF-8') as f: 36 | # markdown_text = f.read() 37 | # except Exception as e: 38 | # util.printD(e) 39 | # return 40 | # return markdown_text 41 | 42 | def civitai_shortcut_ui(): 43 | with gr.Tabs(elem_id="civitai_shortcut_tabs_container") as civitai_tabs: 44 | with gr.Row(visible=False): 45 | recipe_input = gr.Textbox() 46 | shortcut_input = gr.Textbox() 47 | 48 | with gr.TabItem("Model Browser" , id="Shortcut"): 49 | with gr.Row(): 50 | refresh_civitai_sc_browser, refresh_civitai_information = civitai_shortcut_action.on_ui(recipe_input, shortcut_input, civitai_tabs) 51 | 52 | with gr.TabItem("Prompt Recipe" , id="Recipe"): 53 | with gr.Row(): 54 | refresh_recipe = recipe_action.on_ui(recipe_input, shortcut_input, civitai_tabs) 55 | 56 | with gr.TabItem("Assistance" , id="Assistance"): 57 | with gr.Tabs() as civitai_assistance_tabs: 58 | with gr.TabItem("Classification"): 59 | with gr.Row(): 60 | refresh_classification = classification_action.on_ui(shortcut_input) 61 | with gr.TabItem("Scan and Update Models"): 62 | with gr.Row(): 63 | scan_action.on_scan_ui() 64 | 65 | with gr.TabItem("Manage" , id="Manage"): 66 | with gr.Tabs() as civitai_manage_tabs: 67 | with gr.TabItem("Setting"): 68 | with gr.Row(): 69 | refresh_setting = setting_action.on_setting_ui() 70 | # with gr.TabItem("ReadMe"): 71 | # with gr.Row(): 72 | # gr.Markdown(value=readmarkdown()) 73 | 74 | # civitai tab start 75 | civitai_tabs.select( 76 | fn=on_civitai_tabs_select, 77 | inputs=None, 78 | outputs=[refresh_civitai_sc_browser, refresh_recipe , refresh_classification, refresh_setting] 79 | ) 80 | 81 | def update_all_shortcut_informations(): 82 | preISC = ishortcut.load() 83 | if not preISC: 84 | return 85 | 86 | modelid_list = [k for k in preISC] 87 | util.printD("shortcut update start") 88 | for modelid in modelid_list: 89 | ishortcut.write_model_information(modelid, False, None) 90 | util.printD("shortcut update end") 91 | 92 | def update_all_shortcut_informations_thread(): 93 | try: 94 | thread = threading.Thread(target=update_all_shortcut_informations) 95 | thread.start() 96 | except Exception as e: 97 | util.printD(e) 98 | pass 99 | 100 | def init_civitai_shortcut(): 101 | setting.init() 102 | model.update_downloaded_model() 103 | 104 | util.printD(setting.Extensions_Version) 105 | 106 | if setting.shortcut_update_when_start: 107 | update_all_shortcut_informations_thread() 108 | 109 | def on_ui_tabs(): 110 | # init 111 | init_civitai_shortcut() 112 | 113 | with gr.Blocks() as civitai_shortcut: 114 | civitai_shortcut_ui() 115 | 116 | return (civitai_shortcut, "Civitai Shortcut", "civitai_shortcut"), 117 | 118 | 119 | script_callbacks.on_ui_tabs(on_ui_tabs) 120 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .block.padded.cs_box { 2 | padding: 10px !important; 3 | } 4 | 5 | .block.padded.cs_vpadding { 6 | padding: 10px 10px !important; 7 | } --------------------------------------------------------------------------------