├── .github
└── CODEOWNERS
├── .gitignore
├── LICENSE
├── README.md
├── howtogetthedirectlinks.md
├── images
├── anonfiles.png
├── batchlinks_logo.png
├── cancel.jpg
├── checkconsole1.jpg
├── checkconsole2.jpg
├── civit1.jpg
├── civit2.jpg
├── civitnew1.jpg
├── civitnew2.jpg
├── discord1.jpg
├── discord2.jpg
├── downloaded.jpg
├── drive1.png
├── drive2.png
├── dropbox1.png
├── dropbox2.png
├── example.jpg
├── ext_installer.jpg
├── github.jpg
├── githubdirect1.png
├── githubdirect2.png
├── githubdirect3.png
├── githubrelease1.png
├── githubrelease2.png
├── gradiooff1.jpg
├── gradiooff2.jpg
├── huggingface1.jpg
├── huggingface2.jpg
├── logging.jpg
├── mediafire.png
├── mega1.jpg
├── mega2.jpg
├── pastebin.png
├── pixeldrain.png
├── progress.jpg
├── python.png
├── queue.jpg
├── rename.jpg
├── sdless.png
├── shell1.jpg
└── shell2.jpg
├── install.py
├── javascript
└── notification.js
├── misc
└── markydynamictohtml.py
├── notification.mp3
├── regardingthisrepo.md
├── releasenotes.md
├── scripts
├── a_gradiocheck.py
└── batchlinks-downloader.py
├── sdless-macos.sh
├── sdless-windows-debug.bat
├── sdless-windows.bat
└── version.txt
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Global rule:
2 | * @etherealxx
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.bak
2 | *.backup
3 | imageratio.*
4 | *.lnk
5 | tempCodeRunnerFile.python
6 | .DS_Store
7 | gradiovenv/
8 |
9 | # Byte-compiled / optimized / DLL files
10 | __pycache__/
11 | *.py[cod]
12 | *$py.class
13 |
14 | # C extensions
15 | *.so
16 |
17 | # Distribution / packaging
18 | .Python
19 | build/
20 | develop-eggs/
21 | dist/
22 | downloads/
23 | eggs/
24 | .eggs/
25 | lib/
26 | lib64/
27 | parts/
28 | sdist/
29 | var/
30 | wheels/
31 | share/python-wheels/
32 | *.egg-info/
33 | .installed.cfg
34 | *.egg
35 | MANIFEST
36 |
37 | # PyInstaller
38 | # Usually these files are written by a python script from a template
39 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
40 | *.manifest
41 | *.spec
42 |
43 | # Installer logs
44 | pip-log.txt
45 | pip-delete-this-directory.txt
46 |
47 | # Unit test / coverage reports
48 | htmlcov/
49 | .tox/
50 | .nox/
51 | .coverage
52 | .coverage.*
53 | .cache
54 | nosetests.xml
55 | coverage.xml
56 | *.cover
57 | *.py,cover
58 | .hypothesis/
59 | .pytest_cache/
60 | cover/
61 |
62 | # Translations
63 | *.mo
64 | *.pot
65 |
66 | # Django stuff:
67 | *.log
68 | local_settings.py
69 | db.sqlite3
70 | db.sqlite3-journal
71 |
72 | # Flask stuff:
73 | instance/
74 | .webassets-cache
75 |
76 | # Scrapy stuff:
77 | .scrapy
78 |
79 | # Sphinx documentation
80 | docs/_build/
81 |
82 | # PyBuilder
83 | .pybuilder/
84 | target/
85 |
86 | # Jupyter Notebook
87 | .ipynb_checkpoints
88 |
89 | # IPython
90 | profile_default/
91 | ipython_config.py
92 |
93 | # pyenv
94 | # For a library or package, you might want to ignore these files since the code is
95 | # intended to run in multiple environments; otherwise, check them in:
96 | # .python-version
97 |
98 | # pipenv
99 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
100 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
101 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
102 | # install all needed dependencies.
103 | #Pipfile.lock
104 |
105 | # poetry
106 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
107 | # This is especially recommended for binary packages to ensure reproducibility, and is more
108 | # commonly ignored for libraries.
109 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
110 | #poetry.lock
111 |
112 | # pdm
113 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114 | #pdm.lock
115 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
116 | # in version control.
117 | # https://pdm.fming.dev/#use-with-ide
118 | .pdm.toml
119 |
120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121 | __pypackages__/
122 |
123 | # Celery stuff
124 | celerybeat-schedule
125 | celerybeat.pid
126 |
127 | # SageMath parsed files
128 | *.sage.py
129 |
130 | # Environments
131 | .env
132 | .venv
133 | env/
134 | venv/
135 | ENV/
136 | env.bak/
137 | venv.bak/
138 |
139 | # Spyder project settings
140 | .spyderproject
141 | .spyproject
142 |
143 | # Rope project settings
144 | .ropeproject
145 |
146 | # mkdocs documentation
147 | /site
148 |
149 | # mypy
150 | .mypy_cache/
151 | .dmypy.json
152 | dmypy.json
153 |
154 | # Pyre type checker
155 | .pyre/
156 |
157 | # pytype static type analyzer
158 | .pytype/
159 |
160 | # Cython debug symbols
161 | cython_debug/
162 |
163 | # PyCharm
164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166 | # and can be added to the global gitignore or merged into this file. For a more nuclear
167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168 | #.idea/
169 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Etherealxx
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
BatchLinks Downloader
9 |
10 |
11 | Batch-downloading models and stuff in stable-diffusion-webui colab made simple and fast.
12 |
13 |
14 |
15 | Report Bug
16 | ·
17 | Request Feature
18 |
19 |
20 |
21 |
22 |
23 |
58 |
59 |
60 |
61 | Table of Contents
62 |
63 | - Installation
64 | - About
65 | - Example
66 | - Syntax
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | - Additional Syntax
75 |
76 |
77 |
78 |
79 |
80 | - Gradio Queue
81 |
82 |
83 |
84 |
85 | - Other Features
86 |
87 |
88 |
89 |
90 |
91 | - Release Notes
92 | - Roadmap
93 | - Known Bugs
94 |
95 | - Contributing
96 | - Contact
97 | - Acknowledgments
98 |
99 |
100 |
101 |
102 | [does this repo still maintained?](https://github.com/etherealxx/batchlinks-webui/blob/main/regardingthisrepo.md)
103 |
104 | ## Installation
105 |
106 | Copy this line into your colab installation cell. Or into a new cell if you already launched the webui.
107 |
108 | ```
109 | !git clone https://github.com/etherealxx/batchlinks-webui /content/stable-diffusion-webui/extensions/batchlinks-webui
110 | ```
111 |
112 | or, you can copy the url of this repo and install it via webui and restart the UI.
113 | 
114 | (If `gradio no interface is running` or `bad gateway` shows up when restarting the UI, that means you need to restart the cell anyway 😅)
115 |
116 |
123 |
124 | Using `--gradio-queue` on the launch.py argument is highly recommended, as it enables this extension to show download progress bar on the UI and a cancel button. The option itself has no negative effect on the webui. [Read more here.](https://github.com/etherealxx/batchlinks-webui#gradio-queue)
125 | 
126 |
127 | You can also run this extension in [SDless mode](https://github.com/etherealxx/batchlinks-webui#sdless-mode) btw.
128 |
129 | While it's not recommended to use this extension on your local installation, you can use this extension on Windows. [More here.](https://github.com/etherealxx/batchlinks-webui#local-installation-support)
130 |
131 | ## About
132 |
133 | This extension will streamline your downloads on your [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui) colab session. Paste the links you need to download (or you can upload a txt file containing the links), use the hashstag syntax to choose the download location (see below), and hit the `Download All` button to download them!
134 |
135 | ## Example
136 |
137 | Look at this example
138 | 
139 |
140 | ```
141 | #model
142 | https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix3/AOM3_orangemixs.safetensors
143 | #vae
144 | https://huggingface.co/hakurei/waifu-diffusion-v1-4/resolve/main/vae/kl-f8-anime2.ckpt
145 | #embed
146 | https://huggingface.co/datasets/Nerfgun3/bad_prompt/resolve/main/bad_prompt_version2.pt
147 | https://huggingface.co/etherealxx/whoseisthis/resolve/main/bad_artist.pt
148 | #lora
149 | https://civitai.com/api/download/models/8840 ##1-mb-lora-trained-in-5-mins-that-does-the-same-thing-as-25-gb-model-but-better
150 | https://civitai.com/api/download/models/6891 ##sans undertale
151 | https://mega.nz/file/gAxTWBAI#uL7EZay-OND5G6ELJlGfUNG0s7Q4TynZKqFdvs0v0tc ##agent8-finetune
152 | https://mega.nz/file/oU43FSTY#vwAfsAb7RKJ4xtsSP7uzrKpWhh1y8BdpIBFurwsVP2o ##agent8-dreambooth
153 | #hypernet
154 | https://cdn.discordapp.com/attachments/1070489470127841381/1070489471964954684/MomopocoV3.pt
155 | ```
156 |
157 | This piece of lines will be read from top to bottom. Every hashtag, it will change the current output directory to said directory (see below). So what this example do is it will download `AOM3` model to the model folder, then it will download the vae and put it to the Vae folder. Next it will download two embed, `bad prompt` and `bad artist`. Next it will download several LoRAs from CivitAI and MEGA, and put it to the Lora folder. Lastly, it changes the directory to hypernet directory, then
158 |
159 | You can also copy that example and paste it to a `.txt` file to use later. You can load a `.txt` file containing that piece of lines directly from the UI.
160 |
161 | When the items is downloading, you can inspect the running code on the colab cell, or just take a coffee and chill☕. If you activate logging, you can inspect the download progress from the UI, more [here](https://github.com/etherealxx/batchlinks-webui#logging)
162 |
163 | When the download is complete, the downloaded file will be listed
164 | 
165 |
166 | ## Syntax
167 |
168 | #### Hashtag
169 | - Hashtag means change current output directory to this directory. `#model` means every links below this hashtag, will be downloaded to _/content/stable-diffusion-webui/models/Stable-diffusion_, until it hits another hashtag, which will change the output directory again. See [below](https://github.com/etherealxx/batchlinks-webui#valid-hashtags) for valid hashtags.
170 |
171 | Note: If you use some colab that purposefully uses `sd-webui-additional-networks` extension to load Lora, use `#addnetlora` instead of `#lora`. It will download the lora to where it supposed to be.
172 |
173 | #### Links
174 | - Links are the main things you wants to be downloaded. Current supported links are from:
175 | - Huggingface (https://huggingface.co/)
176 | - MEGA (https://mega.nz/) {using `mega-cmd`}
177 | - CivitAI (https://civitai.com/)
178 | - Discord attachments (https://cdn.discordapp.com/attachments/)
179 | - catbox (https://files.catbox.moe)
180 | - Github (https://github.com or https://raw.githubusercontent.com)
181 | - Google Drive (https://drive.google.com) {using `gdown`}
182 | - Pixeldrain (https://pixeldrain.com/u/)
183 | - Mediafire (https://www.mediafire.com/file) {using `mediafire-dl`}
184 | - anonfiles (https://anonfiles.com)
185 |
186 | Every links other than that will be ignored. Keep in mind the only supported links are direct download links (see [here](https://github.com/etherealxx/batchlinks-downloader/blob/main/howtogetthedirectlinks.md)). For Huggingface, Civitai (model page link method), Discord attachments, catbox, pixeldrain, and anonfiles, there will be four different method of downloading offered (see [below](https://github.com/etherealxx/batchlinks-webui#huggingfaces-download-method)). For MEGA, it will use `mega-cmd` to download. For Github, if, the link is a raw file, it will download the file. Else, it will use `git clone`, useful to clone extension repo into the webui extension folder.
187 |
188 | More about CivitAI download method [here](https://github.com/etherealxx/batchlinks-webui#civitais-download-method).
189 |
190 | #### Double Hashtag
191 | - Double hashtag means comment. You can put double hashtag in the same line of the link and it will be ignored (keep in mind to put the link first then the double hashtag)
192 |
193 | #### Others
194 | - Other texts will be ignored.
195 |
196 | ### Valid Hashtags
197 |
198 | `#model`, `#models`, `#checkpoint`, or `#checkpoints` will put the downloaded file to _/content/stable-diffusion-webui/models/Stable-diffusion_
199 |
200 | `#embedding`, `#embeddings`, `#embed`, `#embeds`, `#textualinversion`, or `#ti` will put the downloaded file to _/content/stable-diffusion-webui/embeddings_
201 |
202 | `#vae` or `#vaes` will put the downloaded file to _/content/stable-diffusion-webui/models/VAE_
203 |
204 | `#hypernetwork`, `#hypernetworks`, `#hypernet`, `#hypernets`, `#hynet`, or `#hynets` will put the downloaded file to _/content/stable-diffusion-webui/models/hypernetworks_
205 |
206 | `#lora` or `#loras` will put the downloaded file to _/content/stable-diffusion-webui/models/Lora_
207 |
208 | `#addnetlora`, `#loraaddnet`, `#additionalnetworks`, or `#addnet` will put the downloaded file to _/content/stable-diffusion-webui/extensions/sd-webui-additional-networks/models/lora_
209 |
210 | `#aestheticembedding` or `#aestheticembed` will put the downloaded file to _content/stable-diffusion-webui/extensions/stable-diffusion-webui-aesthetic-gradients/aesthetic_embeddings_
211 |
212 | `#controlnet` or `#cnet` will put the downloaded file to _/content/stable-diffusion-webui/extensions/sd-webui-controlnet/models_
213 |
214 | `#upscale` or `#upscaler` will put the downloaded file to _/content/stable-diffusion-webui/models/ESRGAN_
215 |
216 | `#lycoris`, `#locon`, or `#loha` will put the downloaded file to _/content/stable-diffusion-webui/extensions/sd-webui-additional-networks/models/lora/lycoris_
217 |
218 | `#altmodel` or `#altmodels` will put the downloaded file to the path you choose when using `--ckptdir` argument on `launch.py` line. If you didn't use that argument, this hashtag will points to the same directory as `#model`
219 |
220 | _Lycoris/Locon/Loha_ will works just fine if you use `#addnetlora` instead, as long as you have both [addnet extension](https://github.com/kohya-ss/sd-webui-additional-networks) and [locon extension](https://github.com/KohakuBlueleaf/a1111-sd-webui-locon) installed. It's unclear if it's can be loaded by native auto1111 lora. (Tell me if you know more about this.)
221 |
222 | Github links (if it does not contain `/raw/` or `/release/download/` in it) doesn't need hashtag. It will always considered as webui extension, and the repository will be cloned to _/content/stable-diffusion-webui/extensions/(reponame)_
223 |
224 | ### How to get the direct links (Important!)
225 |
226 | See [here](https://github.com/etherealxx/batchlinks-downloader/blob/main/howtogetthedirectlinks.md)
227 |
228 | ### Huggingface's download method
229 |
230 | So there's four supported method: `gdown`, `wget`, `curl` and `aria2`. Use whatever, really. The difference between them are actually little. Myself love using `gdown` since the output is cleaner than the others. `aria2` has the fastest download speed though.
231 |
232 |
253 |
254 | ## Additional Syntax
255 | ### Rename Downloaded Files
256 | Using `>` symbol, you can rename files. Take this for example
257 | 
258 | Mostly catbox file has random name, by using `>` symbol after the link, you can type the desired name on the right. (Don't forget the extension)
259 |
260 | ### Running Shell Commands
261 | You can run shell commands by using `!` in front of the command you want, just like in google colab cells. Then press the `Download All!` button. (Sure, it doesn't download anything, but, well😅)
262 |
263 | 
264 | You can run many lines at once too!
265 |
266 | ### Extract Everyting (`@extract`)
267 | You can use `@extract` to extract every `*.7z`, `*.rar`, and `*.zip` on current directory).
268 | ```
269 | #lora
270 |
271 | @extract
272 | #embed
273 | ```
274 | This will extract everything on Lora folder, since the code runs from top to bottom, and when the `@extract` executes, the current directory is still on Lora folder.
275 |
276 | ### Custom Hashtag Path (`@new`)
277 | You can use `@new ` to make a new usable hashtag and assign a directory path to it.
For example, you type this and press the `Download All!` button:
278 | ```
279 | @new #private /content/stable-diffusion-webui/outputs
280 | #private
281 |
282 | ```
283 | This will make a new hashtag `#private`, make it points to _/content/stable-diffusion-webui/outputs_, set `#private` as current directory, then download whatever into it. You can check if the new hashtag is assigned successfully by looking at the hashtag table on the right/bottom of the screen.
284 |
285 | ### Custom Direct Link Download with `aria2` (`@aria2`)
286 | If there are other direct links that isn't supported by Batchlinks, you can use `@aria` or `@aria2` custom command to download it. The main syntax is:
287 | ```
288 | @aria2 {link} {path/hashtag} > {filename}
289 | ```
290 | You can change `@aria2` with `@aria`, both works. Both the `path/hashtag` and `filename` is optional. This command was made to avoid hassle writing this:
291 | ```
292 | aria2c --summary-interval=1 --console-log-level=error -c -x 16 -s 16 -k 1M {link} -d {path} -o {filename}
293 | ```
294 | There are several ways to use it. Here i use a [20MB download test file](http://212.183.159.230/20MB.zip) as an example.
295 | ```
296 | @aria2 http://212.183.159.230/20MB.zip
297 | ```
298 | ☝️ This will download the file into the current directory (where the hashtags points to). So if before that line you use `#vae`, it will be downloaded to the vae directory. If there are no hashtags before, it'll be sent to the default `#model` directory. The filename will be the last part of the link (`512MB.zip`).
299 | ```
300 | @aria2 http://212.183.159.230/20MB.zip #vae
301 | ```
302 | ☝️ This will download the file into where `#vae` points to (which is the vae directory). You can use custom hashtag here as well.
303 | ```
304 | @aria2 http://212.183.159.230/20MB.zip /content/stable-diffusion-webui
305 | ```
306 | ☝️ This will download the file into the written path, which is _/content/stable-diffusion-webui_. Make sure it is a folder path, not a file path.
307 | ```
308 | @aria2 http://212.183.159.230/20MB.zip /content/stable-diffusion-webui > 20megabytes.zip
309 | ```
310 | ☝️ This will download the file into the written path and rename the file into `20megabytes.zip`. Remember to make sure to write the extension too.
311 |
312 | ## Gradio Queue
313 |
314 | If you use --gradio-queue argument on `launch.py`, some feature will be activated.
315 |
316 | ### Logging
317 |
318 |
319 | Enable logging by pressing the `Turn On Logging` radio button, and wait till `Logging activated` shows up and the box is blinking with orange border. Logging will tell you what are you actually downloading right now on the webui. After your download session is completed, it's recommended to turn back off the feature.
320 | 
321 |
322 | ### Cancel
323 | Pressing `cancel` button while download in progress will stops the current session. Useful when at one time the download speeds is too slow. If you're currently downloading a single item, that item will be cancelled, but the other downloaded one will remain intact.
324 | 
325 |
326 | ### Progress Bar
327 | There will be an additional progress bar that tells you the current activities.
328 | 
329 |
330 | ### Changes if `--gradio-queue` is off
331 | Feature listed above will dissapear, and your only option for download is just `aria2` (speed is priority).
Note that when you pressed the `Download All!` button, **nothing will shows up on the UI**. You need to check the colab console.
332 | 

333 | Another thing to note is your download session will always be cutted every 70 seconds (to prevent hangs/desync).
334 | 
335 | Don't worry, you can continue your session by pressing the `Resume Download` button. It will refresh the links with the one you haven't downloaded yet, then pressing `Download All!` will download the remaining links.
336 | 
337 |
338 | ## Other Features
339 |
340 | ### Notification
341 |
342 | This extension will play sound effect when the download process is completed. It's somewhat buggy at the moment though.
343 |
344 | ### Copy From Pastebin
345 |
346 | You can put your links on pastebin (write it the way you wrote on Batchlinks), put the pastebin link (https://pastebin.com/xxxx) on the link input, and press the 'Copy from Pastebin' button. It will copy all the the links on the pastebin to the UI. Useful to avoid hassle copy and pasting a bunch of text while on mobile.
347 | 
348 |
349 | ### SDless mode
350 | This mode will run the extension without the need of stable-diffusion-webui. Good for my own purpose of debugging it😁.
351 | 
352 |
353 | Copy-paste this on a new colab cell then run it:
354 | ```
355 | from IPython.display import clear_output
356 | !pip3 show virtualenv >/dev/null || pip install -q virtualenv
357 | ![ -d gradiovenv ] || virtualenv gradiovenv
358 | !git clone https://github.com/etherealxx/batchlinks-webui \
359 | /content/stable-diffusion-webui/extensions/batchlinks-webui
360 | pip3 show gradio >/dev/null || pip install -q gradio==3.16.2; \
361 | pip3 show tqdm >/dev/null || pip3 install -q tqdm
362 | clear_output(wait=True)
363 | !source gradiovenv/bin/activate; \
364 | python /content/stable-diffusion-webui/extensions/batchlinks-webui/scripts/batchlinks-downloader.py
365 | ```
366 | or here's the quicker version without venv:
367 | ```
368 | !pip install gradio==3.16.2
369 | !git clone https://github.com/etherealxx/batchlinks-webui /content/stable-diffusion-webui/extensions/batchlinks-webui
370 | !python /content/stable-diffusion-webui/extensions/batchlinks-webui/scripts/batchlinks-downloader.py
371 | ```
372 |
373 | For Windows, run the sdless-windows.bat. Make sure you have `python3` and `gradio` package version 3.16.2 or above. It won't use venv.
374 |
375 | This one for MacOS. Tested on Mojave. Create a shell script and give execute access to it.
376 | ```
377 | #!/usr/bin/env bash
378 | pip3 show virtualenv >/dev/null || pip3 install virtualenv
379 | [ -d gradiovenv ] || virtualenv gradiovenv
380 | git clone https://github.com/etherealxx/batchlinks-webui \
381 | $HOME/Downloads/stable-diffusion-webui/extensions/batchlinks-webui
382 | source gradiovenv/bin/activate; \
383 | pip3 show gradio >/dev/null || pip3 install gradio==3.16.2; \
384 | pip3 show tqdm >/dev/null || pip3 install -q tqdm
385 | source gradiovenv/bin/activate; \
386 | python3 $HOME/Downloads/stable-diffusion-webui/extensions/batchlinks-webui/scripts/batchlinks-downloader.py
387 | ```
388 |
389 | ### Local Installation Support
390 |
391 | This extension was tested to work on Windows 11. But ultimately, the number one priority for me is colab use, so there's might be bugs on Windows 11 one. Maybe also works on Debian-based linux (but you better inspect the source code first).
392 | On Windows, this extension will install [MEGAcmd](https://github.com/meganz/MEGAcmd) for MEGA file download, [wget-windows](https://github.com/webfolderio/wget-windows) for download using `wget`, [aria2-static-builds](https://github.com/q3aql/aria2-static-builds) for download using `aria2`, [7zr and 7z](https://www.7-zip.org) for extracting `aria2` installation, and for `@extract` command.
393 | MacOS is just partially supported.
394 |
395 | Also remember, in order for this extension to work properly, you need make sure that you tick that `add Python 3.10 to PATH` checkbox when you installed Python (Which is already recommended when installing stable-diffusion-webui)
396 | 
397 |
398 | ### Debug Mode (Developer only)
399 |
400 | By manually switching `globaldebug = False` to `True` on the source code, or using `--debug` argument when running the sdless mode, the Debug Mode will be activated. It shows every `print` calls i use to track variables. It will also tracks every files on the models (etc.) paths into a `snapshot.txt` file (if there isn't one).
401 |
402 | There are some batchlinks syntax features that only available on debug mode (putting it on the textbox and click `Download All!`):
403 |
404 | `@debugresetdownload` - This command will check every model (etc.) directory, and remove every file that isn't on `snapshot.txt`. Useful for me to removes file quickly when testing. **Be careful using this on your local installation.**
405 |
406 | `@debugresetdownload` - This command is (supposed to) download a single link with every method available (gdown, curl, wget, aria2), but now i rarely use this command, and haven't updated since. Might be buggy.
407 |
408 | ## Release Notes
409 | Moved [here](https://github.com/etherealxx/batchlinks-webui/blob/main/releasenotes.md). Check it out for latest features & bug fixes.
410 |
411 | ## Roadmap
412 |
413 | - [ ] Add checker for downloaded models (so that it won't download again after the model is downloaded)
414 | - [ ] Different UI for mobile
415 | - [ ] Gradio progress bar
416 | - [ ] Integrating refresh button (`create_refresh_button`) to refresh models etc. all at once
417 | - [ ] Logo change
418 | - [ ] Moving most of the content of this `Readme.md` to Wiki instead
419 | - [ ] Other download sites (s-ul.eu, gitgud, bunkr.ru, icedrive)
420 | - [ ] Separate 'download' and 'run command' function with a subtab
421 | - [ ] Support download MEGA and Google Drive folder
422 | - [ ] Using `yield` instead of gradio's `every`
423 | - [ ] Youtube video (Tutorial)
424 | - [ ] (Crawler) Download every sd-webui related file from a huggingface repo
425 | - [ ] (Crawler) Download every model from a civitai user page
426 |
_
427 | - [x] aria2 for huggingface download method
428 | - [x] Cleaning the code from unnecesarry comments
429 | - [x] Completed download will use the webui's notification.mp3
430 | - [x] Support customizable hashtag from the UI
431 | - [x] Supports Windows local installation
432 | - [x] UI overhaul
433 | - [x] Using threading/subprocess instead of os.system to download files
434 | - [x] (Windows) wget & aria2 support
435 |
436 | ## Known Bugs
437 |
438 | - Progress bar (the yellow bar) doesn't progress as expected
439 | - Sometimes notification sound doesn't play when downloading same file twice in a row
440 | - Sometimes notification sound shows up when starting download, instead of when the download process is completed.
441 | - There's still a chance that the UI of non `--gradio-queue` session and/or onedotsix freezes after a download session
442 | - Windows: The delay between file is downloaded and the output shows is pretty long, and even sometimes the notification comes at the wrong time.
443 |
444 |
445 | ### Known Fixed Bugs
446 |
447 |
448 | 👈Fixed Bugs
449 |
450 |
451 | Links that has bracket in it needs to be 'escaped' (For example, Baka-DiffusionV1(Fp16).safetensors
must be typed Baka-DiffusionV1\(Fp16\).safetensors
)
☝️Fixed in v2.0.0
452 | The delay between file is downloaded and the output shows is really long (1min+) on camenduru's v1.6 colab (Gradio related?)
☝️Seems like fixed in v1.1.0
453 | File downloaded from MEGA will not listed on the output, as it use different download method. There is some delay between the transfare() function complete until it writes the file. I don't know how long the delay is.
☝️Fixed in v1.1.0
454 | Sometimes colab cannot be shut down with a single click on the stop button. Hitting the button several times will raise a KeyboardInterrupt and forcely stopping the cell.
☝️Seems like fixed somewhere on v3.0.0 and after
455 |
456 |
457 |
458 |
459 | ## Contributing
460 |
461 |
462 |
463 | [I no longer actively maintain this project](https://github.com/etherealxx/batchlinks-webui/blob/main/regardingthisrepo.md). if you have a suggestion or code-fixing that would make this better, please notify me in the issue tab, fork the repo and create a pull request.
464 | But still, a star on this project would be nice. Thanks again.
465 |
466 |
475 |
476 | ## Contact
477 |
478 | My Email - gwathon3@gmail.com
479 | My Youtube - [MJ Devlog](https://www.youtube.com/@mjdevlog)
480 |
481 | ## Acknowledgments
482 |
483 | - [Camenduru's Webui Huggingface](https://github.com/camenduru/stable-diffusion-webui-huggingface) - I use his extension as my base (my gradio skill sucks T.T)
484 | - [SD Civitai Browser](https://github.com/Vetchems/sd-civitai-browser) - Civit download script
485 | - [Mega-to-Google-Drive](https://github.com/menukaonline/Mega-to-Google-Drive) - MEGA download script
486 | - [MEGAcmd](https://github.com/meganz/MEGAcmd)
487 | - [mediafire-dl](https://github.com/Juvenal-Yescas/mediafire-dl)
488 | - [Pixeldrain downloader](https://github.com/FayasNoushad/Pixeldrain) - Inspiration for Pixeldrain download method
489 | - [wget-windows](https://github.com/webfolderio/wget-windows)
490 | - [aria2-static-builds](https://github.com/q3aql/aria2-static-builds)
491 | - [7zr, 7z](https://www.7-zip.org)
492 |
--------------------------------------------------------------------------------
/howtogetthedirectlinks.md:
--------------------------------------------------------------------------------
1 | # How to get the direct links
2 |
3 | Jump to: [Huggingface](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#huggingface) - [Civit AI](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#civit-ai) - [MEGA](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#mega) - [Discord](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#discord-attachments) - [Github (Extension)](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#webui-extension-github-repo-link) - [Github (Raw File)](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#raw-file-from-github) - [Github (Release File)](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#release-file-from-github) - [Google Drive](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#google-drive) - [Pixeldrain](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#pixeldrain) - - [Mediafire](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#mediafire) - [Anonfiles](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#anonfiles) - [Dropbox](https://github.com/etherealxx/batchlinks-webui/blob/main/howtogetthedirectlinks.md#dropbox)
4 |
5 | ## Huggingface
6 |
7 | Locate the model page, click on `files` tab.
8 | 
9 | choose the model you want, **right click** the arrow button beside the LFS logo, then click `copy link address`.
10 | 
11 |
12 | ## Civit AI
13 |
14 | There are two ways to download links from Civit. The model page link method, and the direct link method. The model page link method **will automatically** choose the directory of the saved model without even using hashtag, **except** if you want to put Lora into `#addnetlora` directory, you can use that hashtag. You still need to define the output directory when using the direct link method.
15 |
16 | ### Model Page Link Method
17 |
18 | Locate the desired model on the main page of Civitai, **right click** the model thumbnail, then click `copy link address`.
19 | 
20 | You can also click the thumbnail first then copy the link.
21 | 
22 | The advantage of this method is you will also get the model preview images that can shows up on the latest version of [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui).
23 |
24 | ### Direct Link Method
25 |
26 | Locate the model page, **right click** the huge blue button, then click `copy link address`.
27 | 
28 | With this method, you can also choose model variation, by clicking the dropdown icon on the blue button, then **right click** the desired model an.
29 |
30 |
31 | ## MEGA
32 |
33 | Locate the model you wanted, **right click** the item, click `copy link address`.
34 | 
35 | Click the huge green copy button.
36 | 
37 |
38 | ## Discord attachments
39 |
40 | Locate the file you wanted, **right click** the download icon, then click `get link`.
41 | 
42 | Then click `copy link`.
43 | 
44 |
45 | ## webui Extension (Github repo link)
46 |
47 | Locate the extension's repository, then copy the repository link.
48 | 
49 |
50 | Extension **will automatically** cloned/downloaded on `extensions` directory of webui without the need of hashtag.
51 |
52 | ## Raw file from Github
53 |
54 | Locate the file's repository, then click the desired file.
55 | 
56 | Then **right click** the text that says `raw` **or** `download`, and click `copy link address`
57 | 
58 | 
59 |
60 | ## Release file from Github
61 |
62 | Locate the file's repository, then click release section.
63 | 
64 | Then **right click** the desired assets (the blue text), and click `copy link address`
65 | 
66 |
67 | ## Google Drive
68 |
69 | Whether you own the file or not, make sure the file is public. If you owns the file, right click on the file on Google Drive, then click `get link` (or `share`).
70 | 
71 | Then change the access to `anyone with thelink`, and copy the link by clicking the button
72 | 
73 |
74 | These are some valid google drive link patterns that works:
75 | ```
76 | https://drive.google.com/file/d/1-UbwFtSzKTQkvjdPiKLCejH0Ztq9UJFO/view
77 | https://drive.google.com/file/d/1-UbwFtSzKTQkvjdPiKLCejH0Ztq9UJFO/view?usp=share_link
78 | https://drive.google.com/u/0/uc?id=1-UbwFtSzKTQkvjdPiKLCejH0Ztq9UJFO&export=download
79 | https://drive.google.com/u/0/uc?id=1-UbwFtSzKTQkvjdPiKLCejH0Ztq9UJFO&export=download&confirm=t
80 | ```
81 | ## Pixeldrain
82 |
83 | Just click the `copy link` button, or just copy the link.
84 | 
85 |
86 | ## Anonfiles
87 |
88 | **Don't** right click the download button. Instead, copy the link on the url bar
89 | 
90 |
91 | ## Mediafire
92 |
93 | When you arrived on this page, click the chain link icon to copy link, or just copy the link above
94 | 
95 |
96 | Don't forget that when being pasted, the link must starts with `https://www.mediafire.com/file/` (notice the triple W)
97 |
98 | ## Dropbox
99 |
100 | When the item is yours, go to the homepage, and click copy link, the link will be copied to your clipboard
101 | 
102 |
103 | Or, if you got into this screen, just copy the link above, with the link ends with either `?dl=0` or `?dl=1`
104 | 
--------------------------------------------------------------------------------
/images/anonfiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/anonfiles.png
--------------------------------------------------------------------------------
/images/batchlinks_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/batchlinks_logo.png
--------------------------------------------------------------------------------
/images/cancel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/cancel.jpg
--------------------------------------------------------------------------------
/images/checkconsole1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/checkconsole1.jpg
--------------------------------------------------------------------------------
/images/checkconsole2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/checkconsole2.jpg
--------------------------------------------------------------------------------
/images/civit1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/civit1.jpg
--------------------------------------------------------------------------------
/images/civit2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/civit2.jpg
--------------------------------------------------------------------------------
/images/civitnew1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/civitnew1.jpg
--------------------------------------------------------------------------------
/images/civitnew2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/civitnew2.jpg
--------------------------------------------------------------------------------
/images/discord1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/discord1.jpg
--------------------------------------------------------------------------------
/images/discord2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/discord2.jpg
--------------------------------------------------------------------------------
/images/downloaded.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/downloaded.jpg
--------------------------------------------------------------------------------
/images/drive1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/drive1.png
--------------------------------------------------------------------------------
/images/drive2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/drive2.png
--------------------------------------------------------------------------------
/images/dropbox1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/dropbox1.png
--------------------------------------------------------------------------------
/images/dropbox2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/dropbox2.png
--------------------------------------------------------------------------------
/images/example.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/example.jpg
--------------------------------------------------------------------------------
/images/ext_installer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/ext_installer.jpg
--------------------------------------------------------------------------------
/images/github.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/github.jpg
--------------------------------------------------------------------------------
/images/githubdirect1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/githubdirect1.png
--------------------------------------------------------------------------------
/images/githubdirect2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/githubdirect2.png
--------------------------------------------------------------------------------
/images/githubdirect3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/githubdirect3.png
--------------------------------------------------------------------------------
/images/githubrelease1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/githubrelease1.png
--------------------------------------------------------------------------------
/images/githubrelease2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/githubrelease2.png
--------------------------------------------------------------------------------
/images/gradiooff1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/gradiooff1.jpg
--------------------------------------------------------------------------------
/images/gradiooff2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/gradiooff2.jpg
--------------------------------------------------------------------------------
/images/huggingface1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/huggingface1.jpg
--------------------------------------------------------------------------------
/images/huggingface2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/huggingface2.jpg
--------------------------------------------------------------------------------
/images/logging.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/logging.jpg
--------------------------------------------------------------------------------
/images/mediafire.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/mediafire.png
--------------------------------------------------------------------------------
/images/mega1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/mega1.jpg
--------------------------------------------------------------------------------
/images/mega2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/mega2.jpg
--------------------------------------------------------------------------------
/images/pastebin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/pastebin.png
--------------------------------------------------------------------------------
/images/pixeldrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/pixeldrain.png
--------------------------------------------------------------------------------
/images/progress.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/progress.jpg
--------------------------------------------------------------------------------
/images/python.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/python.png
--------------------------------------------------------------------------------
/images/queue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/queue.jpg
--------------------------------------------------------------------------------
/images/rename.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/rename.jpg
--------------------------------------------------------------------------------
/images/sdless.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/sdless.png
--------------------------------------------------------------------------------
/images/shell1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/shell1.jpg
--------------------------------------------------------------------------------
/images/shell2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/images/shell2.jpg
--------------------------------------------------------------------------------
/install.py:
--------------------------------------------------------------------------------
1 | import launch
2 | import platform
3 |
4 | if platform.system() == "Windows":
5 | if not launch.is_installed("gdown"):
6 | launch.run_pip("install gdown", "requirements for Batchlinks Download extension")
7 |
8 | if not launch.is_installed("wget"):
9 | launch.run_pip("install wget", "requirements for Batchlinks Download extension")
--------------------------------------------------------------------------------
/javascript/notification.js:
--------------------------------------------------------------------------------
1 | let prevTextAreaValue = '';
2 | let isTextAreaVisible = true;
3 |
4 | onUiUpdate(function() {
5 | const textArea = gradioApp().querySelectorAll('#tab_batchlinks textarea')?.[2];
6 | const textAreaValue = textArea ? textArea.value : '';
7 |
8 | if (isTextAreaVisible && textAreaValue.startsWith('All done!') && prevTextAreaValue !== textAreaValue) {
9 | gradioApp().querySelector('#finish_audio audio')?.play();
10 | }
11 |
12 | if (!textArea) {
13 | isTextAreaVisible = false;
14 | } else if (textArea.style.display !== 'none') {
15 | isTextAreaVisible = true;
16 | }
17 |
18 | prevTextAreaValue = textAreaValue;
19 | });
20 |
--------------------------------------------------------------------------------
/misc/markydynamictohtml.py:
--------------------------------------------------------------------------------
1 | #tableofcontentmaker
2 | intro = """
3 |
4 | Table of Contents
5 |
6 | """
7 |
8 | outro = """
9 |
10 |
11 |
12 | """
13 |
14 | tableofcontents = """
15 | - [Installation](#installation)
16 | - [About](#about)
17 | - [Example](#example)
18 | - [Syntax](#syntax)
19 | - [Hashtag](#hashtag)
20 | - [Links](#links)
21 | - [Double Hashtag](#double-hashtag)
22 | - [Others](#others)
23 | - [Valid Hashtags](#valid-hashtags)
24 | - [How to get the direct links (Important!)](#how-to-get-the-direct-links-important)
25 | - [Huggingface's download method](#huggingfaces-download-method)
26 | - [Additional Syntax](#additional-syntax)
27 | - [Rename Downloaded Files](#rename-downloaded-files)
28 | - [Running Shell Commands](#running-shell-commands)
29 | - [Extract Everyting (`@extract`)](#extract-everyting-extract)
30 | - [Custom Hashtag Path (`@new`)](#custom-hashtag-path-new)
31 | - [Custom Direct Link Download with `aria2` (`@aria2`)](#custom-direct-link-download-with-aria2-aria2)
32 | - [Gradio Queue](#gradio-queue)
33 | - [Logging](#logging)
34 | - [Cancel](#cancel)
35 | - [Progress Bar](#progress-bar)
36 | - [Changes if `--gradio-queue` is off](#changes-if---gradio-queue-is-off)
37 | - [Other Features](#other-features)
38 | - [Notification](#notification)
39 | - [Copy From Pastebin](#copy-from-pastebin)
40 | - [SDless mode](#sdless-mode)
41 | - [Local Installation Support](#local-installation-support)
42 | - [Debug Mode (Developer only)](#debug-mode-developer-only)
43 | - [Release Notes](#release-notes)
44 | - [Roadmap](#roadmap)
45 | - [Known Bugs](#known-bugs)
46 | - [Known Fixed Bugs](#known-fixed-bugs)
47 | - [Contributing](#contributing)
48 | - [Contact](#contact)
49 | - [Acknowledgments](#acknowledgments)
50 | """
51 |
52 | def mlineprint(toprint):
53 | if toprint[-1] == '\n':
54 | toprint = toprint[:-1]
55 | print(toprint.replace('\n', '', 1))
56 |
57 |
58 | def writehtmllist(space, namehead, hashtaghead):
59 | spaces = ""
60 | for _ in range(space):
61 | spaces += " "
62 | if initialspace >= 2:
63 | return spaces + f''
64 | else:
65 | return spaces + f'{namehead}'
66 |
67 | if __name__ == "__main__":
68 | rows = tableofcontents.replace('\n', '', 1).splitlines()#split('\n') # split into a list of rows
69 | mlineprint(intro)
70 | for row in rows:
71 | if not row == '':
72 | initialspace = 0
73 | for char in row:
74 | if char == ' ':
75 | initialspace += 1
76 | else:
77 | break
78 | extractedrow = row.split("[")
79 | cuttedrow = extractedrow[1].partition("](")
80 | headingname = cuttedrow[0]
81 | headinghashtag = cuttedrow[2].rstrip(")")
82 | print(writehtmllist(initialspace+4, headingname, headinghashtag))
83 | # print(cuttedrow[0])
84 | # print(cuttedrow[2].rstrip(")"))
85 | # hashtagrow = row.partit
86 | mlineprint(outro)
87 |
--------------------------------------------------------------------------------
/notification.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etherealxx/batchlinks-webui/d44bbb5e2a043f2eed80c3945c0f2c676e41d0e5/notification.mp3
--------------------------------------------------------------------------------
/regardingthisrepo.md:
--------------------------------------------------------------------------------
1 | #### Dear beloved Batchlinks user,
2 |
3 | I want to share with you that I have lost interest in this project/repo. It was a time when I was fully invested in it, spending countless hours fixing bugs, adding new features, and learning Python/Markdown. I remember the excitement of refreshing the notification page on Github to find a new bug report or feature request. It was an addicting experience, and I enjoyed it more than my college studies.
4 |
5 | But unfortunately, things have changed, and my mood and interests shift rapidly. I have been into different things throughout my life, such as game development, graphic design, video editing, music creation, gaming, and more. Even though I loved generating images with AI during the early days of stable diffusion, I rarely do it now, since my focus has shifted into this project.
6 |
7 | Despite my change in interests, I want to emphasize that I don't want to abandon this project. I have made many friends through this project, and it holds a special place in my heart. However, I just can't continue to give it the same level of attention and dedication that it deserves. I might still do some small bug fixes, but for adding features, maybe not.
8 |
9 | I will miss those days of working on this project, but I'm excited to explore new interests in gaming, video editing, and many others. I want to thank camenduru, machiavel, 7743, NUROISEA, NoCrypt, and many other people who supported me on various Discord servers, github issues, and many other places. Also thanks to everyone who has contributed to this project, whether by reporting bug, requesting features, pull request, and many others. And lastly, thank YOU for using this tool. It might just helps you a little, but every single user means A LOT to me.
10 |
11 | I hope this continues to thrive and helps people in the future.💖
12 |
13 | Best regards,
14 | MJ
--------------------------------------------------------------------------------
/releasenotes.md:
--------------------------------------------------------------------------------
1 | # Release Notes
2 |
3 | ### Latest Patch: v3.2.1
4 | - Changed "pruned" option to "fp precision" due to CivitAI API changes
5 | - Fixed bug: No matter when the download process is cancelled or not, CivitAI download will give print out notification that the download process is successful
6 | - Fixed bug: Preventing the UI errored out when storage is full and trying to convert jpg image while downloading civitai stuff (PIL.UnidentifiedImageError)
7 | - UI overhaul
8 |
9 | #### Patch: v3.2.0a
10 | - Added support for [vladmandic's fork of automatic1111's webui](https://github.com/vladmandic/automatic)
11 |
12 | ## Latest Release: v3.2.0
13 | - Bug fixed when downloading with aria2 on Windows
14 | - Bug fixed on gradio update checker on sdless batch file for windows
15 | - CivitAi error prevention (when the website is down)
16 | - Cleaned the code of the hashtag system
17 | - `Copy from Pastebin` feature
18 | - Custom aria command with `@aria`
19 | - Making sure custom hashtag doesn't start with number
20 | - New calmer notification sound
21 | - Readme page cleanup
22 | - Some help link changed to link into wiki instead
23 | - UI fix to adapt with latest gradio (v3.23.0)
24 | - Various bug fixes related to CivitAI download
25 |
26 | #### Release v3.1.1b
27 | - Hotfix: Fixed CivitAI link refuses to be downloaded properly. Seems like CivitAI changes its API.
28 | - Fixed several bugs on Windows, especially SDless
29 |
30 | #### Release v3.1.1a
31 | - Hotfix: Fixed CivitAI 'model type chooser' bug and fixed `@extract` bug on colab
32 |
33 | #### Release v3.1.1
34 | - Added some fix in case CivitAI website is down
35 | - Added message when user pressed "Download All!" but the textbox is empty
36 | - Fixed bug where Lycoris folder always shows on downloaded files the first time user download something
37 |
38 | #### Release v3.1.0a
39 | - Hotfix: Indented block on line 1497 fix
40 |
41 | ### Release v3.1.0
42 | - New hashtag: `#altmodel`, when you use `--ckptdir` argument on `launch.py` line, this hashtag will points to that directory. Otherwise, it'll point to the same directory as `#model`
43 | - New hashtag: `#lycoris`, change current save directory to _/content/stable-diffusion-webui/extensions/sd-webui-additional-networks/models/lora/lycoris_
(Side note: _Lycoris/Locon/Loha_ will works just fine if you use `#addnetlora` instead, as long as you have both [addnet extension](https://github.com/kohya-ss/sd-webui-additional-networks) and [locon extension](https://github.com/KohakuBlueleaf/a1111-sd-webui-locon) installed)
44 | - New hashtag: `#upscaler`, change current save directory to _/content/stable-diffusion-webui/models/ESRGAN_ (This was added few commits ago, but i forgot to write it on the release notes)
45 |
46 | Fixes:
47 | - Fixed bug when installing wget/aria2 on Windows
48 | - Fixed sdless scripts so that it also installs `tqdm`
49 | - Fixed bug where custom paths doesn't put CivitAi download (Model Path method) when custom hashtag is used
50 | - Ongoing downloads will correctly stops when Cancel button is pressed on Windows
51 | - CivitAi (Model Path method) now will not download training dataset by accident
52 |
53 | #### Release v3.0.2
54 | - Hotfix: removed a comment that messed up the non queue mode. Now the extension works again without `--gradio-queue`.
55 |
56 | #### Release v3.0.1
57 | - Hotfix: using `urllib.request` instead of `curl -sI` to get the model name on CivitAI direct link method, since it's more reliable (and the curl method always fails somehow). The `requests` method is returned as a fallback.
58 |
59 | ### Release v3.0.0
60 | - Added `@extract` syntax
61 | - (Almost) Full Windows support
62 | - Auto-download config file if available when downloading from CivitAI (SD 2.0+)
63 | - Auto-renaming for downloading ckpt/safetensors and pruned model from CivitAI using direct link method
64 | - CivitAI direct link now use `curl` to get the filename, and use the chosen download method (from the four) to download. Huge download speed boost. `requests` is no longer needed.
65 | - Supports download from Anonfiles, Dropbox, Google Drive, Mediafire, Pixeldrain
66 | - Supports download from Github (raw and release files)
67 | - Supports for custom hashtags with `@new` syntax
68 | - Supports for SDless mode (read more [here](https://github.com/etherealxx/batchlinks-webui#sdless-mode))
69 | - UI overhaul:
70 | - Now there's a table that shows where does the hashtags points into
71 | - Option to stretch the UI, if your monitor is small, or using colab on mobile
72 | - Option to hide help text
73 | - Option to choose preferred CivitAI models. This will works if you download the model via model page link (https://civitai.com/models/)
74 | - Upload txt now use a little button instead of covering half of the screen
75 |
76 | Fixes:
77 | - CivitAI `model page link` no longer randomly download the first model on the json list.
78 | - Most of Windows bugs
79 | - Renaming problem when using CivitAI model page link method
80 | - Warning message when CivitAI download isn't possible (server down)
81 |
82 | ### Older Release
83 |
84 | 👈v2.0.0 - v2.1.1
85 |
86 | Release v2.1.1
87 | Partial Windows support is back
88 | Changes:
89 |
90 | wget
disabled on windows currently, until it fixed
91 |
92 | Fixes:
93 |
94 | gdown
& curl
bug fixed
95 | utf-8
as default encoding for queue checker (fix bug in Windows)
96 |
97 | Release v2.1.0
98 | Features:
99 |
100 | - Supports renaming downloaded file with
>
(for example: https://files.catbox.moe/uarze8.safetensors > neurosama.safetensors
)
101 | - Supports extension usage without
--gradio-queue
(ported from onedotsix)
102 | - Supports running shell command from the UI with
!
(for example: type !pip freeze
, then hit the Download all!
button and see the colab console)
103 | - Progress bar for
--gradio-queue
104 |
105 | Changes:
106 |
107 | aria2
as the only download method when using without --gradio-queue
108 | - Download session will be cut every 80 seconds on when using without
--gradio-queue
(just like onedotsix)
109 | - Debug stopwatch (decorator) won't run automatically when
globaldebug = True
, must be uncommented manually (it disrupt the progress bar)
110 | - Dropped support for webui based on Gradio 3.9 (update your installation, or use onedotsix instead)
111 |
- UI tweak (Smaller font size)
112 |
113 | Release v2.0.0
114 | Features:
115 |
116 | aria2
as download method.
117 | - Cancel button for cancelling download process (
--gradio-queue
required)
118 | - Detection if a CivitAI links no longer exist
119 |
- New hashtags:
#textualinversion
, #ti
, #aestheticembedding
, #aestheticembed
, #controlnet
, and #cnet
120 | - Toggle logging on/off
121 |
shlex.quote
to properly quote links (Thanks @rti7743!)
122 | - Supports cloning webui extensions
123 |
- Supports download from catbox.moe
124 |
- Supports download from CivitAI model links (Thanks @rti7743!)
125 |
- Supports download from Github (repository and raw files)
126 |
- Supports for aesthetic gradients, controlnet model, and extensions path.
127 |
- UI font scaled down
128 |
- Uses
subprocess.Popen
instead of os.system
129 |
_
130 | - Debug snapshot
131 | When globaldebug = True
, the moment this extension launch, it saves the current state of the webui on various location (into snapshot.txt
), and when you type #debugresetdownloads
on the textbox, it will compare the current state and the last saved state, and removes every new file/folder. This will be useful for debugging and testing.
132 | - Debug every download method
133 | When globaldebug = True
and you type #debugevery method
on the textbox, every link that has 4 different method of download (Huggingface etc.) will be downloaded with every method, regardless of the radio button choice. The result is 4 file being downloaded.
134 | - Debug stopwatch
135 | When globaldebug = True
, it will give an output for how long a single download session lasts
136 |
137 |
138 |
--------------------------------------------------------------------------------
/scripts/a_gradiocheck.py:
--------------------------------------------------------------------------------
1 | from modules.shared import cmd_opts
2 | import os, inspect, re #, sys, shlex
3 |
4 | script_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
5 | batchlinks_dir = os.path.join(script_dir, "batchlinks-downloader.py")
6 |
7 | # remove the progress bar if queue off
8 | with open(batchlinks_dir, 'r', encoding='utf-8') as f:
9 | contents = f.read()
10 |
11 | gradio_queue_on = False
12 |
13 | try:
14 | if cmd_opts.gradio_queue: #automatic1111
15 | gradio_queue_on = True
16 | except AttributeError:
17 | if not cmd_opts.disable_queue: #vladmandic
18 | gradio_queue_on = True
19 |
20 | if gradio_queue_on:
21 | #os.system(f"sed -i 's/def run(command, choosedowner):/def run(command, choosedowner, progress=gr.Progress()):/g' {batchlinks_dir}")
22 | # os.system(f"sed -i '/^#progress/s/^#//' {batchlinks_dir}")
23 | new_contents = re.sub(r'^(\s*)#progress\(', r'\1progress(', contents, flags=re.MULTILINE)
24 | contents = re.sub(r'civitvae\):', 'civitvae, progress=gr.Progress()):', new_contents, flags=re.MULTILINE)
25 |
26 | with open(batchlinks_dir, 'w', encoding='utf-8') as f:
27 | f.write(contents)
28 |
29 | else:
30 | # os.system(f"sed -i 's/def run(command, choosedowner, progress=gr.Progress()):/def run(command, choosedowner):/g' {batchlinks_dir}")
31 | # os.system(f"sed -i '/^progress(/ s/^/#/' {batchlinks_dir}")
32 | new_contents = re.sub(r'^(\s*)progress\(', r'\1#progress(', contents, flags=re.MULTILINE)
33 | contents = re.sub(r'civitvae,\s*progress=gr\.Progress\(\)\):$', 'civitvae):', new_contents, flags=re.MULTILINE)
34 |
35 | with open(batchlinks_dir, 'w', encoding='utf-8') as f:
36 | f.write(contents)
--------------------------------------------------------------------------------
/scripts/batchlinks-downloader.py:
--------------------------------------------------------------------------------
1 | #github.com/etherealxx
2 | import os
3 | import time
4 | import gradio as gr
5 | import urllib.request, subprocess, contextlib #these handle mega.nz
6 | import http.client
7 | import requests #this handle civit
8 | import re
9 | from tqdm import tqdm
10 | #from IPython.display import display, clear_output
11 | import pathlib
12 | import inspect
13 | import platform
14 | import shlex
15 | import signal
16 | import sys
17 | sdless = False
18 | try:
19 | from modules import script_callbacks #,scripts
20 | from modules.paths import models_path, script_path #, data_path
21 | from modules.shared import cmd_opts #check for gradio queue
22 | except ImportError: #sdless
23 | if platform.system() == "Windows":
24 | userprofile = os.environ['USERPROFILE']
25 | downloadpath = os.path.join(userprofile, "Downloads")
26 | script_path = os.path.join(downloadpath, "stable-diffusion-webui")
27 | elif platform.system() == "Darwin":
28 | userhome = os.environ['HOME']
29 | downloadpath = os.path.join(userhome, "Downloads")
30 | script_path = os.path.join(downloadpath, "stable-diffusion-webui")
31 | else:
32 | script_path = '/content/stable-diffusion-webui'
33 | models_path = os.path.join(script_path, 'models')
34 | gradio_queue = True
35 | ckpt_dir = None
36 | import sys
37 | import types
38 | module = types.ModuleType('cmd_opts')
39 | module.gradio_queue = gradio_queue
40 | module.ckpt_dir = ckpt_dir
41 | sys.modules['cmd_opts'] = module
42 | import cmd_opts
43 | sdless = True
44 |
45 | script_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
46 | extension_dir = os.path.abspath(os.path.join(script_dir, "../"))
47 | #Version checking{
48 | version_dir = os.path.join(extension_dir, "version.txt")
49 | with open(version_dir, 'r', encoding='utf-8') as file:
50 | curverall = file.readlines()
51 | currentversion = curverall[0].strip()
52 |
53 | try:
54 | versionurl = "https://raw.githubusercontent.com/etherealxx/batchlinks-webui/main/version.txt"
55 | versionresp = requests.get(versionurl)
56 | version_lines = versionresp.text.splitlines()
57 | latestversion = version_lines[0].strip()
58 | except requests.exceptions.RequestException:
59 | latestversion = '??'
60 |
61 | if latestversion != '??':
62 | if currentversion == latestversion:
63 | latestversiontext = ""
64 | else:
65 | latestversiontext = f"[Latest version: {latestversion}]"
66 | else:
67 | latestversiontext = ""
68 |
69 | currentverforlink = latestversion.replace('.', '')
70 | #}
71 |
72 | vladmandic = False
73 | try:
74 | global gradiostate
75 | if cmd_opts.gradio_queue: #automatic1111
76 | gradiostate = True
77 | else:
78 | gradiostate = False
79 | except AttributeError:
80 | try:
81 | if not cmd_opts.disable_queue: #vladmandic
82 | gradiostate = True
83 | else:
84 | gradiostate = False
85 | vladmandic = True
86 | except AttributeError:
87 | gradiostate = False #at this point just use onedotsix
88 | pass
89 |
90 | typechecker = [
91 | "embedding", "embeddings", "embed", "embeds", "textualinversion", "ti",
92 | "model", "models", "checkpoint", "checkpoints",
93 | "vae", "vaes",
94 | "lora", "loras",
95 | "hypernetwork", "hypernetworks", "hypernet", "hypernets", "hynet", "hynets",
96 | "addnetlora", "loraaddnet", "additionalnetworks", "addnet",
97 | "aestheticembedding", "aestheticembed",
98 | "controlnet", "cnet",
99 | "extension", "extensions", "ext", #obsolete
100 | "upscaler", "upscale",
101 | "altmodel", "altmodels",
102 | "lycoris", "locon", "loha"
103 | ]
104 |
105 | typemain = [
106 | "model", "altmodel", "vae",
107 | "embed", "hynet", "lora",
108 | "addnetlora", "aestheticembed", "cnet",
109 | "ext", "upscaler", "lycoris"
110 | ]
111 | countofdefaulthashtags = len(typemain)
112 |
113 | supportedlinks = [
114 | "https://mega.nz",
115 | "https://huggingface.co",
116 | "https://civitai.com/api/download/models",
117 | "https://civitai.com/models/",
118 | "https://cdn.discordapp.com/attachments",
119 | "https://github.com",
120 | "https://raw.githubusercontent.com",
121 | "https://files.catbox.moe",
122 | "https://drive.google.com",
123 | "https://pixeldrain.com",
124 | "https://www.mediafire.com/file",
125 | "https://anonfiles.com",
126 | "https://www.dropbox.com/s"
127 | ]
128 |
129 | modelpath = os.path.join(script_path, "models/Stable-diffusion")
130 | embedpath = os.path.join(script_path, "embeddings")
131 | vaepath = os.path.join(script_path, "models/VAE")
132 | lorapath = os.path.join(script_path, "models/Lora")
133 | addnetlorapath = os.path.join(script_path, "extensions/sd-webui-additional-networks/models/lora")
134 | hynetpath = os.path.join(script_path, "models/hypernetworks")
135 | aestheticembedpath = os.path.join(script_path, "extensions/stable-diffusion-webui-aesthetic-gradients/aesthetic_embeddings")
136 | cnetpath = os.path.join(script_path, "extensions/sd-webui-controlnet/models")
137 | extpath = os.path.join(script_path, "extensions") #obsolete
138 | upscalerpath = os.path.join(script_path, "models/ESRGAN")
139 | lycorispath = os.path.join(addnetlorapath, "lycoris")
140 |
141 | if vladmandic:
142 | cnetpath = os.path.join(script_path, "models/ControlNet")
143 | lycorispath = os.path.join(script_path, "models/LyCORIS")
144 |
145 | if cmd_opts.ckpt_dir:
146 | altmodelpath = cmd_opts.ckpt_dir
147 | currentfolder = altmodelpath
148 | else:
149 | altmodelpath = modelpath
150 | currentfolder = modelpath
151 |
152 | if platform.system() == "Windows":
153 | for x in typemain:
154 | # exec(f"{x}path = os.path.normpath({x}path)")
155 | exec(f"{x}path = {x}path.replace('/', '\\\\')")
156 | #exec(f"print({x}path)")
157 | if os.path.exists(addnetlorapath) and not os.path.exists(lycorispath):
158 | os.makedirs(lycorispath, exist_ok=True)
159 |
160 | newlines = ['\n', '\r\n', '\r']
161 | currentlink = ''
162 | finalwrite = []
163 | currentcondition = ''
164 | currentsuboutput = ''
165 | processid = ''
166 | logging = False
167 | #currentiterfolder = ''
168 | prockilled = False
169 | currentfoldertrack = []
170 | everyprocessid = []
171 | remaininglinks = []
172 | gdownupgraded = False
173 | mediafireinstalled = False
174 | prevciviterror = False
175 | # addedcustompath = []
176 | # ariamode = False
177 |
178 | globaldebug = False #set this to true to activate every debug features
179 | if len(sys.argv) > 1:
180 | if sys.argv[1] == '--debug':
181 | globaldebug = True
182 |
183 | process = ''
184 |
185 | def stopwatch(func): #unused
186 | """
187 | A function that acts as a stopwatch for another function.
188 |
189 | Args:
190 | func (function): The function to be timed.
191 |
192 | Returns:
193 | The return value of the timed function.
194 | """
195 | def wrapper(*args, **kwargs):
196 | if not globaldebug:
197 | return func(*args, **kwargs)
198 | start_time = time.time()
199 | result = func(*args, **kwargs)
200 | end_time = time.time()
201 | elapsed_time = end_time - start_time
202 | minutes = int(elapsed_time // 60)
203 | seconds = elapsed_time % 60
204 | print(f"Time taken by {func.__name__}: {minutes:.0f}m {seconds:.2f}s")
205 | return result
206 | return wrapper
207 |
208 | #debuggingpurpose{
209 | #Hello debuggers! This will track every files when the extension is launched, and
210 | #you can remove every downloaded files after with hashtag '@debugresetdownloads', for debugging purposes on colab
211 | #(Note: You need to fill the textbox with only a single line of @debugresetdownloads and nothing more)
212 | #There's also another feature, '#debugeverymethod', which will download a single link with all four possible methods available.
213 | import shutil
214 | snapshot = []
215 | paths_to_scan = []
216 |
217 | # take a snapshot of the directories
218 | def take_snapshot():
219 | snapshotdir = os.path.join(script_path, 'snapshot.txt')
220 | global snapshot
221 | global paths_to_scan
222 | paths_to_scan = []
223 | for x in typemain:
224 | exec(f"paths_to_scan.append({x}path)")
225 | if os.path.exists(snapshotdir):
226 | with open(snapshotdir, 'r') as f:
227 | snapshot = [line.strip() for line in f.readlines()]
228 | print("Batchlinks extension: snapshot already exist.")
229 | return
230 | else:
231 | snapshot = []
232 | for path in paths_to_scan:
233 | if os.path.exists(path):
234 | pathtemp = os.listdir(path)
235 | for file in pathtemp:
236 | pathoffile = os.path.join(path, file)
237 | snapshot.append(pathoffile)
238 | with open(snapshotdir, 'w') as f:
239 | for item in snapshot:
240 | f.write(f"{item}\n")
241 | print("Batchlinks extension: snapshot taken.")
242 |
243 | # rewind everything to a fresh state
244 | def global_rewind():
245 | global paths_to_scan
246 | global path
247 | global currentsuboutput
248 | removedall, removed_files, removed_dirs, new_snapshot = [], [], [], []
249 | print('[1;32mDebug rewind initiated...')
250 | print('[0m')
251 | for path in paths_to_scan:
252 | if os.path.exists(path):
253 | pathtemp = os.listdir(path)
254 | for file in pathtemp:
255 | pathoffile = os.path.join(path, file)
256 | new_snapshot.append(pathoffile)
257 | toremoves = [x for x in new_snapshot if x not in snapshot]
258 | for fileordir in toremoves:
259 | if os.path.exists(fileordir):
260 | if os.path.isdir(fileordir):
261 | try:
262 | shutil.rmtree(fileordir)
263 | removed_dirs.append(fileordir)
264 | except PermissionError as e:
265 | print("Error: " + str(e))
266 | else:
267 | os.remove(fileordir)
268 | removed_files.append(fileordir)
269 | if removed_files or removed_dirs:
270 | print("Removed files:")
271 | removedall.append("Removed files:")
272 | for file in removed_files:
273 | print(file)
274 | removedall.append(file)
275 | print("Removed directories:")
276 | removedall.append("Removed directories:")
277 | for dir in removed_dirs:
278 | print(dir)
279 | removedall.append(dir)
280 | print('[1;32mrewind completed')
281 | print('[0m')
282 | return removedall
283 |
284 | # Take a snapshot of the directories
285 | if globaldebug == True:
286 | take_snapshot()
287 | # }
288 |
289 | def printdebug(toprint):
290 | if globaldebug == True:
291 | print(toprint)
292 |
293 | def printvardebug(toprint):
294 | if globaldebug == True:
295 | import inspect
296 | caller = inspect.currentframe().f_back
297 | try:
298 | name = [k for k, v in caller.f_locals.items() if v is toprint][0]
299 | except IndexError:
300 | name = "idk"
301 | if not isinstance(toprint, str):
302 | toprint = str(toprint)
303 | print(f"debug {name}: {toprint}")
304 |
305 | def runwithsubprocess(rawcommand, folder=None, justrun=False, additionalcontext=''): #@note runwithsubprocess
306 |
307 | commandtorun = shlex.split(rawcommand) #construct_command(rawcommand)
308 | printdebug(f"raw command: {rawcommand}")
309 | printdebug(f"merged command: {commandtorun}")
310 | global prockilled
311 | global everyprocessid
312 | # if gradiostate == False and not rawcommand.startswith("aria"):
313 | # subprocess.run(commandtorun, stderr=subprocess.STDOUT, universal_newlines=True)
314 | # else:
315 | if folder != None:
316 | printdebug("debug folderforsavestate: " + str(folder))
317 | savestate_folder(folder)
318 | printdebug("debug currentfoldertrack: " + str(currentfoldertrack))
319 |
320 | global process
321 | if justrun:
322 | process = subprocess.Popen(rawcommand, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=True)
323 | else:
324 | process = subprocess.Popen(commandtorun, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
325 | global processid
326 | processid = process.pid
327 | everyprocessid.append(processid)
328 | printdebug("debug processid: " + str(processid))
329 |
330 | ariacomplete = False
331 | global currentsuboutput
332 | while True:
333 | # Read the output from the process
334 | nextline = process.stdout.readline()
335 | if nextline == '' and process.poll() is not None:
336 | break
337 | # Check if the line contains progress information
338 | elif additionalcontext == 'aria2':
339 | if 'Download Results' in nextline:
340 | ariacomplete = True
341 | print('\n')
342 | print(nextline, end='')
343 | currentsuboutput = nextline
344 | else:
345 | if not ariacomplete:
346 | match = re.search(r'\[[^\]]+\]', nextline)
347 | if match:
348 | stripnext = match.group().strip()
349 | print("\r", end="")
350 | print(f"\r{stripnext}", end='')
351 | currentsuboutput = stripnext
352 | else:
353 | print(nextline, end='')
354 | currentsuboutput = nextline
355 | elif additionalcontext == '7z':
356 | sevenzmessage = [
357 | "Extracting", "Everything", "ERROR"
358 | ]
359 | if nextline.strip().startswith(tuple(sevenzmessage)):
360 | print(nextline, end=' ')
361 | currentsuboutput = nextline
362 | else:
363 | stripnext = nextline.strip()
364 | print("\r", end="")
365 | print(f"\r{stripnext}", end='')
366 | currentsuboutput = stripnext
367 | else:
368 | if justrun:
369 | print(nextline, end='')
370 | else:
371 | if "%" in nextline.strip() or rawcommand.startswith("curl"):
372 | stripnext = nextline.strip()
373 | print("\r", end="")
374 | print(f"\r{stripnext}", end='')
375 | else:
376 | print(nextline, end='')
377 | if additionalcontext == 'mega':
378 | process.kill()
379 | if additionalcontext == 'mega':
380 | if nextline.strip().startswith("Download finished:"):
381 | _ = subprocess.getoutput("taskkill /f /t /im MEGAcmdServer.exe")
382 | printdebug("MEGA server killed")
383 | currentsuboutput = nextline
384 |
385 |
386 | process.wait()
387 | currentsuboutput = ''
388 | processid = ''
389 | if prockilled == True:
390 | printdebug("before rewind folder")
391 | printvardebug(folder)
392 | rewind_folder(folder)
393 | print('[1;31mOperation Cancelled')
394 | print('[0m')
395 | global currentcondition
396 | currentcondition = 'Operation Cancelled'
397 | return
398 |
399 | #these code below handle mega.nz
400 | def unbuffered(proc, stream='stdout'):
401 | stream = getattr(proc, stream)
402 | with contextlib.closing(stream):
403 | while prockilled == False:
404 | out = []
405 | last = stream.read(1)
406 | # Don't loop forever
407 | if last == '' and proc.poll() is not None:
408 | break
409 | while last not in newlines:
410 | # Don't loop forever
411 | if last == '' and proc.poll() is not None:
412 | break
413 | out.append(last)
414 | last = stream.read(1)
415 | out = ''.join(out)
416 | yield out
417 |
418 | def transfare(todownload, folder, torename=''):
419 | #import codecs
420 | #decoder = codecs.getincrementaldecoder("UTF-8")()
421 | todownload_s = shlex.quote(todownload)
422 | folder_s = shlex.quote(folder)
423 | if platform.system() == "Windows":
424 | savestate_folder(folder)
425 | localappdata = os.environ['LOCALAPPDATA']
426 | megagetloc = os.path.join(localappdata, "MEGAcmd", "mega-get.bat")
427 | global process
428 | process = runwithsubprocess(f"{shlex.quote(megagetloc)} {todownload_s} {folder_s}", folder, False, 'mega')
429 | else:
430 | savestate_folder(folder_s)
431 | cmd = ["mega-get", todownload_s, folder_s]
432 | proc = subprocess.Popen(
433 | cmd,
434 | stdout=subprocess.PIPE,
435 | stderr=subprocess.STDOUT,
436 | universal_newlines=True,
437 | )
438 | global processid
439 | global everyprocessid
440 | processid = proc.pid
441 | everyprocessid.append(processid)
442 |
443 | global currentsuboutput
444 | for line in unbuffered(proc):
445 | if prockilled == False:
446 | if not line.startswith("Download"):
447 | currentsuboutput = line
448 | print(f"\r{line}", end="")
449 | else:
450 | print(f"\n{line}")
451 | _ = subprocess.getoutput("pkill -f \"mega-cmd-server\"")
452 | printdebug("MEGA server killed")
453 |
454 | else:
455 | currentsuboutput = ''
456 | print('[1;31mOperation Cancelled')
457 | print('[0m')
458 | global currentcondition
459 | currentcondition = 'Operation Cancelled'
460 | return
461 | currentsuboutput = ''
462 | if torename:
463 | listfilenew = os.listdir(folder)
464 | newerfoldertrack = []
465 | for file in listfilenew:
466 | pathoffile = os.path.join(folder, file)
467 | newerfoldertrack.append(pathoffile)
468 | checkrename = [x for x in newerfoldertrack if x not in currentfoldertrack]
469 | if checkrename:
470 | # renamedfile = os.path.basename(checkrename[0])
471 | # pathtorename = os.path.join(folder, renamedfile)
472 | os.rename(checkrename[0], os.path.join(folder, torename))
473 |
474 |
475 | def installmega():
476 | HOME = os.path.expanduser("~")
477 | ocr_file = pathlib.Path(f"{HOME}/.ipython/ocr.py")
478 | if not ocr_file.exists():
479 | hCode = "https://raw.githubusercontent.com/biplobsd/" \
480 | "OneClickRun/master/res/ocr.py"
481 | urllib.request.urlretrieve(hCode, str(ocr_file))
482 |
483 | from importlib.util import module_from_spec, spec_from_file_location
484 | ocr_spec = spec_from_file_location("ocr", str(ocr_file))
485 | ocr = module_from_spec(ocr_spec)
486 | ocr_spec.loader.exec_module(ocr)
487 |
488 | if not os.path.exists("/usr/bin/mega-cmd"):
489 | #ocr.loadingAn()
490 | print('[1;32mInstalling MEGA ...')
491 | print('[0m')
492 | ocr.runSh('sudo apt-get -y update')
493 | ocr.runSh('sudo apt-get -y install libmms0 libc-ares2 libc6 libcrypto++6 libgcc1 libmediainfo0v5 libpcre3 libpcrecpp0v5 libssl1.1 libstdc++6 libzen0v5 zlib1g apt-transport-https')
494 | ocr.runSh('sudo curl -sL -o /var/cache/apt/archives/MEGAcmd.deb https://mega.nz/linux/MEGAsync/Debian_9.0/amd64/megacmd-Debian_9.0_amd64.deb', output=True)
495 | ocr.runSh('sudo dpkg -i /var/cache/apt/archives/MEGAcmd.deb', output=True)
496 | print('[1;32mMEGA is installed.')
497 | print('[0m')
498 |
499 | def installmegawin():
500 | userprofile = os.environ['USERPROFILE']
501 | localappdata = os.environ['LOCALAPPDATA']
502 | megagetloc = os.path.join(localappdata, "MEGAcmd", "mega-get.bat")
503 | megacmdloc = os.path.join(userprofile, "Downloads", "MEGAcmdSetup64.exe")
504 | if not os.path.exists(megagetloc):
505 | print('[1;32mInstalling MEGA ...')
506 | print('[0m')
507 | runwithsubprocess(f"curl -o {shlex.quote(megacmdloc)} https://mega.nz/MEGAcmdSetup64.exe")
508 | time.sleep(1)
509 | runwithsubprocess(f"{shlex.quote(megacmdloc)} /S")
510 | time.sleep(4)
511 | print('[1;32mMEGA is installed.')
512 | print('[0m')
513 | #clear_output()
514 | #these code above handle mega.nz
515 |
516 | def civitdown(url, folder, torename=''):
517 | filename = url.split('?')[0].rsplit('/', 1)[-1] + ".bdgh"
518 | pathtodown = os.path.join(folder, filename)
519 | max_retries = 5
520 | retry_delay = 10
521 | # url_s = quote(url)
522 |
523 | while prockilled == False:
524 |
525 | downloaded_size = 0
526 | headers = {}
527 |
528 | progress = tqdm(total=1000000000, unit="B", unit_scale=True, desc=f"Downloading {filename}. (will be renamed correctly after downloading)", initial=downloaded_size, leave=False)
529 | global currentsuboutput
530 | global currentcondition
531 | with open(pathtodown, "ab") as f:
532 | while prockilled == False:
533 | try:
534 | response = requests.get(url, headers=headers, stream=True)
535 | total_size = int(response.headers.get("Content-Length", 0))
536 | # if total_size == 0:
537 | # total_size = downloaded_size
538 | # progress.total = total_size
539 |
540 |
541 | for chunk in response.iter_content(chunk_size=1024):
542 | if chunk and prockilled == False:
543 | f.write(chunk)
544 | progress.update(len(chunk))
545 | currentsuboutput = str(progress)
546 | else:
547 | break
548 |
549 | downloaded_size = os.path.getsize(pathtodown)
550 | currentsuboutput = ''
551 | break
552 | except ConnectionError as e:
553 | max_retries -= 1
554 |
555 | if max_retries == 0:
556 | raise e
557 |
558 | time.sleep(retry_delay)
559 |
560 | progress.close()
561 | if prockilled == True:
562 | if os.path.exists(pathtodown):
563 | os.remove(pathtodown)
564 | print('[1;31mOperation Cancelled')
565 | print('[0m')
566 | currentcondition = 'Operation Cancelled'
567 | currentsuboutput = ''
568 | return "Operation Cancelled"
569 |
570 | if torename:
571 | actualfilename = torename
572 | else:
573 | try:
574 | actualfilename = response.headers['Content-Disposition'].split("filename=")[1].strip('"')
575 | except KeyError:
576 | print('[1;31mLink Error')
577 | print('[0m')
578 | break
579 | #%cd {folder}
580 | actualpath = os.path.join(folder, actualfilename)
581 | os.rename(pathtodown, actualpath)
582 | downloaded_size = os.path.getsize(actualpath)
583 | # Check if the download was successful
584 | if downloaded_size >= total_size:
585 | print(f"{actualfilename} successfully downloaded.")
586 | break
587 | else:
588 | print(f"Error: File download failed. Retrying...")
589 |
590 | def getcivitname(link, frommodeltypechooser=False): #@note getcivitname
591 | # searcher = "findstr" if platform.system() == "Windows" else "grep"
592 | printdebug("getcivitname:")
593 | printvardebug(link)
594 | global currentcondition
595 | tempcondition = currentcondition
596 | currentcondition = "Connecting to CivitAI..."
597 | try:
598 | req = urllib.request.Request(link, headers={'User-Agent': 'Mozilla/5.0'})
599 | response = urllib.request.urlopen(req, timeout=7)
600 | contentdis = response.geturl()
601 | # contentdis = [line for line in subprocess.getoutput(f"curl -sI {link} | {searcher} -i content-disposition").splitlines() if line.startswith('location')][0]
602 | except Exception as e:
603 | errortype = str(type(e))
604 | printdebug("Error: " + errortype)
605 | if 'TimeoutError' in errortype:
606 | print("Unable to connect to CivitAI in 7 seconds. Skipping the link...")
607 | currentcondition = tempcondition
608 | return 'batchlinksskip'
609 | else:
610 | if frommodeltypechooser:
611 | currentcondition = tempcondition
612 | return ''
613 | else:
614 | print("Cannot get the filename. Download will continue with the old slow method.\nReason:" + str(e))
615 | currentcondition = tempcondition
616 | return 'batchlinksold'
617 | if contentdis == "https://civitai.com/":
618 | print('[1;31mCivitAI website is currently down ツ')
619 | print('[0m')
620 | currentcondition = tempcondition
621 | return 'batchlinksskip'
622 | cuttedcontent = contentdis.find('response-content-disposition=attachment%3B%20filename%3D%22') + 59
623 | # filename = str(contentdis[cuttedcontent:]).replace('%22&x-id=GetObject', '') #obsolete since 30-03-2023
624 | filename = str(contentdis[cuttedcontent:]).partition('%22&')[0]
625 | # filename = contentdis.split('filename=')[-1].split('%22')[1] #this also works i guess
626 | filename = civitmodeltypename(filename, link)
627 | currentcondition = tempcondition
628 | return filename
629 |
630 | def getcivitname2(link):
631 | searcher = "findstr" if platform.system() == "Windows" else "grep"
632 | try:
633 | contentdis = [line for line in subprocess.getoutput(f"curl -sI {link} | {searcher} -i content-disposition").splitlines() if line.startswith('location')][0]
634 | cuttedcontent = contentdis.find('response-content-disposition=attachment%3B%20filename%3D%22') + 59
635 | filename = str(contentdis[cuttedcontent:]).replace('%22&x-id=GetObject', '')
636 | if filename:
637 | return [0, link, filename]
638 | except:
639 | return []
640 |
641 | def civitmodeltypename(name, filelink):
642 | nameonly, extension = os.path.splitext(name)
643 | if 'type=Pruned' in filelink:
644 | nameonly += "_pruned"
645 |
646 | if 'format=Safetensor' in filelink:
647 | extension = '.safetensors'
648 | elif 'format=PickleTensor' in filelink:
649 | extension = '.ckpt'
650 |
651 | nameoffile = nameonly + extension
652 | return nameoffile
653 |
654 | def checkcivitconfig(link): #check if the current civit link has a config file (v2.0+) @note checkcivitconfig
655 | def getrequest(link2):
656 | response = requests.get(link2)
657 | return response.status_code
658 |
659 | params = ['?type=Config','?type=Config&format=Other']
660 |
661 | for param in params:
662 | response = getrequest(link + param)
663 | if response == 200:
664 | #print('The link exists')
665 | return link + param
666 | return ''
667 |
668 | def civitmodeltypechooser(modeljson, prunedmodel, torchortensor, linkandnames):
669 |
670 | # prunedorfull = ['?type=Model', '?type=Pruned%20Model']
671 | # if prunedmodel == 'True':
672 | prunedorfull = ['&fp=fp32', '&fp=fp16']
673 | if prunedmodel == 'fp16':
674 | prunedorfull.reverse()
675 | pickleorsafe = ['&format=SafeTensor', '&format=PickleTensor']
676 | if torchortensor=='ckpt':
677 | pickleorsafe.reverse()
678 |
679 | defaultlinkurl = [link.get('downloadUrl') for link in modeljson['modelVersions'][0]['files'] if not '?type=' in link.get('downloadUrl')][0]
680 |
681 | indexlinkname = list()
682 | checklater = ''
683 | for index, (link, name) in enumerate(linkandnames.items()):
684 | if prunedorfull[0] in link:
685 | if pickleorsafe[0] in link:
686 | indexlinkname.extend([index, link, name])
687 | break
688 | else:
689 | checklater = link
690 | continue
691 | else:
692 | continue
693 |
694 | if checklater and not indexlinkname:
695 | for index, (link, name) in enumerate(linkandnames.items()):
696 | if checklater == link:
697 | indexlinkname.extend([index, link, name])
698 | checklater = ''
699 | break
700 |
701 | if not indexlinkname:
702 | indexlinkname = getcivitname(defaultlinkurl+prunedorfull[0]+pickleorsafe[0], True) #@note indexlinkname
703 | if indexlinkname == "batchlinksskip" or not indexlinkname:
704 | indexlinkname = list()
705 |
706 | if not indexlinkname:
707 | for index, (link, name) in enumerate(linkandnames.items()):
708 | if defaultlinkurl == link:
709 | indexlinkname.extend([index, link, name])
710 | break
711 |
712 | return indexlinkname
713 |
714 | #thank you @rti7743 for this part {
715 | def civitdown2_get_json(url):
716 | import re
717 | m = re.search(r'https://civitai.com/models/(\d+)', url)
718 | model_id = m.group(1)
719 |
720 | api_url = "https://civitai.com/api/v1/models/" + model_id
721 |
722 | import requests
723 | txt = requests.get(api_url).text
724 |
725 | import json
726 | try:
727 | return json.loads(txt)
728 | except json.decoder.JSONDecodeError:
729 | return 'error'
730 |
731 | def civitdown2_get_save_directory(model_type, default_folder):
732 | printdebug("model_type: " + model_type)
733 | customtypemains = list(typemain[countofdefaulthashtags:])
734 | printdebug("customtypemains: " + str(customtypemains))
735 | for type in customtypemains:
736 | xpath = eval(type + "path")
737 | if default_folder == xpath:
738 | printdebug("customtype returned")
739 | return default_folder
740 | if model_type == "Checkpoint":
741 | if cmd_opts.ckpt_dir and default_folder == altmodelpath:
742 | return altmodelpath
743 | else:
744 | return modelpath
745 | elif model_type == "Hypernetwork":
746 | return hynetpath
747 | elif model_type == "TextualInversion":
748 | return embedpath
749 | elif model_type == "AestheticGradient":
750 | return aestheticembedpath
751 | elif model_type == "VAE":
752 | return vaepath
753 | elif model_type == "LORA":
754 | if default_folder == addnetlorapath:
755 | return addnetlorapath
756 | else:
757 | return lorapath
758 | elif model_type == "LoCon":
759 | return lycorispath
760 | else:
761 | printdebug("defaultfolder returned")
762 | return default_folder
763 |
764 | def civitdown2_convertimage(imagejpg_save_path, imagepng_save_path):
765 | from PIL import Image
766 | try:
767 | img = Image.open(imagejpg_save_path)
768 | img_resized = img.resize((img.width // 2, img.height // 2))
769 | img_resized.save(imagepng_save_path)
770 | if os.path.exists(imagejpg_save_path):
771 | os.remove(imagejpg_save_path)
772 | except Exception as e:
773 | print("Civitai image convertion failed. Reason: " + str(e))
774 | pass
775 |
776 | def civitdown2(url, folder, downloader, renamedfilename, isdebugevery, modeldefaulttype, isprunedmodel, isdownvae): #@note civitdown2
777 | def civitlinkandnamer(model):
778 | linkandname = dict()
779 | for i, link in enumerate(model['modelVersions'][0]['files']):
780 | name = link.get('name')
781 | url = link.get('downloadUrl')
782 | if name and url:
783 | linkandname[url] = name
784 | # print(i, url, name)
785 | else:
786 | pass
787 | return linkandname
788 |
789 | model = civitdown2_get_json(url)
790 | if model == 'error':
791 | print('[1;31mFailed retrieving the model data.')
792 | print('[1;31mCivitAI website might going down currently.')
793 | print('[0m')
794 | return
795 |
796 | if model == {'error': 'Model not found'}:
797 | print('[1;31mModel ' + url + ' is not available anymore')
798 | print('[0m')
799 | return
800 |
801 | save_directory = civitdown2_get_save_directory(model['type'], folder)
802 | if not os.path.exists(save_directory):
803 | os.makedirs(save_directory, exist_ok=True)
804 | try:
805 | parameter = url.split("?")[-1] + "?"
806 | except:
807 | parameter = ''
808 |
809 | civitlinkandnames = civitlinkandnamer(model)
810 |
811 | if model['type'] == "Checkpoint":
812 | data_index, data_url, data_filename = civitmodeltypechooser(model, isprunedmodel, modeldefaulttype, civitlinkandnames) #@note
813 | data_filename = civitmodeltypename(data_filename, data_url)
814 | image_filename = model['modelVersions'][0]['files'][0]['name']
815 | else:
816 | data_url = model['modelVersions'][0]['files'][0]['downloadUrl']
817 | data_filename = model['modelVersions'][0]['files'][0]['name']
818 | if "?type=Training%20Data" in data_url:
819 | data_url = data_url.partition("?")[0]
820 | wannabename = getcivitname(data_url)
821 | if wannabename:
822 | if wannabename != 'batchlinksskip':
823 | data_filename = wannabename
824 | else:
825 | return ''
826 | image_filename = data_filename
827 |
828 | if renamedfilename:
829 | data_filename = renamedfilename
830 | image_filename = data_filename
831 |
832 | image_url = model['modelVersions'][0]['images'][0]['url']
833 | printvardebug(data_url)
834 | printvardebug(data_filename)
835 | printvardebug(image_url)
836 | if model['type'] == "TextualInversion":
837 | image_filename_jpg = pathlib.PurePath(image_filename).stem + ".preview.jpg"
838 | image_filename_png = pathlib.PurePath(image_filename).stem + ".preview.png"
839 | else:
840 | image_filename_jpg = pathlib.PurePath(image_filename).stem + ".jpg"
841 | image_filename_png = pathlib.PurePath(image_filename).stem + ".png"
842 |
843 | data_save_path = os.path.join(save_directory, data_filename)
844 | imagejpg_save_path = os.path.join(save_directory, image_filename_jpg)
845 | imagepng_save_path = os.path.join(save_directory, image_filename_png)
846 |
847 | currentmode = 'civit'
848 | if isdebugevery:
849 | currentmode = 'civitdebugevery'
850 |
851 | printdebug(f"debug download_url({data_url}, {data_save_path}, {downloader})")
852 | if prockilled == False:
853 | hfdown(data_url, data_save_path, downloader, currentmode)
854 | printdebug("normal download done, now check for the config")
855 | config_url = checkcivitconfig(data_url)
856 | vae_url, vae_name = '',''
857 | for link, name in civitlinkandnames.items():
858 | if '?type=VAE' in link:
859 | vae_url, vae_name = link, name
860 | break
861 | if model['type'] == "Checkpoint":
862 | if prockilled == False and isdownvae and vae_url:
863 | namefile = os.path.splitext(data_filename)[0]
864 | vae_name = namefile + '.vae.pt'
865 | data_save_path = os.path.join(os.path.dirname(data_save_path), vae_name)
866 | hfdown(vae_url, data_save_path, downloader, currentmode)
867 | if prockilled == False and config_url:
868 | namefile = os.path.splitext(data_filename)[0]
869 | config_name = namefile + '.yaml'
870 | data_save_path = os.path.join(os.path.dirname(data_save_path), config_name)
871 | hfdown(config_url, data_save_path, downloader, currentmode)
872 |
873 | if prockilled == False:
874 | hfdown(image_url, imagejpg_save_path, downloader, currentmode)
875 | if os.path.exists(imagejpg_save_path):
876 | civitdown2_convertimage(imagejpg_save_path, imagepng_save_path)
877 | else:
878 | print(imagejpg_save_path + " doesn't exist")
879 | if prockilled == False:
880 | print(f"{data_save_path} successfully downloaded.")
881 |
882 | if isdebugevery:
883 | additionalname = '-' + downloader
884 |
885 | if model['type'] == "TextualInversion":
886 | image_filename_jpg = pathlib.PurePath(data_filename).stem + additionalname + ".preview.jpg"
887 | image_filename_png = pathlib.PurePath(data_filename).stem + additionalname + ".preview.png"
888 | else:
889 | image_filename_jpg = pathlib.PurePath(data_filename).stem + additionalname + ".jpg"
890 | image_filename_png = pathlib.PurePath(data_filename).stem + additionalname + ".png"
891 |
892 | imagejpg_save_path = os.path.join(save_directory, image_filename_jpg)
893 | imagepng_save_path = os.path.join(save_directory, image_filename_png)
894 |
895 | #}
896 | def mediadrivedown(todownload, folder, mode, torename=''): #@note mediadrivedown
897 | todownload_s = shlex.quote(todownload)
898 | if platform.system() == "Windows":
899 | folder_s = folder
900 | else:
901 | folder_s = shlex.quote(folder)
902 | prevcurdir = os.getcwd()
903 | os.chdir(folder)
904 | savestate_folder(folder_s)
905 |
906 | if mode=='gdrive':
907 | runwithsubprocess(f"gdown --fuzzy {todownload_s}", folder_s)
908 | elif mode=='mediafire':
909 | runwithsubprocess(f"mediafire-dl {todownload_s}", folder_s)
910 |
911 | os.chdir(prevcurdir)
912 | if torename:
913 | listfilenew = os.listdir(folder)
914 | newerfoldertrack = []
915 | for file in listfilenew:
916 | pathoffile = os.path.join(folder, file)
917 | newerfoldertrack.append(pathoffile)
918 | checkrename = [x for x in newerfoldertrack if x not in currentfoldertrack]
919 | if checkrename:
920 | os.rename(checkrename[0], os.path.join(folder, torename))
921 |
922 | def hfdown(todownload, folder, downloader, mode='default', torename=''): #@note hfdown
923 | global currentcondition
924 | global prevciviterror
925 | prevciviterror = False
926 | # global ariamode
927 | if mode=='civit' or mode=='civitdebugevery':
928 | filename = pathlib.Path(folder).name
929 | filename_s = shlex.quote(filename)
930 | filepath = folder
931 | filepath_s = shlex.quote(folder)
932 | todownload_s = todownload
933 | folder = os.path.dirname(filepath)
934 | # folder_s = pathlib.Path(folder).parent.resolve()
935 | folder_s = shlex.quote(folder)
936 | folder_win = folder
937 | else:
938 | if mode=='pixeldrain' or mode=='dropbox':
939 | filename = torename
940 | else:
941 | filename = todownload.rsplit('/', 1)[-1]
942 | filename_s = shlex.quote(filename)
943 | filepath = os.path.join(folder, filename)
944 | filepath_s = shlex.quote(filepath)
945 | todownload_s = shlex.quote(todownload)
946 | folder_s = shlex.quote(folder)
947 | folder_win = folder
948 | ariafilename_s = filename_s
949 | if torename:
950 | ariafilename_s = shlex.quote(torename)
951 |
952 | printvardebug(folder)
953 | printvardebug(todownload)
954 | printvardebug(filename)
955 | printvardebug(filename_s)
956 | printvardebug(filepath)
957 | printvardebug(filepath_s)
958 | printvardebug(todownload_s)
959 | printvardebug(folder_s)
960 | printvardebug(folder_win)
961 | #savestate_folder(folder_s)
962 |
963 | if platform.system() == "Windows": #@note windows downloader
964 | localappdata = os.environ['LOCALAPPDATA']
965 | batchlinksinstallpath = os.path.join(localappdata, "batchlinks")
966 | printvardebug(batchlinksinstallpath)
967 | wgetpath = os.path.join(batchlinksinstallpath, "wget-gnutls-x64.exe")
968 |
969 | ariazippath = os.path.join(batchlinksinstallpath, "aria2-1.36.0-win-64bit-build2.7z")
970 | ariapath = os.path.join(batchlinksinstallpath, "aria2-1.36.0-win-64bit-build2", "aria2c.exe")
971 | os.makedirs(batchlinksinstallpath, exist_ok=True)
972 | if os.path.exists(batchlinksinstallpath):
973 | printdebug("batchlinksinstallpath created")
974 | if downloader=='gdown':
975 | global gdownupgraded
976 | if gdownupgraded == False:
977 | tempcondition = currentcondition
978 | currentcondition = "Upgrading gdown..."
979 | print('[1;32mUpgrading gdown ...')
980 | print('[0m')
981 | runwithsubprocess(f"pip3 install -q --upgrade --no-cache-dir gdown")
982 | print('[1;32mgdown upgraded!')
983 | print('[0m')
984 | currentcondition = tempcondition
985 | gdownupgraded = True
986 | try:
987 | runwithsubprocess(f"gdown {todownload_s} -O {filepath_s}", folder_win)
988 | except FileNotFoundError:
989 | import gdown
990 | gdown.download(todownload, filepath, quiet=False)
991 | elif downloader=='wget':
992 | #os.system("python -m wget -o " + os.path.join(folder, filename) + " " + todownload)
993 | # import wget
994 | # wget.download(todownload, filepath)
995 | # checkwget = subprocess.getoutput(wgetpath)
996 | # if "is not recognized" in checkwget:
997 | if not os.path.exists(wgetpath):
998 | tempcondition = currentcondition
999 | currentcondition = "Installing wget Windows..."
1000 | print('[1;32mInstalling wget Windows...')
1001 | print('[0m')
1002 | wgetwinlink = "https://github.com/webfolderio/wget-windows/releases/download/v1.21.3.june.19.2022/wget-gnutls-x64.exe"
1003 | print(wgetwinlink)
1004 | runwithsubprocess("curl -Lo " + shlex.quote(wgetpath) + " " + wgetwinlink, batchlinksinstallpath)
1005 | print('[1;32mwget Windows installed!')
1006 | print('[0m')
1007 | currentcondition = tempcondition
1008 | runwithsubprocess(f"{shlex.quote(wgetpath)} -O {filepath_s} {todownload_s} --progress=bar:force", folder_win)
1009 | elif downloader=='curl':
1010 | runwithsubprocess(f"curl -Lo {filepath_s} {todownload_s}", folder_win)
1011 | elif downloader=='aria2':
1012 | # checkaria = subprocess.getoutput(ariapath)
1013 | # if "is not recognized" in checkaria or "cannot find the path" in checkaria:
1014 | if not os.path.exists(ariapath):
1015 | tempcondition = currentcondition
1016 | currentcondition = "Installing aria2 Windows..."
1017 | print('[1;32mInstalling aria2 Windows...')
1018 | print('[0m')
1019 | ariaziplink = "https://github.com/q3aql/aria2-static-builds/releases/download/v1.36.0/aria2-1.36.0-win-64bit-build2.7z"
1020 | print(ariaziplink)
1021 | runwithsubprocess("curl -Lo " + shlex.quote(ariazippath) + " " + ariaziplink, batchlinksinstallpath)
1022 | sevenzpath = install7zWin()
1023 | runwithsubprocess(f"{shlex.quote(sevenzpath)} x {shlex.quote(ariazippath)} -o{shlex.quote(batchlinksinstallpath)}", batchlinksinstallpath, False, '7z')
1024 | print()
1025 | print('[1;32maria2 Windows installed!')
1026 | print('[0m')
1027 | currentcondition = tempcondition
1028 | # ariamode = True
1029 | runwithsubprocess(f"{shlex.quote(ariapath)} --summary-interval=1 --console-log-level=error --check-certificate=false -c -x 16 -s 16 -k 1M {todownload_s} -d {folder_s} -o {ariafilename_s}", folder_win, False, 'aria2')
1030 | else: #non-windows
1031 | if downloader=='gdown':
1032 | printdebug(f"debug gdown {todownload_s} -O {filepath_s}")
1033 | runwithsubprocess(f"gdown {todownload_s} -O {filepath_s}", folder_s)
1034 | elif downloader=='wget':
1035 | runwithsubprocess(f"wget -O {filepath_s} {todownload_s} --progress=bar:force", folder_s)
1036 | elif downloader=='curl':
1037 | runwithsubprocess(f"curl -Lo {filepath_s} {todownload_s}", folder_s)
1038 | # curdir = os.getcwd()
1039 | # os.rename(os.path.join(curdir, filename), filepath)
1040 | elif downloader=='aria2':
1041 | ariachecker = "dpkg-query -W -f='${Status}' aria2"
1042 | checkaria = subprocess.getoutput(ariachecker)
1043 | if "no packages found matching aria2" in checkaria:
1044 | tempcondition = currentcondition
1045 | currentcondition = "Installing aria2..."
1046 | print('[1;32mInstalling aria2 ...')
1047 | print('[0m')
1048 | runwithsubprocess(f"apt -y update -qq")
1049 | runwithsubprocess(f"apt -y install -qq aria2")
1050 | print('[1;32maria2 installed!')
1051 | print('[0m')
1052 | currentcondition = tempcondition
1053 | # ariamode = True
1054 | runwithsubprocess(f"aria2c --summary-interval=1 --console-log-level=error -c -x 16 -s 16 -k 1M {todownload_s} -d {folder_s} -o {ariafilename_s}", folder_s, False, 'aria2')
1055 | printdebug("\nmode: " + mode)
1056 | if mode=='debugevery':
1057 | time.sleep(2)
1058 | try:
1059 | os.rename(filepath, os.path.join(folder, f"{os.path.splitext(filename)[0]}-{downloader}{os.path.splitext(filename)[1]}"))
1060 | printdebug("renamed to " + f"{os.path.splitext(filename)[0]}-{downloader}{os.path.splitext(filename)[1]}")
1061 | except FileNotFoundError or FileExistsError:
1062 | printdebug("rename failed somehow")
1063 | pass
1064 | elif mode=='civitdebugevery':
1065 | time.sleep(2)
1066 | printdebug("debug filename: " + str(filename))
1067 | printdebug("debug filename_s: " + str(filename_s))
1068 | printdebug("debug filepath: " + str(filepath_s))
1069 | printdebug("debug todownload_s: " + str(todownload_s))
1070 | printdebug("debug folder_s: " + str(folder_s))
1071 | try:
1072 | os.rename(folder, os.path.join(folder_s, f"{os.path.splitext(filename)[0]}-{downloader}{os.path.splitext(filename)[1]}"))
1073 | printdebug("renamed to " + f"{os.path.splitext(filename)[0]}-{downloader}{os.path.splitext(filename)[1]}")
1074 | except FileNotFoundError or FileExistsError:
1075 | printdebug("rename failed somehow")
1076 | pass
1077 | if torename and prockilled == False and filename != torename and downloader != 'aria2':
1078 | def saferename(oldpath, newpath):
1079 | try:
1080 | os.rename(oldpath, newpath)
1081 | except Exception as e:
1082 | print("Rename failed. Reason: " + str(e))
1083 | pass
1084 |
1085 | saferename(filepath, os.path.join(folder, shlex.quote(torename)))
1086 | if mode=='civit0' and prockilled == False:
1087 | # if torename:
1088 | # filepath = os.path.join(folder, torename)
1089 | try:
1090 | if os.path.exists(filepath):
1091 | if os.path.getsize(filepath) <= 5 * 1024:
1092 | try:
1093 | with open(filepath, "r", encoding="utf-8") as file:
1094 | if "We'll be right back | Civitai" in file.read():
1095 | prevciviterror = True
1096 | print('[1;31mCivitAI website is currently down ツ')
1097 | print('[0m')
1098 | except Exception as e:
1099 | print("File size checking failed. Reason: " + str(e))
1100 | pass
1101 | except Exception as e:
1102 | print("File size checking failed. Reason: " + str(e))
1103 | pass
1104 | if mode=='dropbox' and prockilled == False:
1105 | if os.path.exists(filepath):
1106 | if os.path.getsize(filepath) <= 1024:
1107 | print('[1;31mDropbox filesize below 1kb')
1108 | print("There's a chance that the file got too much traffic and dropbox blocked access to it")
1109 | print('[0m')
1110 | # if prockilled == True:
1111 | # #rewind_folder(folder_s)
1112 | # pass
1113 |
1114 | def install7zWin(): #@note install7z
1115 | #usage: sevenzpath = install7zWin()
1116 | global currentcondition
1117 | tempcondition = currentcondition
1118 | currentcondition = "Installing 7z..."
1119 | localappdata = os.environ['LOCALAPPDATA']
1120 | batchlinksinstallpath = os.path.join(localappdata, "batchlinks")
1121 | sevenzrpath = os.path.join(batchlinksinstallpath, "7zr.exe")
1122 | sevenzrlink = "https://www.7-zip.org/a/7zr.exe"
1123 | svnzpacklink = "https://7-zip.org/a/7z2201-x64.exe"
1124 | svnzpackpath = os.path.join(batchlinksinstallpath, "7z2201-x64.exe")
1125 | svnzexecpath = os.path.join(batchlinksinstallpath, "7z.exe")
1126 | if not os.path.exists(sevenzrpath):
1127 | print(sevenzrlink)
1128 | runwithsubprocess("curl -Lo " + shlex.quote(sevenzrpath) + " " + sevenzrlink, batchlinksinstallpath)
1129 | if not os.path.exists(svnzpackpath):
1130 | print(svnzpacklink)
1131 | runwithsubprocess("curl -Lo " + shlex.quote(svnzpackpath) + " " + svnzpacklink, batchlinksinstallpath)
1132 | if not os.path.exists(svnzexecpath):
1133 | runwithsubprocess(f"{shlex.quote(sevenzrpath)} x {shlex.quote(svnzpackpath)} -p- -o{shlex.quote(batchlinksinstallpath)} -y -sdel -bb0", batchlinksinstallpath, False, '7z')
1134 | currentcondition = tempcondition
1135 | return svnzexecpath
1136 |
1137 | def savestate_folder(folder):
1138 | global currentfoldertrack
1139 | currentfoldertrack = []
1140 | listfile = os.listdir(folder)
1141 | for file in listfile:
1142 | pathoffile = os.path.join(folder, file)
1143 | currentfoldertrack.append(pathoffile)
1144 |
1145 | def rewind_folder(folder):
1146 | listfilenew = os.listdir(folder)
1147 | newerfoldertrack = []
1148 | for file in listfilenew:
1149 | pathoffile = os.path.join(folder, file)
1150 | newerfoldertrack.append(pathoffile)
1151 | toremove = [x for x in newerfoldertrack if x not in currentfoldertrack]
1152 | printdebug("\ndebug toremove: " + str(toremove))
1153 | print()
1154 | for fileordir in toremove:
1155 | if os.path.exists(fileordir):
1156 | if os.path.isdir(fileordir):
1157 | shutil.rmtree(fileordir)
1158 | print("Removed incomplete download: " + fileordir)
1159 | else:
1160 | os.remove(fileordir)
1161 | print("Removed incomplete download: " + fileordir)
1162 |
1163 | def writeall(olddict, shellonly, custompaths=''):
1164 | newdict = trackall()
1165 | global finalwrite
1166 | finalwrite = []
1167 |
1168 | finalwrite.append("All done!")
1169 | if custompaths:
1170 | finalwrite.append("⬇️Custom path added:⬇️")
1171 | for hashtag, thepath in custompaths.items():
1172 | finalwrite.append(f"{hashtag} -> {thepath}")
1173 | finalwrite.append("Downloaded files: ")
1174 |
1175 | for oldtype, olddir in olddict.items():
1176 | for newtype, newdir in newdict.items():
1177 | if newtype == oldtype:
1178 | s = set(olddir)
1179 | trackcompare = [x for x in newdir if x not in s]
1180 | if len(trackcompare) > 0:
1181 | exec(f"finalwrite.append('⬇️' + {newtype}path + '⬇️')")
1182 | for item in trackcompare:
1183 | finalwrite.append(item)
1184 | if bool(remaininglinks):
1185 | finalwrite.append("(There are still some files that have not been downloaded. Click the 'Resume Download' button to load the links that haven't been downloaded.)")
1186 | printvardebug(finalwrite)
1187 |
1188 | if len(finalwrite) > 1 and "Downloaded files:" in finalwrite[1] and len(finalwrite) < 3:
1189 | finalwrite.append("(Not finding something here? Check the terminal/colab console)")
1190 |
1191 | finaloutput = list_to_text(finalwrite)
1192 | finalwrite = []
1193 | if shellonly:
1194 | return "Commands executed successfully."
1195 | else:
1196 | return finaloutput
1197 |
1198 | def writepart(box, path):
1199 | global finalwrite
1200 | if len(box) > 0:
1201 | finalwrite.append("⬇️" + path + "⬇️")
1202 | for item in box:
1203 | finalwrite.append(item)
1204 |
1205 | def trackall():
1206 | filesdict = dict()
1207 | for x in typemain:
1208 | if x == "altmodel" and altmodelpath == modelpath:
1209 | continue
1210 | exec(f"os.makedirs({x}path, exist_ok=True)")
1211 | exec(f"filesdict['{x}'] = os.listdir({x}path)")
1212 | return filesdict
1213 |
1214 | def currentfoldertohashtag(folder):
1215 | for x in typemain:
1216 | checkpath = str()
1217 | checkpath = eval(x+'path')
1218 | printdebug("checkpath: " + checkpath)
1219 | if str(folder).strip() == checkpath:
1220 | thehashtag = "#" + x
1221 | printdebug("thehashtag: " + thehashtag)
1222 | return thehashtag
1223 | return "#debug"
1224 |
1225 | def splitrename(linkcurrent):
1226 | renamecurrent = ''
1227 | if ">" in linkcurrent:
1228 | file_rename = linkcurrent.split(">")
1229 | file_rename = [file_rename.strip() for file_rename in file_rename]
1230 | linkcurrent = file_rename[0]
1231 | if file_rename[1]:
1232 | renamecurrent = file_rename[1]
1233 | return linkcurrent, renamecurrent
1234 |
1235 | def extractcurdir(currentdir): #@note extractcurdir
1236 | allfileshere = os.listdir(currentdir)
1237 | szfileall = []
1238 | if platform.system() == "Windows":
1239 | sevenzpath = install7zWin()
1240 | global currentcondition
1241 | extensiontoextract = [".zip", ".rar", ".7z", ".tar"]
1242 | for filehere in allfileshere:
1243 | if filehere.endswith(tuple(extensiontoextract)):
1244 | szpath = os.path.join(currentdir, filehere)
1245 | szfileall.append(szpath)
1246 | for szfile in szfileall:
1247 | if prockilled == False:
1248 | # currentcondition = "Extracting" + os.path.basename(szfile) + "..."
1249 | if platform.system() == "Windows":
1250 | runwithsubprocess(f"{shlex.quote(sevenzpath)} x {shlex.quote(szfile)} -p- -o{shlex.quote(currentdir)} -y -sdel -bb0", currentdir, False, '7z')
1251 | else:
1252 | runwithsubprocess(f"7z x {shlex.quote(szfile)} -p- -o{shlex.quote(currentdir)} -y -sdel -bb0", currentdir, False, '7z')
1253 |
1254 | #@stopwatch #the decorator mess with the progress bar #@note run
1255 | def run(command, choosedowner, civitdefault, civitpruned, civitvae, progress=gr.Progress()):
1256 | progress(0.01, desc='')
1257 | global prockilled
1258 | prockilled = False
1259 | global everyprocessid
1260 | everyprocessid = []
1261 | everymethod = False
1262 | global currentcondition
1263 | resumebuttonvisible = False
1264 | if command.strip().startswith('https://pastebin.com/') and command.strip().count('\n') == 0:
1265 | currentcondition = f'Done.'
1266 | if gradiostate == True:
1267 | return ["Use the 'Copy from Pastebin' button instead", gr.Dataframe.update(value=buildarrayofhashtags('bottom'))] #gr.Dataframe.update(value=buildarrayofhashtags('right')),
1268 | else:
1269 | return ["Use the 'Copy from Pastebin' button instead", gr.Dataframe.update(value=buildarrayofhashtags('bottom')), gr.Button.update(visible=resumebuttonvisible)] #gr.Dataframe.update(value=buildarrayofhashtags('right')),
1270 |
1271 | if command.strip() == '@debugresetdownloads' and snapshot != {} and globaldebug == True:
1272 | currentcondition = f'Removing downloaded files...'
1273 | removed_files = global_rewind()
1274 | texttowrite = ["⬇️Removed files⬇️"]
1275 | for item in removed_files:
1276 | texttowrite.append(item)
1277 | writefinal = list_to_text(texttowrite)
1278 | currentcondition = f'Removing done.'
1279 | if gradiostate == True:
1280 | return [writefinal, gr.Dataframe.update(value=buildarrayofhashtags('bottom'))] #gr.Dataframe.update(value=buildarrayofhashtags('right')),
1281 | else:
1282 | return [writefinal, gr.Dataframe.update(value=buildarrayofhashtags('bottom')), gr.Button.update(visible=resumebuttonvisible)] #gr.Dataframe.update(value=buildarrayofhashtags('right')),
1283 |
1284 | if not command.strip():
1285 | currentcondition = "Logging activated."
1286 | texttowrite = ["The link box is empty."]
1287 | writefinal = list_to_text(texttowrite)
1288 | if gradiostate == True:
1289 | return [writefinal, gr.Dataframe.update(value=buildarrayofhashtags('bottom'))] #gr.Dataframe.update(value=buildarrayofhashtags('right'))
1290 | else:
1291 | return [writefinal, gr.Dataframe.update(value=buildarrayofhashtags('bottom')), gr.Button.update(visible=resumebuttonvisible)] #gr.Dataframe.update(value=buildarrayofhashtags('right')),
1292 | oldfilesdict = trackall()
1293 | if cmd_opts.ckpt_dir:
1294 | altmodelpath = cmd_opts.ckpt_dir
1295 | currentfolder = altmodelpath
1296 | else:
1297 | altmodelpath = os.path.join(models_path, "Stable-diffusion")
1298 | currentfolder = modelpath
1299 | currenthashtag = '#model'
1300 | os.makedirs(currentfolder, exist_ok=True)
1301 | currentcondition = 'Extracting links...'
1302 | links = extract_links(command)
1303 | isshell = True
1304 | for listpart in links:
1305 | if not listpart.startswith('!'):
1306 | isshell = False
1307 | break
1308 | printdebug("links: " + str(links))
1309 | steps = float(0)
1310 | totalsteps = float(1)
1311 |
1312 | usemega = False
1313 | usegdrive = False
1314 | usemediafire = False
1315 | # global ariamode
1316 | global gdownupgraded
1317 | global mediafireinstalled
1318 | for item in links:
1319 | if not item.startswith('#'):
1320 | totalsteps += 1
1321 | if item.startswith('https://mega.nz'):
1322 | usemega = True
1323 | if item.startswith('https://drive.google.com'):
1324 | usegdrive = True
1325 | if item.startswith('https://www.mediafire.com/file'):
1326 | usemediafire = True
1327 |
1328 | #break
1329 | printdebug("totalsteps: " + str(totalsteps))
1330 | if usemega == True:
1331 | currentcondition = 'Installing Mega...'
1332 | progress(0.01, desc='Installing Mega...')
1333 | if platform.system() == "Windows":
1334 | installmegawin()
1335 | else:
1336 | installmega()
1337 | if usegdrive == True and gdownupgraded == False:
1338 | tempcondition = currentcondition
1339 | currentcondition = "Upgrading gdown..."
1340 | print('[1;32mUpgrading gdown ...')
1341 | print('[0m')
1342 | runwithsubprocess(f"pip3 install -q --upgrade --no-cache-dir gdown")
1343 | print('[1;32mgdown upgraded!')
1344 | print('[0m')
1345 | currentcondition = tempcondition
1346 | gdownupgraded = True
1347 | if usemediafire == True and mediafireinstalled == False:
1348 | tempcondition = currentcondition
1349 | currentcondition = "Installing mediafire-dl..."
1350 | print('[1;32mInstalling mediafire-dl...')
1351 | print('[0m')
1352 | runwithsubprocess(f"pip3 install git+https://github.com/Juvenal-Yescas/mediafire-dl")
1353 | print('[1;32mmediafire-dl installed!')
1354 | print('[0m')
1355 | currentcondition = tempcondition
1356 | mediafireinstalled = True
1357 |
1358 | print('[1;32mBatchLinks Downloads starting...')
1359 | print('[0m')
1360 | printdebug('prockilled: ' + str(prockilled))
1361 | global remaininglinks
1362 | batchtime = time.time()
1363 | addedcustompath = dict()
1364 | downmethod = ['gdown', 'wget', 'curl', 'aria2']
1365 | hfmethods = [
1366 | "https://raw.githubusercontent.com",
1367 | "https://huggingface.co",
1368 | "https://cdn.discordapp.com/attachments"
1369 | ]
1370 | global typemain
1371 | global typechecker
1372 | for listpart in links:
1373 | if prockilled == False:
1374 | currenttorename = ''
1375 | printdebug("steps: " + str(steps))
1376 | printdebug("total steps: " + str(totalsteps))
1377 | printdebug("percentage: " + str(round(steps/totalsteps, 1)))
1378 | printdebug("currenttypemain: " + str(typemain))
1379 | # ariamode = False
1380 | if gradiostate == False:
1381 | if time.time() - batchtime >= 70:
1382 | remaininglinks = links[links.index(listpart):]
1383 | printdebug("remaining links: " + str(remaininglinks))
1384 | if bool(remaininglinks):
1385 | printdebug("currentfolder: " + currentfolder)
1386 | tophashtag = currentfoldertohashtag(currentfolder)
1387 | printdebug("tophashtag: " + tophashtag)
1388 | remaininglinks.insert(0, tophashtag)
1389 | printdebug("remaining links new: " + str(remaininglinks))
1390 | print()
1391 | print('[1;33mRuntime was stopped to prevent hangs.')
1392 | print("[1;33mCheck the UI and press 'Resume Download' to load the remaining links")
1393 | print("[1;33mThen click 'Download All!' again")
1394 | print('[0m')
1395 | print("These are some links that haven't been downloaded yet.👇")
1396 | printremains = list_to_text(remaininglinks)
1397 | print(printremains)
1398 | resumebuttonvisible = True
1399 | cancelrun()
1400 | break
1401 |
1402 | if listpart.startswith("https://mega.nz"):
1403 | currentlink, currenttorename = splitrename(listpart)
1404 | print('\n')
1405 | print(currentlink)
1406 | currentcondition = f'Downloading {currentlink}...'
1407 | progress(round(steps/totalsteps, 3), desc='Downloading from ' + os.path.basename(currentlink).split('#')[0] + f' into {currenthashtag}...')
1408 | transfare(currentlink, currentfolder, currenttorename)
1409 | steps += 1
1410 |
1411 | elif listpart.startswith(tuple(hfmethods)):
1412 | # elif listpart.startswith("https://huggingface.co") or listpart.startswith("https://raw.githubusercontent.com") or listpart.startswith("https://cdn.discordapp.com/attachments"):
1413 | currentlink, currenttorename = splitrename(listpart)
1414 | print('\n')
1415 | print(currentlink)
1416 | currentcondition = f'Downloading {currentlink}...'
1417 | progress(round(steps/totalsteps, 3), desc='Downloading ' + os.path.basename(currentlink) + f' into {currenthashtag}...')
1418 | if everymethod == False:
1419 | hfdown(currentlink, currentfolder, choosedowner, 'default', currenttorename)
1420 | else:
1421 | for xmethod in downmethod:
1422 | if prockilled == False:
1423 | hfdown(currentlink, currentfolder, xmethod, 'debugevery')
1424 | steps += 1
1425 |
1426 | elif listpart.startswith("https://www.dropbox.com/s"): #@note dropbox
1427 | currentlink, currenttorename = splitrename(listpart)
1428 | if currentlink.endswith("?dl=0"):
1429 | currentlink = currentlink.split("?dl=")[0] + "?dl=1"
1430 | realname = currentlink.split("/")[-1].split("?dl=")[0]
1431 | if currenttorename == '':
1432 | currenttorename = realname
1433 | print('\n')
1434 | print(currentlink)
1435 | currentcondition = f'Downloading {currentlink}...'
1436 | progress(round(steps/totalsteps, 3), desc='Downloading ' + currenttorename + f' into {currenthashtag}...')
1437 | hfdown(currentlink, currentfolder, choosedowner, 'dropbox', currenttorename)
1438 | steps += 1
1439 |
1440 | elif listpart.startswith("https://drive.google.com"):
1441 | currentlink, currenttorename = splitrename(listpart)
1442 | print('\n')
1443 | print(currentlink)
1444 | currentcondition = f'Downloading {currentlink}...'
1445 | match = re.search(r'\?id=([^\&]+)\&export|/d/([^\s/]+)/', currentlink)
1446 | if match:
1447 | if match.group(1):
1448 | extracted_string = match.group(1)
1449 | else:
1450 | extracted_string = match.group(2)
1451 | progress(round(steps/totalsteps, 3), desc='Downloading from ' + extracted_string + f' into {currenthashtag}...')
1452 | mediadrivedown(currentlink, currentfolder, 'gdrive', currenttorename)
1453 | steps += 1
1454 |
1455 | elif listpart.startswith("https://pixeldrain.com"): #@note pixeldrain
1456 | currentlink, currenttorename = splitrename(listpart)
1457 | if not currentlink.startswith("https://pixeldrain.com/api/file/"):
1458 | fileid = currentlink.split("/")[-1]
1459 | currentlink = f"https://pixeldrain.com/api/file/{fileid}"
1460 | currentcondition = f'Retrieving Pixeldrain link...'
1461 | searcher = "findstr" if platform.system() == "Windows" else "grep"
1462 | try:
1463 | filename = subprocess.getoutput(f"curl -sI {currentlink} | {searcher} -i Content-Disposition").split('filename="', 1)[1].rsplit('"', 1)[0]
1464 | except IndexError:
1465 | print("Something wrong while retrieving the Pixeldrain link")
1466 | continue
1467 | if currenttorename == '':
1468 | currenttorename = filename
1469 | print('\n')
1470 | print(currentlink)
1471 | currentcondition = f'Downloading {currentlink}...'
1472 | progress(round(steps/totalsteps, 3), desc='Downloading ' + currenttorename + f' into {currenthashtag}...')
1473 | # if everymethod == False:
1474 | hfdown(currentlink, currentfolder, choosedowner, 'pixeldrain', currenttorename)
1475 | # else:
1476 | # for xmethod in downmethod:
1477 | # if prockilled == False:
1478 | # hfdown(currentlink, currentfolder, xmethod, 'debugevery')
1479 | steps += 1
1480 |
1481 | elif listpart.startswith("https://www.mediafire.com/file"): #@note mediafire
1482 | currentlink, currenttorename = splitrename(listpart)
1483 | print('\n')
1484 | print(currentlink)
1485 | currentcondition = f'Downloading {currentlink}...'
1486 | progress(round(steps/totalsteps, 3), desc='Downloading ' + currentlink.split("/")[-2] + f' into {currenthashtag}...')
1487 | # if everymethod == False:
1488 | mediadrivedown(currentlink, currentfolder, 'mediafire', currenttorename)
1489 | # else:
1490 | # for xmethod in downmethod:
1491 | # if prockilled == False:
1492 | # hfdown(currentlink, currentfolder, xmethod, 'debugevery')
1493 | steps += 1
1494 |
1495 | elif listpart.startswith("https://anonfiles.com"): #@note anonfiles
1496 | currentlink, currenttorename = splitrename(listpart)
1497 | print('\n')
1498 | print(currentlink)
1499 |
1500 | currentcondition = f'Retrieving anonfiles link...'
1501 | # print(filename)
1502 | # Send HTTP request to the website and read the response
1503 | response = urllib.request.urlopen(currentlink)
1504 | html_content = response.read().decode('utf-8')
1505 |
1506 | # Find all links that contain "anonfiles" in them using regular expressions
1507 | download_links = re.findall(r'href=["\'](https?:\/\/.*?anonfiles.*?)["\']', html_content)
1508 | currentlink = max(download_links, key=len)
1509 |
1510 | currentcondition = f'Downloading {currentlink}...'
1511 | progress(round(steps/totalsteps, 3), desc='Downloading ' + currentlink.split("/")[-1] + f' into {currenthashtag}...')
1512 | if everymethod == False:
1513 | hfdown(currentlink, currentfolder, choosedowner, 'default', currenttorename)
1514 | else:
1515 | for xmethod in downmethod:
1516 | if prockilled == False:
1517 | hfdown(currentlink, currentfolder, xmethod, 'debugevery')
1518 | steps += 1
1519 |
1520 | elif listpart.startswith("https://files.catbox.moe"):
1521 | currentlink, currenttorename = splitrename(listpart)
1522 | print('\n')
1523 | print(currentlink)
1524 | try:
1525 | urllib.request.urlopen(currentlink)
1526 | except http.client.RemoteDisconnected:
1527 | print('[1;31mConnection to ' + currentlink + ' failed.')
1528 | print("This colab session's server might doesn't have access to catbox")
1529 | print('[0m')
1530 | continue
1531 | except urllib.error.URLError as e:
1532 | print('[1;31mConnection to ' + currentlink + ' failed.')
1533 | print("This colab session's server might doesn't have access to catbox")
1534 | print('[0m')
1535 | continue
1536 | print(currentlink)
1537 | currentcondition = f'Downloading {currentlink}...'
1538 | progress(round(steps/totalsteps, 3), desc='Downloading ' + os.path.basename(currentlink) + f' into {currenthashtag}...')
1539 | if everymethod == False:
1540 | hfdown(currentlink, currentfolder, choosedowner, 'default', currenttorename)
1541 | else:
1542 | for xmethod in downmethod:
1543 | if prockilled == False:
1544 | hfdown(currentlink, currentfolder, xmethod, 'debugevery')
1545 | steps += 1
1546 |
1547 | elif listpart.startswith("https://civitai.com/api/download/models/"): #@note civit direct
1548 | usenewmethod = True
1549 | currentlink, currenttorename = splitrename(listpart)
1550 | if currenttorename == '':
1551 | currenttorename = getcivitname(listpart)
1552 | if currenttorename == 'batchlinksold':
1553 | usenewmethod = False
1554 | elif currenttorename == 'batchlinksskip':
1555 | continue
1556 | # print("That CivitAI link no longer exist, or the server is currently down.")
1557 | # continue
1558 |
1559 | print('\n')
1560 | print(currentlink)
1561 | currentcondition = f'Downloading {currentlink}...'
1562 | if everymethod == False:
1563 | if usenewmethod:
1564 | progress(round(steps/totalsteps, 3), desc='Downloading ' + currenttorename + f' into {currenthashtag}...')
1565 | hfdown(currentlink, currentfolder, choosedowner, 'civit0', currenttorename)
1566 | else:
1567 | progress(round(steps/totalsteps, 3), desc='Downloading model number ' + os.path.basename(currentlink) + '...')
1568 | civitdown(currentlink, currentfolder, currenttorename)
1569 | if not prevciviterror:
1570 | configlink = checkcivitconfig(currentlink)
1571 | if not configlink=='':
1572 | namefile= os.path.splitext(currenttorename.split('?')[0])[0]
1573 | currenttorename = namefile + '.yaml'
1574 | hfdown(configlink, currentfolder, choosedowner, 'civit0', currenttorename)
1575 | else:
1576 | for xmethod in downmethod:
1577 | if prockilled == False:
1578 | hfdown(currentlink, currentfolder, xmethod, 'debugevery')
1579 | # civitdown(currentlink, currentfolder, currenttorename)
1580 | steps += 1
1581 |
1582 | elif listpart.startswith("https://github.com"):
1583 | currentlink, currenttorename = splitrename(listpart)
1584 | print('\n')
1585 | print(currentlink)
1586 | if '/raw/' in listpart or '/releases/download/' in listpart:
1587 | currentcondition = f'Downloading {currentlink}...'
1588 | progress(round(steps/totalsteps, 3), desc='Downloading ' + os.path.basename(currentlink) + f' into {currenthashtag}...')
1589 | if everymethod == False:
1590 | hfdown(currentlink, currentfolder, choosedowner, 'default', currenttorename)
1591 | else:
1592 | for xmethod in downmethod:
1593 | if prockilled == False:
1594 | hfdown(currentlink, currentfolder, xmethod, 'debugevery')
1595 | else:
1596 | splits = listpart.split('#')[0].split("/")
1597 | currentlink = "/".join(splits[:5])
1598 | foldername = listpart.split('#')[0].rsplit('/', 1)[-1]
1599 | folderpath = os.path.join(extpath, foldername)
1600 | currentcondition = f'Cloning {currentlink}...'
1601 | progress(round(steps/totalsteps, 3), desc='Cloning from ' + currentlink.split('/', 3)[-1] + f' into #ext...')
1602 | if platform.system() == "Windows":
1603 | runwithsubprocess(f"git clone {currentlink} {shlex.quote(folderpath)}")
1604 | else:
1605 | runwithsubprocess(f"git clone {currentlink} {folderpath}")
1606 | steps += 1
1607 |
1608 | elif listpart.startswith("https://civitai.com/models/"):
1609 | currentlink, currenttorename = splitrename(listpart)
1610 | print('\n')
1611 | print(currentlink)
1612 | currentcondition = f'Downloading {currentlink}...'
1613 | #customtypemains = list
1614 | progress(round(steps/totalsteps, 3), desc='Downloading ' + os.path.basename(currentlink) + f'...')
1615 | for tag in tuple((typemain[countofdefaulthashtags:])):
1616 | if "#" + tag == currenthashtag and gradiostate:
1617 | progress(round(steps/totalsteps, 3), desc='Downloading ' + os.path.basename(currentlink) + f' into {currenthashtag}...')
1618 | pass
1619 | break
1620 | if everymethod == False:
1621 | civitdown2(currentlink, currentfolder, choosedowner, currenttorename, False, civitdefault, civitpruned, civitvae)
1622 | # else:
1623 | # for xmethod in downmethod:
1624 | # if prockilled == False:
1625 | # civitdown2(currentlink, currentfolder, xmethod, True)
1626 | steps += 1
1627 |
1628 | elif listpart.startswith("!"):
1629 | commandtorun = listpart[1:]
1630 | currentcondition = f'Running command: {commandtorun}'
1631 | progress(round(steps/totalsteps, 3), desc=currentcondition)
1632 | runwithsubprocess(commandtorun, None, True)
1633 | steps += 1
1634 |
1635 | elif listpart.startswith("@debugeverymethod") and globaldebug == True and gradiostate == True:
1636 | print('\n')
1637 | everymethod = True
1638 | print('[1;32mDebugEveryMethod activated!')
1639 | print('[1;32mOne link will be downloaded with every possible download method.')
1640 | print('[0m')
1641 |
1642 | elif listpart.startswith("@debugresetdownloads") and snapshot != {} and globaldebug == True:
1643 | print('\n')
1644 | currentcondition = f'Removing downloaded files...'
1645 | removed_files = global_rewind()
1646 | oldfilesdict = trackall()
1647 | texttowrite = ["⬇️Removed files⬇️"]
1648 | for item in removed_files:
1649 | texttowrite.append(item)
1650 | writefinal = list_to_text(texttowrite)
1651 | printdebug(str(writefinal))
1652 |
1653 | elif listpart.startswith("@extract"): #@note hashtagextract
1654 | print('\n')
1655 | currentcondition = 'Extracting every archive file in current directory...'
1656 | print('Extracting every archive file in current directory...\n')
1657 | extractcurdir(currentfolder)
1658 |
1659 | elif listpart.startswith("@new"): #@note hashtagcustom
1660 | newcommand, newhashtag, newpath = shlex.split(listpart)
1661 | if newcommand and newhashtag and newpath:
1662 | if newhashtag.startswith("#"):
1663 | printdebug("making custom hashtag")
1664 | newtype = newhashtag[1:]
1665 | #global typemain #moved to top
1666 | #global typechecker #moved to top
1667 | if not newtype[0].isdigit():
1668 | if not newtype in typemain and not newpath in typechecker:
1669 | try:
1670 | typemain.append(newtype)
1671 | printdebug('typemain: ' + str(typemain))
1672 | typechecker.append(newtype)
1673 | printdebug('typechecker: ' + str(typechecker))
1674 | newglobalpath = newtype + "path"
1675 | printdebug('newglobalpath: ' + newtype + "path")
1676 | newpath = os.path.abspath(os.path.normpath(newpath).rstrip(os.sep))
1677 | os.makedirs(newpath, exist_ok=True)
1678 | globals()[newglobalpath] = newpath
1679 | printdebug("modelpath = " + eval(newglobalpath))
1680 | oldfilesdict[newtype] = os.listdir(newpath)
1681 | # global addedcustompath
1682 | addedcustompath[newhashtag] = newpath
1683 | printdebug("addedcustompath: " + str(addedcustompath))
1684 | print(f"New custom path added!\n{newhashtag} means {newpath}")
1685 | except Exception as e:
1686 | print("Adding custom path failed! Reason: " + str(e))
1687 | else:
1688 | print("Adding custom path failed! Reason: Custom hashtag name cannot starts with a number")
1689 |
1690 | elif listpart.startswith("@aria") or listpart.startswith("@aria2"): #@note customaria
1691 |
1692 | # checkariacommand = ['##', '>']
1693 | printvardebug(listpart)
1694 | if "##" in listpart:
1695 | listpart = listpart.split('##')[0]
1696 | if ":\\" in listpart: #windows path
1697 | listpart = listpart.replace('\\', '\\\\').replace('"', '\\"')
1698 | ariacmd = shlex.split(listpart)
1699 | printvardebug(ariacmd)
1700 | arialink, ariarename, ariapath = '', '', ''
1701 | renamedalready = False
1702 | custompath = False
1703 | # startcomment = False
1704 | if (ariacmd[0] == '@aria2' or ariacmd[0] == '@aria') and len(ariacmd) > 1: #two items. @aria and link
1705 | if ariacmd[1].startswith('http'):
1706 | arialink = ariacmd[1]
1707 |
1708 | if len(ariacmd) > 2: #three items. @aria, link, and path
1709 | if ariacmd[2].startswith(">") and len(ariacmd) > 3: #four items. @aria, link, '>', and rename (no custom path)
1710 | ariarename = shlex.quote(ariacmd[3])
1711 | renamedalready = True
1712 | else:
1713 | if ariacmd[2].startswith('#'): #three items. @aria, link, hashtagpath
1714 | tobeariapath, _ = hashtagtopath(ariacmd[2])
1715 | if tobeariapath:
1716 | ariapath = tobeariapath
1717 | else: #three items. @aria, link, custompath
1718 | # ariapath = shlex.quote(ariacmd[2])
1719 | ariapath = ariacmd[2]
1720 | custompath = True #message for file downloaded to custom path will be added later
1721 | # if len(cmd) > 3:
1722 | # if cmd[3].startswith('>'):
1723 | # renaming = True
1724 | # else:
1725 | # renaming = False
1726 | if len(ariacmd) > 4 and ariacmd[3].startswith('>') and not renamedalready: #five items. @aria, link, path, '>', and rename
1727 | ariarename = shlex.quote(ariacmd[4])
1728 |
1729 | if not ariapath:
1730 | ariapath = currentfolder
1731 |
1732 | if not ariarename:
1733 | ariarename = arialink.rsplit('/', 1)[-1]
1734 |
1735 | #finalcommand = f"aria2c --summary-interval=1 --console-log-level=error -c -x 16 -s 16 -k 1M {arialink} -d {ariapath} -o {ariarename}"
1736 | printvardebug(arialink)
1737 | printvardebug(ariapath)
1738 | printvardebug(ariarename)
1739 | if arialink and not prockilled:
1740 | try:
1741 | currentcondition = f'Downloading from {arialink} into {ariapath}...'
1742 | hfdown(arialink, ariapath, 'aria2', 'default', ariarename)
1743 | except Exception as e:
1744 | print(f"Custom aria download failed. Reason: {str(e)}")
1745 |
1746 | elif listpart.startswith("#") and listpart.endswith(tuple(typemain)): #tuple(typemain[countofdefaulthashtags:])
1747 | try:
1748 | printdebug("one of typemain")
1749 | currenthashtag = listpart
1750 | currentfolder = eval(listpart[1:] + "path")
1751 | os.makedirs(currentfolder, exist_ok=True)
1752 | except Exception as e:
1753 | print(f"Cannot use hashtag: {str(e)}")
1754 |
1755 | else:
1756 | tobecurrentfolder, tobecurrenthashtag = hashtagtopath(listpart)
1757 | if tobecurrentfolder:
1758 | currentfolder = tobecurrentfolder
1759 | if tobecurrentfolder:
1760 | currenthashtag = tobecurrenthashtag
1761 |
1762 | else:
1763 | currentcondition = 'Operation cancelled'
1764 | return "Operation cancelled"
1765 |
1766 | currentcondition = 'Writing output...'
1767 | downloadedfiles = writeall(oldfilesdict, isshell, addedcustompath)
1768 | for tokill in everyprocessid:
1769 | try:
1770 | os.kill(tokill, signal.SIGTERM)
1771 | except ProcessLookupError:
1772 | pass
1773 | except PermissionError:
1774 | pass
1775 | except OSError:
1776 | pass
1777 | print()
1778 | print('[1;32mBatchLinks Downloads finished!')
1779 | print('[0m')
1780 | currentcondition = 'Done!'
1781 | printdebug(f"this should be the output:\n" + str(downloadedfiles))
1782 | progress(1.00, desc='')
1783 | if gradiostate == True:
1784 | return [downloadedfiles, gr.Dataframe.update(value=buildarrayofhashtags('bottom'))] #@note dataframe #gr.Dataframe.update(value=buildarrayofhashtags('right')), was here
1785 | else:
1786 | return [downloadedfiles, gr.Dataframe.update(value=buildarrayofhashtags('bottom')), gr.Button.update(visible=resumebuttonvisible)] #gr.Dataframe.update(value=buildarrayofhashtags('right')), was here
1787 |
1788 | def hashtagtopath(thehashtag):
1789 | hashtagcurrent, foldercurrent = '',''
1790 | notbreaking = True
1791 | typemainlocal = []
1792 | for x in typemain:
1793 | typemainlocal.append(x)
1794 | for y in typemainlocal:
1795 | if thehashtag[1:] == y:
1796 | printdebug("one of typemain, found on else")
1797 | hashtagcurrent = thehashtag
1798 | foldercurrent = eval(thehashtag[1:] + "path")
1799 | os.makedirs(foldercurrent, exist_ok=True)
1800 | notbreaking = False
1801 | for prefix in typechecker:
1802 | if thehashtag.startswith("#" + prefix) and notbreaking:
1803 | if prefix in ["embedding", "embeddings", "embed", "embeds","textualinversion", "ti"]:
1804 | hashtagcurrent = '#embed'
1805 | elif prefix in ["model", "models", "checkpoint", "checkpoints"]:
1806 | hashtagcurrent = '#model'
1807 | elif prefix in ["vae", "vaes"]:
1808 | hashtagcurrent = '#vae'
1809 | elif prefix in ["lora", "loras"]:
1810 | hashtagcurrent ='#lora'
1811 | elif prefix in ["hypernetwork", "hypernetworks", "hypernet", "hypernets", "hynet", "hynets",]:
1812 | hashtagcurrent = '#hynet'
1813 | elif prefix in ["addnetlora", "loraaddnet", "additionalnetworks", "addnet"]:
1814 | hashtagcurrent = '#addnetlora'
1815 | elif prefix in ["controlnet", "cnet"]:
1816 | hashtagcurrent = '#cnet'
1817 | elif prefix in ["extension", "extensions", "ext"]:
1818 | hashtagcurrent = '#ext'
1819 | elif prefix in ["aestheticembedding", "aestheticembed"]:
1820 | hashtagcurrent = '#aestheticembed'
1821 | elif prefix in ["upscaler", "upscale"]:
1822 | hashtagcurrent = '#upscaler'
1823 | elif prefix in ["altmodel", "altmodels"]:
1824 | hashtagcurrent = '#altmodel'
1825 | elif prefix in ["lycoris", "locon", "loha"]:
1826 | hashtagcurrent = '#lycoris'
1827 | try:
1828 | foldercurrent = eval(hashtagcurrent[1:] + 'path')
1829 | except Exception as e:
1830 | print(f"Cannot use hashtag: {e}")
1831 | continue
1832 |
1833 | os.makedirs(foldercurrent, exist_ok=True)
1834 |
1835 | return foldercurrent, hashtagcurrent
1836 |
1837 | wildcardcommand = [
1838 | "@debugeverymethod", "@debugresetdownloads",
1839 | "@extract", "@aria", "@aria2", "@new"
1840 | ]
1841 |
1842 | def extract_links(string):
1843 | links = []
1844 | lines = string.split('\n')
1845 | for line in lines:
1846 | line = line.split('##')[0].strip()
1847 | if line.startswith(tuple(supportedlinks)):
1848 | links.append(line)
1849 | elif line.startswith(tuple(wildcardcommand)):
1850 | links.append(line)
1851 | elif line.startswith("!"):
1852 | links.append(line.strip())
1853 | elif line.startswith("#"):
1854 | links.append(line.strip())
1855 | # else:
1856 | # for prefix in typechecker:
1857 | # if line.startswith("#" + prefix):
1858 | # links.append(line)
1859 |
1860 | #print(f"links: {links}")
1861 | return links
1862 |
1863 | def list_to_text(lst):
1864 | stripped_list = [item.strip(',').strip('\"') for item in lst]
1865 | return '\n'.join(stripped_list)
1866 |
1867 | def uploaded(textpath):
1868 | if not textpath is None:
1869 | print(textpath)
1870 | file_paths = textpath.name
1871 | print(file_paths)
1872 | links = []
1873 |
1874 | with open(file_paths, 'r') as file:
1875 | for line in file:
1876 | if line.startswith(tuple(supportedlinks)):
1877 | links.append(line.strip())
1878 | elif line.startswith("!"):
1879 | links.append(line.strip())
1880 | elif line.startswith(tuple(wildcardcommand)):
1881 | links.append(line.strip())
1882 | elif line.startswith("#"):
1883 | links.append(line.strip())
1884 | # else:
1885 | # for prefix in typechecker:
1886 | # if line.startswith("#" + prefix):
1887 | # links.append(line.strip())
1888 |
1889 | text = list_to_text(links)
1890 | return text
1891 |
1892 | count = 0
1893 | def keeplog():
1894 | global currentcondition
1895 | global currentsuboutput
1896 | global logging
1897 | if logging == False:
1898 | currentcondition = "Logging activated."
1899 | currentsuboutput = ''
1900 | logging = True
1901 | return [currentcondition, gr.Button.update(visible=True), gr.Button.update(visible=False)]
1902 | if currentsuboutput == '':
1903 | return [currentcondition, gr.Button.update(visible=True), gr.Button.update(visible=False)]
1904 | else:
1905 | return [f"{currentcondition}\n{currentsuboutput}", gr.Button.update(visible=True), gr.Button.update(visible=False)]
1906 |
1907 | def offlog():
1908 | global currentcondition
1909 | global currentsuboutput
1910 | global logging
1911 | if logging == True:
1912 | currentcondition = "Logging deactivated."
1913 | currentsuboutput = ''
1914 | logging = False
1915 | return [f"{currentcondition}", gr.Button.update(visible=False), gr.Button.update(visible=True)]
1916 |
1917 | def empty():
1918 | return ''
1919 |
1920 | def cancelrun():
1921 | global processid
1922 | global prockilled
1923 | printdebug("debug processid: " + str(processid))
1924 | if not processid == '':
1925 | if platform.system() == "Windows":
1926 | global process
1927 | process.terminate()
1928 | _ = subprocess.getoutput("taskkill /F /T /PID" + str(process.pid))
1929 | _ = subprocess.getoutput("taskkill /f /t /im MEGAcmdServer.exe")
1930 | else:
1931 | try:
1932 | os.kill(processid, signal.SIGTERM)
1933 | except ProcessLookupError:
1934 | pass
1935 | except PermissionError:
1936 | pass
1937 | except OSError:
1938 | pass
1939 | #os.killpg(os.getpgid(processid.pid), signal.SIGTERM)
1940 | prockilled = True
1941 | if prockilled == True and globaldebug == True:
1942 | print()
1943 | print("This should kill")
1944 | print()
1945 | return ["Operation Cancelled",gr.Dataframe.update(value=buildarrayofhashtags('bottom'))]
1946 |
1947 | def fillbox():
1948 | global remaininglinks
1949 | if bool(remaininglinks):
1950 | text = list_to_text(remaininglinks)
1951 | remaininglinks = []
1952 | return [text, 'Links updated!\nClick Download All! to download the rest of the links', gr.Button.update(visible=False)]
1953 | return ['', '', gr.Button.update(visible=False)]
1954 |
1955 | if gradiostate == True:
1956 | storedstatedownloader = "gdown"
1957 | else:
1958 | storedstatedownloader = "aria2"
1959 | storedstatemodeltype = "safetensors"
1960 | storedstateprecision = "fp16"
1961 | storedstatevae = True
1962 |
1963 | def grchangedown(downstate):
1964 | global storedstatedownloader
1965 | storedstatedownloader = downstate
1966 | printvardebug(storedstatedownloader)
1967 |
1968 | def grchangetype(typestate):
1969 | global storedstatemodeltype
1970 | storedstatemodeltype = typestate
1971 | printvardebug(storedstatemodeltype)
1972 |
1973 | def grchangefp(fpstate):
1974 | global storedstateprecision
1975 | storedstateprecision = fpstate
1976 | printvardebug(storedstateprecision)
1977 |
1978 | def grchangevae(vaestate):
1979 | global storedstatevae
1980 | storedstatevae = vaestate
1981 | printvardebug(storedstatevae)
1982 |
1983 | def stretchui(stretch): #outputs=[boxtohide, bottomlist] #outputs=[boxtohide, downbox, civitbox, choose_downloader, civit_default, civit_ispruned, civit_alsodownvae])
1984 | # if stretch:
1985 | # return [gr.Box.update(visible=False), gr.Accordion.update(visible=False), gr.Accordion.update(visible=True)]
1986 | # else:
1987 | # return [gr.Box.update(visible=True), gr.Accordion.update(visible=True), gr.Accordion.update(visible=False)]
1988 | global storedstatedownloader
1989 | global storedstatemodeltype
1990 | global storedstateprecision
1991 | global storedstatevae
1992 | if stretch:
1993 | return [gr.Box.update(visible=False), gr.Row.update(visible=True), gr.Row.update(visible=True), gr.Radio.update(value=storedstatedownloader), gr.Radio.update(value=storedstatemodeltype), gr.Radio.update(value=storedstateprecision), gr.Checkbox.update(value=storedstatevae)]
1994 | else:
1995 | return [gr.Box.update(visible=True), gr.Row.update(visible=False), gr.Row.update(visible=False), gr.Radio.update(value=storedstatedownloader), gr.Radio.update(value=storedstatemodeltype), gr.Radio.update(value=storedstateprecision), gr.Checkbox.update(value=storedstatevae)]
1996 |
1997 | def hidehelp(hide):
1998 | if hide:
1999 | return [gr.Markdown.update(value=titletext), gr.Markdown.update(visible=False)]
2000 | else:
2001 | return [gr.Markdown.update(value=introductiontext), gr.Markdown.update(visible=True)]
2002 |
2003 | # countofdefaulthashtags = len(typemain)
2004 | def buildarrayofhashtags(rightorbottom):
2005 | printdebug(f"buildarray {rightorbottom} initiated!")
2006 | # defaultpathtime = True
2007 | def writingpath(j, path):
2008 | if path == modelpath:
2009 | return path + " (default path)"
2010 | elif path == altmodelpath:
2011 | return path
2012 | else:
2013 | if rightorbottom == 'right' and j < countofdefaulthashtags:
2014 | return path.replace(script_path, "~")
2015 | else:
2016 | return path
2017 | hashtagandpath = []
2018 | for i, x in enumerate(typemain):
2019 | try:
2020 | xpath = eval(x+"path")
2021 | if cmd_opts.ckpt_dir:
2022 | hashtagandpath.append(["#"+x, writingpath(i, xpath)])
2023 | elif x != "altmodel":
2024 | hashtagandpath.append(["#"+x, writingpath(i, xpath)])
2025 | # defaultpathtime = False
2026 | except Exception as e:
2027 | print(str(e))
2028 | return hashtagandpath
2029 |
2030 | def copyfrompastebin(boxwithlink):
2031 | pastebinlink = boxwithlink.strip()
2032 | if pastebinlink.startswith('https://pastebin.com/'):
2033 | if pastebinlink.count('\n') == 0:
2034 | global currentcondition
2035 | currentcondition = f'Gathering links from Pastebin..'
2036 | if not '/raw/' in pastebinlink:
2037 | pastebinlink = pastebinlink.replace("pastebin.com/", "pastebin.com/raw/")
2038 |
2039 | pbinresponse = requests.get(pastebinlink)
2040 |
2041 | if pbinresponse.status_code == 200:
2042 | pbintextlist = pbinresponse.text.splitlines()
2043 | links = []
2044 | for line in pbintextlist:
2045 | if line.startswith(tuple(supportedlinks)):
2046 | links.append(line.strip())
2047 | elif line.startswith("!"):
2048 | links.append(line.strip())
2049 | elif line.startswith("@new"):
2050 | links.append(line.strip())
2051 | elif line.startswith(tuple(wildcardcommand)):
2052 | links.append(line.strip())
2053 | elif line.startswith("#"):
2054 | links.append(line.strip())
2055 | currentcondition = f'Done.'
2056 | finallinks = list_to_text(links)
2057 | return [finallinks, "Pastebin links retrieved successfully."]
2058 | else:
2059 | currentcondition = f'Done.'
2060 | print("Error retrieving data from pastebin")
2061 | return [boxwithlink, "Error retrieving data from pastebin"]
2062 | else:
2063 | currentcondition = f'Done.'
2064 | return [boxwithlink, "Pastebin link must be the only one link on the textbox! (And only one pastebin link supported)"]
2065 | else:
2066 | currentcondition = f'Done.'
2067 | return [boxwithlink, "Pastebin link must be the first (and the only) link on the textbox!"]
2068 |
2069 | titletext = f"""⬇️ Batchlinks Downloader ({currentversion}) {latestversiontext}
"""
2070 | introductiontext = f"""
2071 | {titletext}
2072 |
2073 | This tool will read the textbox and download every links from top to bottom one by one
2074 | Put your links down below. Supported link: Huggingface, CivitAI, MEGA, Discord, Github, Catbox, Google Drive, Pixeldrain, Mediafire, Anonfiles, Dropbox
2075 | Use hashtag to separate downloaded items based on their download location
2076 | Valid hashtags: #embed
, #model
, #hypernet
, #lora
, #vae
, #addnetlora
, etc.
2077 | (For colab that uses sd-webui-additional-networks extension to load LoRA, use #addnetlora
instead)
2078 | Use double hashtag (##) after links for comment. Useful to mark which links downloads what.
2079 | Remember to always press the 🔄️ refresh button on the UI after downloading models etc. in order for them to show up on the list.
2080 | """
2081 | knowmoretext = f"""
2082 | Click these links for more:
2083 | Readme Page
2084 | Example
2085 | Syntax
2086 | Valid Hashtags
2087 | Here's how you can get the direct links
2088 | Report Bug
2089 | """
2090 | testboxplaceholder = f"""#model
2091 |
2092 | #vae
2093 |
2094 | #lora
2095 |
2096 | ##this is a comment, and these text is just an example
2097 | """
2098 |
2099 | def on_ui_tabs():
2100 | with gr.Blocks() as batchlinks:
2101 | with gr.Row():
2102 | with gr.Column(scale=2):
2103 | introduction = gr.Markdown(introductiontext)
2104 | with gr.Column(scale=1):
2105 | with gr.Row():
2106 | uistretcher = gr.Checkbox(value=False, label="Stretch UI", interactive=True)
2107 | helphider = gr.Checkbox(value=False, label="Hide Help", interactive=True)
2108 | knowmore = gr.Markdown(knowmoretext)
2109 | with gr.Group():
2110 | command = gr.Textbox(label="Links", placeholder=testboxplaceholder, lines=5)
2111 | if gradiostate == True:
2112 | logbox = gr.Textbox(label="Log", interactive=False)
2113 | else:
2114 | if vladmandic:
2115 | logbox = gr.Textbox("(remove the --disable-queue args on launch.py to enable optional logging)", label="Log", interactive=False)
2116 | else:
2117 | logbox = gr.Textbox("(use --gradio-queue args on launch.py to enable optional logging)", label="Log", interactive=False)
2118 |
2119 | with gr.Row():
2120 | with gr.Box():
2121 | if gradiostate == True:
2122 | with gr.Column():
2123 | # with gr.Row():
2124 | # gr.Textbox(value=None, interactive=False, show_label=False)
2125 | btn_onlog = gr.Button("Turn On Logging", variant="primary", visible=True)
2126 | # with gr.Row():
2127 | btn_offlog = gr.Button("Turn Off Logging", visible=False)
2128 | loggingon = btn_onlog.click(keeplog, outputs=[logbox, btn_offlog, btn_onlog], every=1)
2129 | btn_offlog.click(offlog, outputs=[logbox, btn_offlog, btn_onlog], cancels=[loggingon])
2130 | # gr.Textbox(value=None, interactive=False, show_label=False)
2131 | # logging = gr.Radio(["Turn On Logging"], show_label=False)
2132 | # logging.change(keeplog, outputs=logbox, every=1)
2133 | out_text = gr.Textbox(label="Output")
2134 | else:
2135 | if vladmandic:
2136 | print("Batchlinks webui extension: (Optional) Remove the --disable-queue args to enable logging & cancel button on this extension")
2137 | else:
2138 | print("Batchlinks webui extension: (Optional) Use --gradio-queue args to enable logging & cancel button on this extension")
2139 | out_text = gr.Textbox("(If this text disappear, that means a download session is in progress.)", label="Output")
2140 |
2141 | # if platform.system() == "Windows":
2142 | # choose_downloader = gr.Radio(["gdown", "wget", "curl"], value="gdown", label="Download method")
2143 | # else:
2144 |
2145 |
2146 | with gr.Row(variant='panel', visible=False) as downbox:
2147 |
2148 | if gradiostate == True:
2149 | with gr.Column(scale=1):
2150 | choose_downloader2 = gr.Radio(["gdown", "wget", "curl", "aria2"], value="gdown", label="Download method")
2151 | else:
2152 | with gr.Column(scale=1):
2153 | choose_downloader2 = gr.Radio(["aria2"], value="aria2", label="Downloader")
2154 |
2155 |
2156 | with gr.Row(variant='panel', visible=False) as civitbox:
2157 | # with gr.Box():
2158 | # with gr.Column(scale=1):
2159 | # with gr.Row():
2160 | civit_default2 = gr.Radio(["ckpt", "safetensors"], value="safetensors", label="CivitAI Preferred Model Type", interactive=True)
2161 | civit_ispruned2 = gr.Radio(["fp16", "fp32"], value="fp16", label="Model Precision", interactive=True)
2162 | # with gr.Row():
2163 | # civit_ispruned = gr.Checkbox(True, label="Pruned", interactive=True)
2164 | civit_alsodownvae2 = gr.Checkbox(True, label="Also Download VAE", interactive=True)
2165 |
2166 | # def passvaluebetweenradio(newval):
2167 | # return gr.Radio.update(value=newval)
2168 |
2169 | # def passvaluebetweencheckbox(newval):
2170 | # return gr.Checkbox.update(value=newval)
2171 |
2172 | # choose_downloader2.change(passvaluebetweenradio, inputs=choose_downloader2, outputs=choose_downloader)
2173 | # civit_default2.change(passvaluebetweenradio, inputs=civit_default2, outputs=civit_default)
2174 | # civit_ispruned2.change(passvaluebetweenradio, inputs=civit_ispruned2, outputs=civit_ispruned)
2175 | # civit_alsodownvae2.change(passvaluebetweencheckbox, inputs=civit_alsodownvae2, outputs=civit_alsodownvae)
2176 |
2177 | with gr.Row():
2178 | if gradiostate == True:
2179 | with gr.Column(scale=1, min_width=100):
2180 | btn_run = gr.Button("Download All!", variant="primary")
2181 | # btn_upload = gr.UploadButton("Upload .txt", file_types="text")
2182 | # btn_upload.upload(uploaded, btn_upload, file_output)
2183 | with gr.Column(scale=1, min_width=100):
2184 | btn_cancel = gr.Button("Cancel")
2185 | with gr.Row():
2186 | file_output = gr.UploadButton(file_types=['.txt'], label='Upload txt')
2187 | btn_pastebin = gr.Button("Copy from Pastebin")
2188 |
2189 | else:
2190 | btn_run = gr.Button("Download All!", variant="primary")
2191 | btn_resume = gr.Button("Resume Download", visible=False)
2192 | with gr.Column(scale=1, min_width=100):
2193 | with gr.Row():
2194 | file_output = gr.UploadButton(file_types=['.txt'], label='Upload txt')
2195 | btn_pastebin = gr.Button("Copy from Pastebin")
2196 | # with gr.Column(scale=1, min_width=100):
2197 | # file_output = gr.UploadButton(file_types=['.txt'], label='Upload txt')
2198 | # copy_pastebin = gr.Button("Copy from Pastebin")
2199 | if gradiostate == False:
2200 | with gr.Row():
2201 | gr.Markdown(
2202 | f"""
2203 |
After clicking the Download All button, it's recommended to inspect the
2204 | colab console, as every information about the download progress is there.
2205 | """)
2206 |
2207 | if gradiostate == False:
2208 | btn_resume.click(fillbox, None, outputs=[command, out_text, btn_resume])
2209 |
2210 | # file_output = gr.File(file_types=['.txt'], label="you can upload a .txt file containing links here")
2211 | # file_output.change(uploaded, file_output, command)
2212 |
2213 | file_output.upload(uploaded, file_output, command)
2214 | with gr.Box(visible=True) as boxtohide:
2215 | with gr.Row(variant='panel'):
2216 | if gradiostate == True:
2217 | with gr.Column(scale=1):
2218 | choose_downloader = gr.Radio(["gdown", "wget", "curl", "aria2"], value=storedstatedownloader, label="Download method")
2219 | else:
2220 | with gr.Column(scale=1):
2221 | choose_downloader = gr.Radio(["aria2"], value=storedstatedownloader, label="Downloader")
2222 | with gr.Row(variant='panel'):
2223 | civit_default = gr.Radio(["ckpt", "safetensors"], value=storedstatemodeltype, label="CivitAI Preferred Model Type", interactive=True)
2224 | civit_ispruned = gr.Radio(["fp16", "fp32"], value=storedstateprecision, label="Model Precision", interactive=True)
2225 | # with gr.Row():
2226 | # civit_ispruned = gr.Checkbox(True, label="Pruned", interactive=True)
2227 | civit_alsodownvae = gr.Checkbox(storedstatevae, label="Also Download VAE", interactive=True)
2228 | choose_downloader.change(grchangedown, inputs=choose_downloader)
2229 | civit_default.change(grchangetype, inputs=civit_default)
2230 | civit_ispruned.change(grchangefp, inputs=civit_ispruned)
2231 | civit_alsodownvae.change(grchangevae, inputs=civit_alsodownvae)
2232 | # with gr.Box(visible=True) as boxtohide:
2233 | # gr.Markdown("""
2234 | # If you feel the UI is too cramped, click the Stretch UI button above.
2235 | # """)
2236 | # with gr.Accordion("List of Every Hashtags and its Path", open=False, visible=True) as rightlist:
2237 | # righttable = gr.DataFrame(
2238 | # buildarrayofhashtags('right'),
2239 | # headers=["hashtag", "path"],
2240 | # datatype=["str", "str"],
2241 | # interactive=False
2242 | # )
2243 | # sidetext = gr.Markdown(knowmoretext, visible=True)
2244 | def passvaluebetweenradio(newval):
2245 | return gr.Radio.update(value=newval)
2246 |
2247 | def passvaluebetweencheckbox(newval):
2248 | return gr.Checkbox.update(value=newval)
2249 |
2250 | choose_downloader2.change(passvaluebetweenradio, inputs=choose_downloader2, outputs=choose_downloader)
2251 | civit_default2.change(passvaluebetweenradio, inputs=civit_default2, outputs=civit_default)
2252 | civit_ispruned2.change(passvaluebetweenradio, inputs=civit_ispruned2, outputs=civit_ispruned)
2253 | civit_alsodownvae2.change(passvaluebetweencheckbox, inputs=civit_alsodownvae2, outputs=civit_alsodownvae)
2254 | finish_audio = gr.Audio(interactive=False, value=os.path.join(extension_dir, "notification.mp3"), elem_id="finish_audio", visible=False)
2255 | with gr.Accordion("List of Every Hashtags and its Path", open=False, visible=True) as bottomlist:
2256 | bottomtable = gr.DataFrame(
2257 | buildarrayofhashtags('bottom'),
2258 | headers=["hashtag", "path"],
2259 | datatype=["str", "str"],
2260 | interactive=False,
2261 | )
2262 | gr.Markdown(
2263 | f"""
2264 | Made with ❤️ by etherealxx
2265 | """)
2266 | helphider.change(hidehelp, helphider, outputs=[introduction, knowmore])
2267 | # uistretcher.change(stretchui, uistretcher, outputs=[boxtohide, rightlist, bottomlist])
2268 | # uistretcher.change(stretchui, uistretcher, outputs=boxtohide)
2269 | uistretcher.change(stretchui, uistretcher, outputs=[boxtohide, downbox, civitbox, choose_downloader2, civit_default2, civit_ispruned2, civit_alsodownvae2])
2270 | #batchlinks.load(debug, output=debug_txt, every=1)
2271 | if gradiostate == True:
2272 | run_event = btn_run.click(run, inputs=[command, choose_downloader, civit_default, civit_ispruned, civit_alsodownvae], outputs=[out_text, bottomtable]) #righttable was on output
2273 | btn_cancel.click(cancelrun, None, outputs=[out_text, bottomtable], cancels=[run_event])
2274 | else:
2275 | btn_run.click(run, inputs=[command, choose_downloader, civit_default, civit_ispruned, civit_alsodownvae], outputs=[out_text, bottomtable, btn_resume]) #righttable was on output
2276 |
2277 | btn_pastebin.click(copyfrompastebin, inputs=[command], outputs=[command, out_text])
2278 |
2279 | if sdless:
2280 | if platform.system() == "Windows":
2281 | batchlinks.queue(64).launch(inbrowser=True)
2282 | else:
2283 | batchlinks.queue(64).launch(share=True)
2284 | else:
2285 | return (batchlinks, "Batchlinks Downloader", "batchlinks"),
2286 | if not sdless:
2287 | script_callbacks.on_ui_tabs(on_ui_tabs)
2288 | else:
2289 | on_ui_tabs()
2290 |
--------------------------------------------------------------------------------
/sdless-macos.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | pip3 show virtualenv >/dev/null || pip3 install -q virtualenv
3 | [ -d gradiovenv ] || virtualenv gradiovenv
4 | source gradiovenv/bin/activate; \
5 | pip3 show gradio >/dev/null || pip3 install -q gradio==3.16.2; \
6 | pip3 show tqdm >/dev/null || pip3 install -q tqdm
7 | source gradiovenv/bin/activate; \
8 | python3 scripts/batchlinks-downloader.py
--------------------------------------------------------------------------------
/sdless-windows-debug.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if exist gradioinstalled.bak goto beginning
3 | echo Checking gradio version...
4 | echo.
5 | python -c "import gradio" 2>nul && (
6 | python -c "import gradio; assert gradio.__version__ == '3.16.2'" 2>nul || (
7 | pip install gradio==3.16.2
8 | )
9 | ) || (
10 | pip install gradio==3.16.2
11 | )
12 | pip show tqdm >nul || pip install tqdm
13 | pip freeze | findstr /i "\" >nul && pip freeze | findstr /i "\" >nul && (echo. > gradioinstalled.bak)
14 | echo.
15 |
16 | :beginning
17 | cd scripts
18 | echo ^> python -m batchlinks-downloader.py --debug
19 | echo.
20 | python -m batchlinks-downloader.py --debug
21 | echo.
22 | echo Close the terminal to stop the looping launch
23 | echo.
24 | pause
25 | echo.
26 | goto beginning
--------------------------------------------------------------------------------
/sdless-windows.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if exist gradioinstalled.bak goto beginning
3 | echo Checking gradio version...
4 | echo.
5 | python -c "import gradio" 2>nul && (
6 | python -c "import gradio; assert gradio.__version__ == '3.16.2'" 2>nul || (
7 | pip install gradio==3.16.2
8 | )
9 | ) || (
10 | pip install gradio==3.16.2
11 | )
12 | pip show tqdm >nul || pip install tqdm
13 | pip freeze | findstr /i "\" >nul && pip freeze | findstr /i "\" >nul && (echo. > gradioinstalled.bak)
14 | echo.
15 |
16 | :beginning
17 | cd scripts
18 | echo ^> python -m batchlinks-downloader.py
19 | echo.
20 | python -m batchlinks-downloader.py
21 | echo.
22 | echo Close the terminal to stop the looping launch
23 | echo.
24 | pause
25 | echo.
26 | goto beginning
--------------------------------------------------------------------------------
/version.txt:
--------------------------------------------------------------------------------
1 | v3.2.1
2 | (^current version, hopefully not the last^)
3 |
4 | Release notes:
5 |
6 | ## v3.2.1
7 | - Changed "pruned" option to "fp precision" due to CivitAI API changes
8 | - Fixed bug: No matter when the download process is cancelled or not, CivitAI download will give print out notification that the download process is successful
9 | - Fixed bug: Preventing the UI errored out when storage is full and trying to convert jpg image while downloading civitai stuff (PIL.UnidentifiedImageError)
10 | - UI overhaul
11 |
12 | ## v3.2.0a
13 | - Added support for vladmandic's fork of automatic1111's webui
14 |
15 | ## v3.2.0
16 | - Bug fixed when downloading with aria2 on Windows
17 | - Bug fixed on gradio update checker on sdless batch file for windows
18 | - CivitAi error prevention (when the website is down)
19 | - Cleaned the code of the hashtag system
20 | - `Copy from Pastebin` feature
21 | - Custom aria command with `@aria`
22 | - Making sure custom hashtag doesn't start with number
23 | - New calmer notification sound
24 | - Readme page cleanup
25 | - Some help link changed to link into wiki instead
26 | - UI fix to adapt with latest gradio (v3.23.0)
27 | - Various bug fixes related to CivitAI download
28 |
29 | ## v3.1.1b
30 | - Hotfix: Fixed CivitAI link refuses to be downloaded properly. Seems like CivitAI changes its API.
31 | - Fixed several bugs on Windows, especially SDless
32 |
33 | ## v3.1.1a
34 | - Hotfix: Fixed CivitAI 'model type chooser' bug and fixed `@extract` bug on colab
35 |
36 | ## v3.1.1
37 | - Added some fix and messages in case CivitAI website is down
38 | - Added message when user pressed "Download All!" but the textbox is empty
39 | - Fixed bug where Lycoris folder always shows on downloaded files the first time user download something
40 |
41 | ## v3.1.0a
42 | - Hotfix: Indented block on line 1497 fix
43 |
44 | ## v3.1.0
45 | - New hashtag: `#altmodel`, when you use `--ckptdir` argument on `launch.py` line, this hashtag will points to that directory. Otherwise, it'll point to the same directory as `#model`
46 | - New hashtag: `#lycoris`, change current save directory to _/content/stable-diffusion-webui/extensions/sd-webui-additional-networks/models/lora/lycoris_
(Side note: _Lycoris/Locon/Loha_ will works just fine if you use `#addnetlora` instead, as long as you have both [addnet extension](https://github.com/kohya-ss/sd-webui-additional-networks) and [locon extension](https://github.com/KohakuBlueleaf/a1111-sd-webui-locon) installed)
47 | - New hashtag: `#upscaler`, change current save directory to _/content/stable-diffusion-webui/models/ESRGAN_ (This was added few commits ago, but i forgot to write it on the release notes)
48 |
49 | Fixes:
50 | - Fixed bug when installing wget/aria2 on Windows
51 | - Fixed bug where custom paths doesn't put CivitAi download (Model Path method) when custom hashtag is used
52 | - Ongoing downloads will correctly stops when Cancel button is pressed on Windows
53 | - CivitAi (Model Path method) now will not download training dataset by accident
54 |
55 | ## v3.0.2
56 | - Hotfix: removed a comment that messed up the non queue mode. Now the extension works again without `--gradio-queue`.
57 |
58 | ## v3.0.1
59 | - Hotfix: using `urllib.request` instead of `curl -sI` to get the model name on CivitAI direct link method, since it's more reliable (and the curl method always fails somehow). The `requests` method is returned as a fallback.
60 |
61 | ## v3.0.0
62 | - Added `@extract` syntax
63 | - Supports for custom hashtags with `@new` syntax
64 | - (Almost) Full Windows support
65 | - Auto-download config file if available when downloading from CivitAI (SD 2.0+)
66 | - Auto-renaming for downloading ckpt/safetensors and pruned model from CivitAI using direct link method
67 | - CivitAI direct link now use `curl` to get the filename, and use the chosen download method (from the four) to download. Huge download speed boost. `requests` is no longer needed.
68 | - Supports download from Anonfiles, Dropbox, Google Drive, Mediafire, Pixeldrain
69 | - Supports download from Github (raw and release files)
70 | - Supports for SDless mode (read more [here](https://github.com/etherealxx/batchlinks-webui#sdless-mode))
71 | - UI overhaul:
72 | - Now there's a table that shows where does the hashtags points into
73 | - Option to stretch the UI, if your monitor is small, or using colab on mobile
74 | - Option to hide help text
75 | - Option to choose preferred CivitAI models. This will works if you download the model via model page link (https://civitai.com/models/)
76 | - Upload txt now use a little button instead of covering half of the screen
77 |
78 | Fixes:
79 | - CivitAI `model page link` no longer randomly download the first model on the json list.
80 | - Most of Windows bugs
81 | - Renaming problem when using CivitAI model page link method
82 | - Warning message when CivitAI download isn't possible (server down)
83 |
84 | ## v2.1.2
85 | - CivitAI direct link now use curl to get the filename, and use the chosen download method to download. Requests is no longer needed.
86 |
87 | ## v2.1.1
88 | - Partial Windows support is back
89 |
90 | Changes:
91 | - wget disabled on windows currently, until it fixed
92 |
93 | Fixes:
94 | - gdown & curl bug fixed
95 | - utf-8 as default encoding for queue checker (fix bug in Windows)
96 |
97 | ## v2.1.0
98 | Features:
99 | - Supports renaming downloaded file with `>` (for example: `https://files.catbox.moe/uarze8.safetensors > neurosama.safetensors`)
100 | - Supports extension usage without `--gradio-queue` (ported from [onedotsix](https://github.com/etherealxx/batchlinks-webui/tree/onedotsix))
101 | - Supports running shell command from the UI with `!` (for example: type `!pip freeze`, then hit the `Download all!` button and see the colab console)
102 | - Progress bar for `--gradio-queue`
103 |
104 | Changes:
105 | - `aria2` as *the only* download method when using without `--gradio-queue`
106 | - Download session will be cut every 80 seconds on when using without `--gradio-queue` (just like [onedotsix](https://github.com/etherealxx/batchlinks-webui/tree/onedotsix))
107 | - _Debug stopwatch (decorator)_ won't run automatically when `globaldebug = True`, must be uncommented manually (it disrupt the progress bar)
108 | - Dropped support for webui based on Gradio 3.9 (update your installation, or use [onedotsix](https://github.com/etherealxx/batchlinks-webui/tree/onedotsix) instead)
109 | - UI tweak (Smaller font size)
110 |
111 | ## v2.0.0
112 | Features:
113 | - `aria2` as download method.
114 | - Cancel button for cancelling download process (`--gradio-queue` required)
115 | - Debug snapshot.
116 | When `global_debug = True`, the moment this extension launch, it saves the current state of the webui on various location (into `snapshot.txt`), and when you type `#debugresetdownloads` on the textbox, it will compare the current state and the last saved state, and removes every new file/folder. This will be useful for debugging and testing.
117 | - Debug every download method.
118 | When `global_debug = True` and you type `#debugevery method` on the textbox, every link that has 4 different method of download (Huggingface etc.) will be downloaded with every method, regardless of the radio button choice. The result is 4 file being downloaded.
119 | - Detection if a CivitAI links no longer exist
120 | - New hashtags: `#textualinversion`, `#ti`, `#aestheticembedding`, `#aestheticembed`, `#controlnet`, and `#cnet`
121 | - Toggle logging on/off
122 | - `shlex.quote` to properly quote links (Thanks **[@rti7743](https://github.com/rti7743)**!)
123 | - Supports cloning webui extensions
124 | - Supports download from CivitAI model links (Thanks **[@rti7743](https://github.com/rti7743)**!)
125 | - Supports download from Github (repository and raw files)
126 | - Supports for aesthetic gradients, controlnet model, and extensions path.
127 | - UI font scaled down
128 | - Uses `subprocess.Popen` instead of `os.system`
129 |
130 |
131 | Older releases:
132 |
133 | - commit "alpha" is v0.1.0 (09b1d81806e5bfc4d1783ccf644359fabc372fb7)
134 | - commit "Civit support, i hope it works." is v0.2.0 (2420ea4dd670ec71481a2ed43ff22c7c1a959543)
135 | - commit "🪲Bug fix and 🖼️stuff" is v0.2.1 (c7a91b6e14ca6f35438e29ab7ad3d2e00d62c99b)
136 | - commit "Merge pull request #1 from etherealxx/gradioevery" is v1.0.0 (a89e50fdfb5008f703750cf0e7878fc6c25f9219)
137 |
--------------------------------------------------------------------------------