2ch commited on
Commit
b1c669d
·
verified ·
1 Parent(s): 9c99b1e

Upload 3 files

Browse files
Files changed (3) hide show
  1. javascript/colab_sd_models.js +244 -0
  2. scripts/colab_sd_models.py +242 -0
  3. style.css +302 -0
javascript/colab_sd_models.js ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const observer = new MutationObserver((mutations) => {
3
+ mutations.forEach((mutation) => {
4
+ if (mutation.addedNodes) {
5
+ mutation.addedNodes.forEach((node) => {
6
+ if (node.id === 'tab_models_list') {
7
+ // запуск кода после загрузки элементов
8
+ Array.from(document.querySelectorAll('#tabs > div.tab-nav > button')).find(button => button.textContent.includes('модели')).click();
9
+ // переопределение параметров колонок на основе числа моделей в категории
10
+ const models_checkbox_grids = document.querySelectorAll("#tab_models_list fieldset > div[data-testid='checkbox-group']");
11
+ models_checkbox_grids.forEach(models_checkbox_grid => {
12
+ const model_label = models_checkbox_grid.querySelectorAll('label');
13
+ const numColumns = model_label.length > 9 ? 'repeat(auto-fit, minmax(250px, 1fr))' : '1fr';
14
+ models_checkbox_grid.style.gridTemplateColumns = numColumns;
15
+ });
16
+ // подвсетка категории мужицких моделей (костаыль, потому что градио своим скриптом долбит каждые Nms переопределение классов)
17
+ const intervalId = setInterval(() => {
18
+ const modelsNavButtons = document.querySelectorAll("div#tab_models_list > div.gap > div.tabs > div.tab-nav > button");
19
+ const MaleCat = Array.from(modelsNavButtons).find(button => button.textContent.includes('мужские'));
20
+ if (MaleCat) {
21
+ MaleCat.setAttribute("id", "male_only");
22
+ }
23
+ }, 100);
24
+ // получение инфы по кд о свободном пространтсве из скрытого уродского текстбокса в красивый элементик в шапочке
25
+ const freespacetextarea = document.querySelector("#free_space_area > label > textarea");
26
+ const frespace_out = document.querySelector("#frespace_out");
27
+ let prevValue = freespacetextarea.value;
28
+ setInterval(function () {
29
+ const currentValue = freespacetextarea.value;
30
+ if (currentValue !== prevValue) {
31
+ frespace_out.innerHTML = `${currentValue}`;
32
+ prevValue = currentValue;
33
+ }
34
+ }, 100);
35
+ // скликивание реальной но скрытой уродской кнопки с обработчиком проверки свободного места при нажатии на фейковую но красивую кнопочку
36
+ const freespace_getButton = document.querySelector("#freespace_get");
37
+ const free_spaceOrigButton = document.querySelector("#free_space_button");
38
+ freespace_getButton.addEventListener("click", function () {
39
+ free_spaceOrigButton.click();
40
+ });
41
+ // копирование реальных элементов из вкладок с моделями в результатах поиска
42
+ const SearchBlock = document.querySelector('#clear_search_models_results').closest('label').closest('div').closest('div').closest('div');
43
+ const ModelDLHeaderBlock = document.querySelector('.models_dl_header').closest('div').parentNode.closest('div').closest('div');
44
+ const ModelDLHeaderContainer = document.querySelector('.models_dl_header').closest('div').parentNode;
45
+ ModelDLHeaderBlock.appendChild(SearchBlock);
46
+ // небольшой css-фикс
47
+ ModelDLHeaderContainer.style.cssText = `display: flex; flex-direction: row; align-items: center; justify-content: flex-start; flex-wrap: wrap;`;
48
+ document.querySelector('.models_dl_header').parentNode.style.marginRight = "50px";
49
+ // фильтрация моделей при вводе
50
+ const searchInput = document.querySelector('input[type="text"]');
51
+ const findedModels = document.querySelector('#finded_models');
52
+ const tabModelsList = document.querySelector('#tab_models_list');
53
+ const labels = tabModelsList.querySelectorAll('label');
54
+ const clearSearchResultsButton = document.querySelector("#clear_search_models_results");
55
+ searchInput.addEventListener('input', (event) => {
56
+ const searchTerm = event.target.value.toLowerCase();
57
+ findedModels.innerHTML = '';
58
+ if (searchTerm !== '') {
59
+ labels.forEach((label) => {
60
+ const labelText = label.textContent.toLowerCase();
61
+ if (labelText.includes(searchTerm)) {
62
+ const clone = label.cloneNode(true);
63
+ findedModels.appendChild(clone);
64
+ const originalCheckbox = label.querySelector('input[type="checkbox"]');
65
+ const clonedCheckbox = clone.querySelector('input[type="checkbox"]');
66
+ clonedCheckbox.addEventListener('change', () => {
67
+ originalCheckbox.click();
68
+ });
69
+ }
70
+ });
71
+ }
72
+ });
73
+ // обработчик на кнопочку очистки результатов
74
+ clearSearchResultsButton.addEventListener('click', () => {
75
+ findedModels.innerHTML = '';
76
+ searchInput.value = '';
77
+ });
78
+ // автоматическое скликивание скрытых кнопок для подгрузки установленных моделей и свободного места после загрузки дополнения через 1 сек
79
+ setTimeout(() => document.querySelector("#files_button").click(), 1000);
80
+ setTimeout(() => document.querySelector("#free_space_button").click(), 1000);
81
+ // файловый менеджер на тексбоксах
82
+ setTimeout(() => {
83
+ const filesArea = document.querySelector("#files_area > label > textarea");
84
+ const filesCheckbox = document.querySelector("#files_checkbox");
85
+ const deleteArea = document.querySelector("#delete_area > label > textarea");
86
+ // обновление списка чекбоксов с файлами при изменении списка путей в текстбоксе
87
+ function addCheckboxEventListeners() {
88
+ const delete_checkboxes = document.querySelectorAll("#files_checkbox > label > input[type=checkbox]");
89
+ delete_checkboxes.forEach(checkbox => {
90
+ checkbox.addEventListener("change", event => {
91
+ // отмеченные на удаление делаем красными и зачеркнутыми
92
+ const delete_span = event.target.parentElement.querySelector("span");
93
+ if (event.target.checked) {
94
+ delete_span.style.textDecoration = "line-through";
95
+ delete_span.style.color = "#ed5252";
96
+ } else {
97
+ delete_span.style.textDecoration = "none";
98
+ delete_span.style.color = "";
99
+ }
100
+ });
101
+ });
102
+ }
103
+ // функция для обновления чекбоксов с файлами почти в реальном времени
104
+ function updateCheckboxes() {
105
+ while (filesCheckbox.firstChild) {
106
+ filesCheckbox.removeChild(filesCheckbox.firstChild);
107
+ }
108
+ const fileNames = filesArea.value.split("\n").map(path => path.split("/").pop());
109
+ if (fileNames.length === 0 || (fileNames.length === 1 && fileNames[0] === "")) {
110
+ const message = document.createElement("p");
111
+ message.textContent = "ничего не найдено";
112
+ filesCheckbox.appendChild(message);
113
+ } else {
114
+ fileNames.forEach(fileName => {
115
+ const checkbox = document.createElement("input");
116
+ checkbox.type = "checkbox";
117
+ checkbox.id = fileName;
118
+ checkbox.addEventListener("change", event => {
119
+ if (event.target.checked) {
120
+ deleteArea.value += (deleteArea.value ? "\n" : "") + fileName;
121
+ } else {
122
+ const lines = deleteArea.value.split("\n");
123
+ const index = lines.indexOf(fileName);
124
+ if (index !== -1) {
125
+ lines.splice(index, 1);
126
+ deleteArea.value = lines.join("\n");
127
+ }
128
+ }
129
+ deleteArea.dispatchEvent(new Event('input')); // имитация ввода, это самая важная часть кода
130
+ });
131
+ const label = document.createElement("label");
132
+ label.htmlFor = fileName;
133
+ label.className = "filecheckbox";
134
+ const span = document.createElement("span");
135
+ span.textContent = fileName;
136
+ label.appendChild(checkbox);
137
+ label.appendChild(span);
138
+ filesCheckbox.appendChild(label);
139
+ });
140
+ }
141
+ deleteArea.value = deleteArea.value.trim();
142
+ deleteArea.dispatchEvent(new Event('input')); // имитация ввода, без этого не работает
143
+ addCheckboxEventListeners()
144
+ }
145
+ // наблюдаем за скрытым тексбоксом с путями до моделей и обнов��ям чекбоксы
146
+ const observer = new MutationObserver(updateCheckboxes);
147
+ observer.observe(filesArea, { characterData: true, subtree: true });
148
+ updateCheckboxes();
149
+ // скликивание реальной но скрытой уродской кнопки с обработчиком обновления списка файлов при нажатии на фейковую но красивую кнопочку
150
+ const RefreshFilesButton = document.querySelector("#refresh_files_button");
151
+ RefreshFilesButton.addEventListener("click", () => {
152
+ document.querySelector("#files_button").click();
153
+ // задержки по 3 секунды необходимы, чтобы колаб одуплился
154
+ setTimeout(function () { updateCheckboxes(); }, 3000);
155
+ });
156
+ // при клике на фейковую кнопочку удаления - произойдет и обновление списка файлов с задержкой 3 сек
157
+ document.querySelector("#delete_button").addEventListener("click", function () {
158
+ setTimeout(function () {
159
+ document.querySelector("#files_button").click();
160
+ // задержки по 3 секунды необходимы, чтобы колаб одуплился
161
+ setTimeout(function () { updateCheckboxes(); }, 3000);
162
+ }, 3000);
163
+ });
164
+ // скликивание реальной но скрытой уродской кнопки с обработчиком удаления моделей при нажатии на фейковую но красивую кнопочку
165
+ const OrigDelButton = document.querySelector("#delete_button");
166
+ const CustomDelButton = document.querySelector("#delete_files_button");
167
+ CustomDelButton.addEventListener("click", () => {
168
+ OrigDelButton.click();
169
+ });
170
+ // проверка выхлопа из функции загрузки в скрытом текстбоксе
171
+ setInterval(function () {
172
+ var DLresultText = document.querySelector("#downloads_result_text > span.finish_dl_func");
173
+ DLresultText.textContent = document.querySelector("#dlresultbox > label > textarea").value;
174
+ // функция скрытия прогрессбара
175
+ function checkDLresult(element, text) {
176
+ if (element.textContent.includes(text)) {
177
+ document.querySelector("div.downloads_result_container > div.models_porgress_loader").style.removeProperty("display");
178
+ document.querySelector("#downloads_start_text").style.removeProperty("display");
179
+ document.querySelector("#downloads_result_text > span.dl_progress_info").textContent = "чтобы новые файлы появились в выпадающем списке моделей, нужно обновить их список по соответсвующей кнопке";
180
+ }
181
+ }
182
+ // просто скрываем прогрессбар если в выхлопе есть фраза о завершении или предупреждение
183
+ checkDLresult(DLresultText, "заверш");
184
+ checkDLresult(DLresultText, "слишком");
185
+ checkDLresult(DLresultText, "ОШИБКА");
186
+ // раскрашиваем текст сообщения о результате выполнения, если что-то пошло не так
187
+ if (DLresultText) {
188
+ if (DLresultText.textContent.includes("слишком")) {
189
+ DLresultText.style.setProperty("color", "#ff4f8b", "important");
190
+ } else if (DLresultText.textContent.includes("ОШИБКА")) {
191
+ DLresultText.style.setProperty("color", "#de2f2f", "important");
192
+ } else if (DLresultText.textContent.includes("заверш")) {
193
+ DLresultText.style.setProperty("color", "#99fb99", "important");
194
+ }
195
+ }
196
+ }, 200);
197
+ // действия по клику на фейковую но видимую кнопку для скачивания
198
+ document.querySelector("#general_download_button").addEventListener("click", function () {
199
+ // очистка текстбокса от выхлопа предыдущего выполнения
200
+ var resultTextareaDL = document.querySelector("#dlresultbox > label > textarea");
201
+ resultTextareaDL.value = "";
202
+ var resultClearOut = new Event("input", { bubbles: true }); // без этого не будет работать обноволение .value
203
+ resultTextareaDL.dispatchEvent(resultClearOut);
204
+ // делаем прогрессбар и место для результирующего текста видимыми
205
+ const DLprogressBar = document.querySelector("div.downloads_result_container > div.models_porgress_loader");
206
+ DLprogressBar.style.setProperty("display", "block", "important");
207
+ const DLresultText = document.querySelector("#downloads_result_text");
208
+ DLresultText.style.setProperty("display", "block", "important");
209
+ document.querySelector("#downloads_start_text").style.setProperty("display", "block", "important");
210
+ // скликивание реальных но скрытых кнопок с обработчиками загрузки файлов по чекбоксам и кастомных ссылок при нажатии на фейковую но кнопочку
211
+ // формирование списка из кастомных ссылок
212
+ document.querySelector("#ownlinks_download_button").click();
213
+ setTimeout(function () {
214
+ // через 3 сек. добавим его к списку ссылок из чекбоксов и отправим на загрузку вместе
215
+ document.querySelector("#checkboxes_download_button").click();
216
+ }, 3000); // задержка, чтобы колаб успел одуплиться
217
+ });
218
+ // если кнопка загрузки уже нажата, запрещаем кликать еще раз пока функция загрузки не выплюнет ответ
219
+ var GendownloadButton = document.querySelector("#general_download_button");
220
+ var DLprogressBar = document.querySelector("div.downloads_result_container > div.models_porgress_loader");
221
+ if (GendownloadButton && DLprogressBar) {
222
+ var DLobserver = new MutationObserver(function (mutations) {
223
+ mutations.forEach(function (mutation) {
224
+ if (mutation.type === "attributes" && mutation.attributeName === "style") { // отслеживание видимости прогрессбара
225
+ if (DLprogressBar.style.display === "block") {
226
+ GendownloadButton.setAttribute("disabled", "disabled");
227
+ } else {
228
+ setTimeout(function () {
229
+ GendownloadButton.removeAttribute("disabled");
230
+ }, 3000); // не сразу даем кликнуть снова, а после ожидания 3 секунды, чтобы не закликали
231
+ }
232
+ }
233
+ });
234
+ });
235
+ DLobserver.observe(DLprogressBar, { attributes: true });
236
+ }
237
+ }, 9000); // запуск скриптов через 9 секунд после загрузки вебуи, чтобы успели отработать скрипты других дополнений и градио
238
+ }
239
+ });
240
+ }
241
+ });
242
+ });
243
+ observer.observe(document.body, { childList: true, subtree: true });
244
+ });
scripts/colab_sd_models.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import requests
4
+ import shutil
5
+ import subprocess
6
+ from concurrent.futures import ThreadPoolExecutor, as_completed
7
+
8
+ import gradio as gr
9
+ from bs4 import BeautifulSoup
10
+ from modules import script_callbacks, scripts, shared, sd_models
11
+ from modules.paths_internal import data_path
12
+
13
+
14
+ urlprefix = "https://huggingface.co/2ch/models/resolve/main/"
15
+ models_json_data = requests.get(urlprefix + "colab_models.json").json()
16
+ wget = "wget -nv -t 10 --show-progress --progress=bar:force -q --content-disposition "
17
+ sdroot = data_path
18
+ models_folder_path = sd_models.model_path
19
+ loras_folder_path = shared.cmd_opts.lora_dir
20
+ embeddings_folder_path = shared.cmd_opts.embeddings_dir
21
+ ct = "token=542c1d6077168822e1b49e30e3437a5d"
22
+
23
+
24
+ def on_ui_tabs():
25
+ with gr.Blocks() as models_list:
26
+ # шапка вкладки с поиском и свободным местом на диске
27
+ gr.HTML(
28
+ """<div class="models_top_container"><div class="models_top_header_text"><h1 class="models_dl_header">выбор и скачивание моделей</h1><p>учитывай весьма ограниченное пространство на диске в колабе!</p></div><div class="freespaceinfo"><div id="frespace_output"><span>свободно в колабе: <span id="frespace_out">нажми на кнопочку</span></div><div id="freespace_get"></div></div></div>""")
29
+ search_and_results = gr.HTML(
30
+ """<label id="models_search_field"><input type="text" id="models_search_input" class="svelte-1pie7s6" placeholder="начни вводить для поиска"/><span id="clear_search_models_results"></span></label><div id="finded_models"></div>""")
31
+ # вкладки с категориями
32
+ buttons = []
33
+ checkbox_groups = []
34
+ checkboxes = []
35
+ for category in models_json_data['categories']:
36
+ categories = ["models_A", "models_B", "models_C", "models_D", "models_E", "models_F", "models_G", "models_H", "models_I", "models_J", "models_K", "models_L", "models_M", "models_N", "models_O", "models_P", "models_Q"]
37
+ tab_names = ["аниме", "лайнарт", "женские", "игры и кино", "техника и космос", "крипота", "макро", "мемные", "мужские", "мультфильмы", "пиксельарт", "трехмерная графика", "универсальные", "фотореализм", "фурри", "футанари",
38
+ "художественные"]
39
+ category_map = dict(zip(categories, tab_names))
40
+ tab_name = category_map.get(category)
41
+ with gr.Tab(tab_name):
42
+ checkbox_group = gr.CheckboxGroup(sorted(models_json_data['categories'][category], key=lambda x: x.lower()), label="")
43
+ checkboxes.append(checkbox_group)
44
+ checkbox_groups.append(checkbox_group)
45
+ # ссылки для реквеста добавления моделей
46
+ gr.HTML(
47
+ """<div class="request_new_models">не нашел нужную модель? запрос на добавление можно отправить в <a href="https://t.me/colabSDbot" target="_blank">бота</a> или <a href="https://t.me/stabdiff" target="_blank">группу</a>.</div>""")
48
+
49
+ # функция для формирования списка своих ссылок
50
+ def get_own_links(ownmodels, ownloras, ownembeddings):
51
+ urls = []
52
+ for text, dlpath in zip([ownmodels, ownloras, ownembeddings], [models_folder_path, loras_folder_path, embeddings_folder_path]):
53
+ lines = text.split("\n")
54
+ for line in lines:
55
+ if line.strip():
56
+ # url = wget+line+" -P "+dlpath
57
+ link = line.strip() + (f'?{ct}' if '?' not in line else f'&{ct}') if 'civitai' in line else line.strip()
58
+ url = f'{wget}"{link}" -P {dlpath}'
59
+ urls.append(url)
60
+ own_urls = os.path.join(sdroot, "urls.txt")
61
+ with open(own_urls, "w") as f:
62
+ for url in urls:
63
+ f.write(url + "\n")
64
+ print("список загрузки сформирован...")
65
+
66
+ # текстбоксы для указания своих ссылок
67
+ gr.HTML("""<div class="ownfiles_header"><h2>здесь можно указать прямые ссылки на загрузку моделей, лор и внедрений</h2></div>""")
68
+ with gr.Row():
69
+ plhd = """вставляй каждую ссылку с новой строки!\nпримеры ссылок:\nhttps://models.tensorplay.ai/104249\nhttps://civitai.com/api/download/models/110660\nhttps://huggingface.co/2ch/gay/resolve/main/lora/BettercocksFlaccid.safetensors"""
70
+ ownmodels = gr.Textbox(label="модели", placeholder=plhd, info="прямые ссылки на Checkpoints", lines=5, elem_id="ownmodels")
71
+ ownloras = gr.Textbox(label="лоры", placeholder=plhd, info="прямые ссылки на LoRas", lines=5, elem_id="ownloras")
72
+ ownembeddings = gr.Textbox(label="внедрения", placeholder=plhd, info="прямые ссылки на Textual Inversions", lines=5, elem_id="ownembeddings")
73
+ # кнопка при нажатии на которую происходит сначала клик на кнопку формирования списка своих ссылок, а потом на кнопку основной функции загрузки
74
+ download_button = gr.Button("скачать выбранные модели", elem_id="general_download_button")
75
+ # кнопка для формирования списка своих ссылок
76
+ button = gr.Button("скачать по ссылкам", elem_id="ownlinks_download_button")
77
+ button.click(get_own_links, inputs=[ownmodels, ownloras, ownembeddings])
78
+ # кнопка для отправки на загрузку
79
+ download_button = gr.Button("скачать отмеченные модели", elem_id="checkboxes_download_button")
80
+
81
+ # функция определения точки монтирования и свободного места на диске
82
+ def find_mount_point():
83
+ # __file__ = "."
84
+ path = os.path.realpath(__file__)
85
+ path = os.path.abspath(path)
86
+ while not os.path.ismount(path):
87
+ path = os.path.dirname(path)
88
+ return path
89
+
90
+ def free_space():
91
+ total, used, free = shutil.disk_usage(find_mount_point())
92
+ power = 2 ** 10
93
+ n = 0
94
+ power_labels = {0: '', 1: 'Кило', 2: 'Мега', 3: 'Гига', 4: 'Тера'}
95
+ while free > power:
96
+ free /= power
97
+ n += 1
98
+ return f"{free:.2f} {power_labels[n]}байт"
99
+
100
+ # функция вычисления общего размера загружаемых файлов в байтах
101
+ def get_file_size(url):
102
+ url = re.search(r'https?://\S+', url).group()
103
+
104
+ def contleght(url):
105
+ return int(requests.get(url, stream=True).headers.get('Content-Length', 0))
106
+
107
+ if "huggingface" in url:
108
+ try:
109
+ try:
110
+ file_size = int(next((pre.text.split('size ')[1].split('\n')[0] for pre in BeautifulSoup(requests.get(url.replace('resolve', 'blame')).text, 'html.parser').find_all('pre') if 'size' in pre.text)))
111
+ except:
112
+ file_size_text = BeautifulSoup(requests.get(url.replace('resolve', 'blob')).text, 'html.parser').find('strong', string='Size of remote file:').next_sibling.strip()
113
+ file_size = int(float(file_size_text.split()[0]) * {'KB': 1024, 'MB': 1024 ** 2, 'GB': 1024 ** 3, 'TB': 1024 ** 4}[file_size_text.split()[1]])
114
+ except:
115
+ file_size = contleght(url)
116
+ if file_size < 1048576: file_size = contleght(url)
117
+ elif "civitai" in url:
118
+ try:
119
+ file_size = int(requests.get("https://civitai.com/api/v1/model-versions/" + url.split('/')[-1] + civitai_token).json()["files"][0]["sizeKB"] * 1024)
120
+ except:
121
+ file_size = contleght(url)
122
+ if file_size < 1048576: file_size = contleght(url)
123
+ else:
124
+ file_size = contleght(url)
125
+ return file_size
126
+
127
+ # основная функция для загрузки отмеченных чекбоксов
128
+ def start_download(*checkbox_groups):
129
+ try:
130
+ urls = []
131
+ for checkbox_group in checkbox_groups:
132
+ selected_choices = checkbox_group
133
+ for choice in selected_choices:
134
+ if choice in models_json_data["models"]:
135
+ file_names = models_json_data["models"][choice].split(',')
136
+ for file_name in file_names:
137
+ url = wget + urlprefix + file_name.strip() + " -P " + models_folder_path
138
+ urls.append(url)
139
+ own_urls = os.path.join(sdroot, "urls.txt")
140
+ with open(own_urls, "r") as f:
141
+ own_links = f.read().splitlines()
142
+ urls.extend(own_links)
143
+ os.remove(own_urls)
144
+ except Exception as e:
145
+ print(f"ОШИБКА: {e}")
146
+ return f"ОШИБКА: {e}"
147
+ try:
148
+ def bytes_convert(size_bytes):
149
+ if size_bytes >= 1073741824:
150
+ return f"{round(size_bytes / 1073741824, 2)} Гб"
151
+ else:
152
+ return f"{round(size_bytes / 1048576, 2)} Мб"
153
+
154
+ def downloader(url):
155
+ process = subprocess.Popen(url, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
156
+ while True:
157
+ output = process.stdout.readline().decode('utf-8')
158
+ if output == '' and process.poll() is not None:
159
+ break
160
+ if output:
161
+ yield output.strip()
162
+ return process.poll()
163
+
164
+ total_file_size = 0
165
+ with ThreadPoolExecutor(max_workers=len(urls)) as executor:
166
+ futures = [executor.submit(get_file_size, url) for url in urls]
167
+ for future in as_completed(futures):
168
+ total_file_size += future.result()
169
+ total, used, free = shutil.disk_usage(find_mount_point())
170
+ if total_file_size <= (free - 1073741824):
171
+ print(f"загрузка {bytes_convert(total_file_size)} уже началась, жди!")
172
+ with ThreadPoolExecutor(max_workers=len(urls)) as executor:
173
+ futures = [executor.submit(downloader, url) for url in urls]
174
+ for future in as_completed(futures):
175
+ for line in future.result():
176
+ print(line)
177
+ if os.path.exists(os.path.join(models_folder_path, "nullModel.ckpt")):
178
+ try:
179
+ os.remove(os.path.join(models_folder_path, "nullModel.ckpt"))
180
+ except:
181
+ pass
182
+ return "функция загрузки завершила работу!"
183
+ else:
184
+ print(f"слишком много файлов! ты пытаешься скачать {bytes_convert(total_file_size)}, имея свободных только {bytes_convert(free)} (и как минимум 1 Гб должен оставаться не занятым на диске!).")
185
+ return f"слишком много файлов! ты пытаешься скачать {bytes_convert(total_file_size)}, имея свободных только {bytes_convert(free)} (и как минимум 1 Гб должен оставаться не занятым на диске!)."
186
+ except Exception as e:
187
+ print(f"ОШИБКА: {e}")
188
+ return f"ОШИБКА: {e}"
189
+
190
+ # скрытый текстбокс для вывода информации о результате
191
+ dlresultbox = gr.Textbox(label="", elem_id="dlresultbox")
192
+ download_button.click(start_download, inputs=checkbox_groups, outputs=dlresultbox)
193
+ # анимация процесса выполнения функции загрузки
194
+ gr.HTML(
195
+ """<div class="downloads_result_container"><div class="models_porgress_loader"></div><div id="downloads_start_text">задача по загрузке запущена, подробности в выводе ячейки в колабе...</div><div id="downloads_result_text"><span class="finish_dl_func"></span><span class="dl_progress_info"></span></div></div>""")
196
+ # скрытые элементы для взаимодействия с функцией определения места на диске
197
+ spacetextbox = gr.Textbox(label="", elem_id="free_space_area")
198
+ space_button = gr.Button("проверить свободное место", elem_id="free_space_button")
199
+ space_button.click(fn=free_space, outputs=spacetextbox)
200
+ # HTML-разметка для формирования чекбоксов для удаления файлов моделей
201
+ gr.HTML(
202
+ """<hr class="divider"/><div id="filemanager"><h2 class="current_models_files">файлы моделей которые можно удалить для освобождения места:</h2><div id="files_checkbox"></div><div class="filebuttons"><div id="delete_files_button"></div><div id="refresh_files_button"></div></div></div>""")
203
+
204
+ # функция получения путей до установленных файлов моделей
205
+ def get_models_paths():
206
+ file_paths = []
207
+ for root, dirs, files in os.walk(models_folder_path):
208
+ for file in files:
209
+ file_path = os.path.join(root, file)
210
+ file_paths.append(file_path)
211
+ return '\n'.join(file_paths)
212
+
213
+ # скрытые текстбокс и кнопка для получения списка файлов из папки моделей
214
+ filestextbox = gr.Textbox(label="", elem_id="files_area")
215
+ files_button = gr.Button("установленные модели", elem_id="files_button")
216
+ files_button.click(fn=get_models_paths, outputs=filestextbox)
217
+
218
+ # функция удаления списка отмеченных файлов
219
+ def del_models(inputs):
220
+ files_to_delete = inputs.split("\n")
221
+ for file in files_to_delete:
222
+ if file and file != "None":
223
+ try:
224
+ os.remove(os.path.join(models_folder_path, file))
225
+ print(f"успешно удалена модель: {file}")
226
+ except OSError as e:
227
+ print(f"ОШИБКА: {e.filename} - {e.strerror}.")
228
+ else:
229
+ print("удалять нечего, или ничего не выбрано для удаления")
230
+
231
+ # скрытые текстбокс и кнопка для формирования списка файлов на удаление
232
+ deletetextbox = gr.Textbox(label="", elem_id="delete_area")
233
+ delete_button = gr.Button("удалить", elem_id="delete_button")
234
+ delete_button.click(fn=del_models, inputs=deletetextbox, outputs=deletetextbox)
235
+ with gr.Accordion("описания моделей (не обновляется)", open=False):
236
+ gr.HTML(requests.get("https://raw.githubusercontent.com/PR0LAPSE/PR0LAPSE.github.io/main/models.html").text)
237
+ # возврат всех элементов
238
+ return (models_list, "модели", "models_list"),
239
+
240
+
241
+ # вывод в вебуи
242
+ script_callbacks.on_ui_tabs(on_ui_tabs)
style.css ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #free_space_area,
2
+ #free_space_button,
3
+ #files_area,
4
+ #delete_area,
5
+ #files_button,
6
+ #delete_button,
7
+ #ownlinks_download_button,
8
+ #checkboxes_download_button,
9
+ #dlresultbox,
10
+ #downloads_result_text,
11
+ #downloads_start_text,
12
+ .models_porgress_loader {
13
+ display: none !important;
14
+ }
15
+ #models_search_input {
16
+ width: 500px !important;
17
+ float: none !important;
18
+ display: inline !important;
19
+ position: relative;
20
+ top: -25px;
21
+ }
22
+ #clear_search_models_results {
23
+ display: inline-block;
24
+ cursor: pointer !important;
25
+ --wh: 40px;
26
+ width: var(--wh);
27
+ height: var(--wh);
28
+ padding-top: 0px !important;
29
+ margin: 10px 0 0 10px!important;
30
+ position: relative;
31
+ top: -10px;
32
+ border-radius: 5px;
33
+ border: var(--button-border-width) solid var(--button-secondary-border-color);
34
+ text-align: center;
35
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cpath d='m9.02,30.98L30.98,9.02m0,21.96L9.02,9.02' fill='none' opacity='.5' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.17'/%3E%3C/svg%3E");
36
+ background-color: var(--button-secondary-background-fill);
37
+ }
38
+ #finded_models {
39
+ position: absolute;
40
+ z-index: 2;
41
+ width: 500px;
42
+ top: 50px;
43
+ background: var(--input-background-fill);
44
+ color: var(--body-text-color);
45
+ }
46
+ #finded_models label {
47
+ background: none !important;
48
+ border: none;
49
+ }
50
+ #tab_models_list .tabs label {
51
+ background: none !important;
52
+ border: none;
53
+ }
54
+ #tab_models_list .tabs .tabitem div fieldset div {
55
+ display: grid;
56
+ grid-gap: 10px;
57
+ }
58
+ #tab_models_list div[data-testid='checkbox-group'] label span {
59
+ width: 275px;
60
+ font-size: 0.92em !important;
61
+ white-space: nowrap;
62
+ overflow: hidden;
63
+ text-overflow: ellipsis;
64
+ }
65
+ #tab_models_list div.tab-nav > button {
66
+ font-size: 1em !important;
67
+ }
68
+ #tab_models_list fieldset label:hover:not(.selected) {
69
+ color: var(--checkbox-background-color-selected)!important;
70
+ }
71
+ #tab_models_list fieldset .selected {
72
+ color: #60d43d !important;
73
+ }
74
+ #general_download_button {
75
+ border: var(--button-border-width) solid var(--button-primary-border-color);
76
+ background: var(--button-primary-background-fill);
77
+ color: var(--button-primary-text-color);
78
+ margin-top: 28px !important;
79
+ }
80
+ #tab_models_list button.secondary {
81
+ width: 70%;
82
+ margin: 0 auto;
83
+ }
84
+ #male_only {
85
+ color: #b8dabb !important;
86
+ }
87
+ .freespaceinfo {
88
+ width: fit-content;
89
+ display: flex;
90
+ flex-direction: row;
91
+ flex-wrap: nowrap;
92
+ justify-content: flex-start;
93
+ align-items: center;
94
+ }
95
+ #frespace_output {
96
+ width: fit-content;
97
+ height: 40px;
98
+ border: 1px solid var(--button-secondary-border-color);
99
+ margin: -30px 0px 0px 0px;
100
+ display: flex;
101
+ flex-direction: row;
102
+ flex-wrap: nowrap;
103
+ align-items: center;
104
+ padding: 0px 15px;
105
+ font-size: 1.2em;
106
+ }
107
+ #freespace_get {
108
+ width: 40px;
109
+ height: 40px;
110
+ border: 1px solid var(--button-secondary-border-color);
111
+ margin: -30px 50px 0px 10px;
112
+ cursor: pointer;
113
+ background-image: url("data:image/svg+xml,%3Csvg id='refresh' xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 36 36'%3E%3Cpath fill='%23fff' opacity='.5' d='m3.33,33v-2.81h6.09l-.7-.56c-2-1.59-3.45-3.33-4.36-5.2-.91-1.88-1.36-3.97-1.36-6.28,0-3.31.98-6.29,2.93-8.93,1.95-2.64,4.52-4.46,7.71-5.46v2.91c-2.34.91-4.23,2.41-5.67,4.52-1.44,2.11-2.16,4.43-2.16,6.96,0,1.97.37,3.68,1.1,5.13.73,1.45,1.73,2.71,2.98,3.77l1.41.98v-5.81h2.81v10.78H3.33Zm19.08-.7v-2.95c2.37-.91,4.27-2.41,5.67-4.52,1.41-2.11,2.11-4.43,2.11-6.96,0-1.5-.37-3.02-1.1-4.57-.73-1.55-1.7-2.91-2.88-4.1l-1.36-1.22v5.81h-2.81V3h10.78v2.81h-6.14l.7.66c1.88,1.75,3.28,3.63,4.22,5.63.94,2,1.41,3.92,1.41,5.77,0,3.31-.97,6.3-2.91,8.95-1.94,2.66-4.5,4.48-7.69,5.48Z'%3E%3C/path%3E%3C/svg%3E");
114
+ background-position: 50%;
115
+ background-repeat: no-repeat
116
+ }
117
+ #freespace_get:hover {
118
+ background-color: #00000078;
119
+ background-image: url("data:image/svg+xml,%3Csvg id='refresh' xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 36 36'%3E%3Cpath fill='%23fff' opacity='.7' d='m3.33,33v-2.81h6.09l-.7-.56c-2-1.59-3.45-3.33-4.36-5.2-.91-1.88-1.36-3.97-1.36-6.28,0-3.31.98-6.29,2.93-8.93,1.95-2.64,4.52-4.46,7.71-5.46v2.91c-2.34.91-4.23,2.41-5.67,4.52-1.44,2.11-2.16,4.43-2.16,6.96,0,1.97.37,3.68,1.1,5.13.73,1.45,1.73,2.71,2.98,3.77l1.41.98v-5.81h2.81v10.78H3.33Zm19.08-.7v-2.95c2.37-.91,4.27-2.41,5.67-4.52,1.41-2.11,2.11-4.43,2.11-6.96,0-1.5-.37-3.02-1.1-4.57-.73-1.55-1.7-2.91-2.88-4.1l-1.36-1.22v5.81h-2.81V3h10.78v2.81h-6.14l.7.66c1.88,1.75,3.28,3.63,4.22,5.63.94,2,1.41,3.92,1.41,5.77,0,3.31-.97,6.3-2.91,8.95-1.94,2.66-4.5,4.48-7.69,5.48Z'%3E%3C/path%3E%3C/svg%3E");
120
+ }
121
+ #freespace_get:active {
122
+ background-color: #00000078;
123
+ background-image: url("data:image/svg+xml,%3Csvg id='refresh' xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 36 36'%3E%3Cpath fill='%23fff' opacity='.85' d='m3.33,33v-2.81h6.09l-.7-.56c-2-1.59-3.45-3.33-4.36-5.2-.91-1.88-1.36-3.97-1.36-6.28,0-3.31.98-6.29,2.93-8.93,1.95-2.64,4.52-4.46,7.71-5.46v2.91c-2.34.91-4.23,2.41-5.67,4.52-1.44,2.11-2.16,4.43-2.16,6.96,0,1.97.37,3.68,1.1,5.13.73,1.45,1.73,2.71,2.98,3.77l1.41.98v-5.81h2.81v10.78H3.33Zm19.08-.7v-2.95c2.37-.91,4.27-2.41,5.67-4.52,1.41-2.11,2.11-4.43,2.11-6.96,0-1.5-.37-3.02-1.1-4.57-.73-1.55-1.7-2.91-2.88-4.1l-1.36-1.22v5.81h-2.81V3h10.78v2.81h-6.14l.7.66c1.88,1.75,3.28,3.63,4.22,5.63.94,2,1.41,3.92,1.41,5.77,0,3.31-.97,6.3-2.91,8.95-1.94,2.66-4.5,4.48-7.69,5.48Z'%3E%3C/path%3E%3C/svg%3E");
124
+ transform: rotate(90deg)
125
+ }
126
+ span#frespace_out {
127
+ color: #7ced7c
128
+ }
129
+ .ownfiles_header {
130
+ display: flex;
131
+ }
132
+ .ownfiles_header > span {
133
+ margin-right: 50px;
134
+ }
135
+ .ownfiles_header h2 {
136
+ font-size: 1.2em !important;
137
+ color: #deeedb !important;
138
+ font-weight: normal !important
139
+ }
140
+ #ownmodels > label > span {
141
+ color: #37e46f;
142
+ }
143
+ #ownloras > label > span {
144
+ color: #45b9ec;
145
+ }
146
+ #ownembeddings > label > span {
147
+ color: #f46ea2;
148
+ }
149
+ #ownmodels > label > textarea,
150
+ #ownloras > label > textarea,
151
+ #ownembeddings > label > textarea {
152
+ font-family: monospace;
153
+ font-size: 0.95em
154
+ }
155
+ #ownmodels > label > textarea {
156
+ color: #a7d0b4;
157
+ }
158
+ #ownloras > label > textarea {
159
+ color: #9bc5d9;
160
+ }
161
+ #ownembeddings > label > textarea {
162
+ color: #dcbfcb;
163
+ }
164
+ #filemanager {
165
+ height: max-content;
166
+ }
167
+ .current_models_files {
168
+ margin-bottom: 30px!important;
169
+ color: #eedbdb !important;
170
+ }
171
+ #files_checkbox {
172
+ width: max-content;
173
+ display: grid;
174
+ grid-template-columns: repeat(3, 1fr);
175
+ grid-gap: 35px;
176
+ grid-row-gap: 18px;
177
+ }
178
+ .filecheckbox input {
179
+ margin-right: 6px;
180
+ border: var(--checkbox-border-width) solid var(--checkbox-border-color) !important;
181
+ background-color: var(--checkbox-background-color) !important;
182
+ line-height: var(--line-sm) !important;
183
+ }
184
+ .filecheckbox input:checked {
185
+ border-color: #f47373 !important;
186
+ background-image: #de2f2f !important;
187
+ background-color: #de2f2f !important;
188
+ }
189
+ .filecheckbox span:hover {
190
+ color: #ff9494
191
+ }
192
+ .filebuttons {
193
+ display: flex;
194
+ flex-direction: row;
195
+ flex-wrap: wrap;
196
+ align-content: center;
197
+ align-items: center;
198
+ justify-content: center;
199
+ margin-top: 15px;
200
+ padding-top: 15px;
201
+ border-top: 2px solid #afafaf17;
202
+ }
203
+ #delete_files_button,
204
+ #refresh_files_button {
205
+ width: 40px;
206
+ height: 40px;
207
+ border: 1px solid var(--button-secondary-border-color);
208
+ margin: 0 50px;
209
+ cursor: pointer;
210
+ background-color: transparent;
211
+ background-position: 50%;
212
+ background-repeat: no-repeat
213
+ }
214
+ #refresh_files_button {
215
+ background-image: url("data:image/svg+xml,%3Csvg id='refresh' xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 36 36'%3E%3Cpath fill='%23fff' opacity='.5' d='m3.33,33v-2.81h6.09l-.7-.56c-2-1.59-3.45-3.33-4.36-5.2-.91-1.88-1.36-3.97-1.36-6.28,0-3.31.98-6.29,2.93-8.93,1.95-2.64,4.52-4.46,7.71-5.46v2.91c-2.34.91-4.23,2.41-5.67,4.52-1.44,2.11-2.16,4.43-2.16,6.96,0,1.97.37,3.68,1.1,5.13.73,1.45,1.73,2.71,2.98,3.77l1.41.98v-5.81h2.81v10.78H3.33Zm19.08-.7v-2.95c2.37-.91,4.27-2.41,5.67-4.52,1.41-2.11,2.11-4.43,2.11-6.96,0-1.5-.37-3.02-1.1-4.57-.73-1.55-1.7-2.91-2.88-4.1l-1.36-1.22v5.81h-2.81V3h10.78v2.81h-6.14l.7.66c1.88,1.75,3.28,3.63,4.22,5.63.94,2,1.41,3.92,1.41,5.77,0,3.31-.97,6.3-2.91,8.95-1.94,2.66-4.5,4.48-7.69,5.48Z'%3E%3C/path%3E%3C/svg%3E");
216
+ }
217
+ #refresh_files_button:hover {
218
+ background-color: #00000078;
219
+ background-image: url("data:image/svg+xml,%3Csvg id='refresh' xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 36 36'%3E%3Cpath fill='%23fff' opacity='.7' d='m3.33,33v-2.81h6.09l-.7-.56c-2-1.59-3.45-3.33-4.36-5.2-.91-1.88-1.36-3.97-1.36-6.28,0-3.31.98-6.29,2.93-8.93,1.95-2.64,4.52-4.46,7.71-5.46v2.91c-2.34.91-4.23,2.41-5.67,4.52-1.44,2.11-2.16,4.43-2.16,6.96,0,1.97.37,3.68,1.1,5.13.73,1.45,1.73,2.71,2.98,3.77l1.41.98v-5.81h2.81v10.78H3.33Zm19.08-.7v-2.95c2.37-.91,4.27-2.41,5.67-4.52,1.41-2.11,2.11-4.43,2.11-6.96,0-1.5-.37-3.02-1.1-4.57-.73-1.55-1.7-2.91-2.88-4.1l-1.36-1.22v5.81h-2.81V3h10.78v2.81h-6.14l.7.66c1.88,1.75,3.28,3.63,4.22,5.63.94,2,1.41,3.92,1.41,5.77,0,3.31-.97,6.3-2.91,8.95-1.94,2.66-4.5,4.48-7.69,5.48Z'%3E%3C/path%3E%3C/svg%3E");
220
+ }
221
+ #refresh_files_button:active {
222
+ background-color: #00000078;
223
+ background-image: url("data:image/svg+xml,%3Csvg id='refresh' xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 36 36'%3E%3Cpath fill='%23fff' opacity='.85' d='m3.33,33v-2.81h6.09l-.7-.56c-2-1.59-3.45-3.33-4.36-5.2-.91-1.88-1.36-3.97-1.36-6.28,0-3.31.98-6.29,2.93-8.93,1.95-2.64,4.52-4.46,7.71-5.46v2.91c-2.34.91-4.23,2.41-5.67,4.52-1.44,2.11-2.16,4.43-2.16,6.96,0,1.97.37,3.68,1.1,5.13.73,1.45,1.73,2.71,2.98,3.77l1.41.98v-5.81h2.81v10.78H3.33Zm19.08-.7v-2.95c2.37-.91,4.27-2.41,5.67-4.52,1.41-2.11,2.11-4.43,2.11-6.96,0-1.5-.37-3.02-1.1-4.57-.73-1.55-1.7-2.91-2.88-4.1l-1.36-1.22v5.81h-2.81V3h10.78v2.81h-6.14l.7.66c1.88,1.75,3.28,3.63,4.22,5.63.94,2,1.41,3.92,1.41,5.77,0,3.31-.97,6.3-2.91,8.95-1.94,2.66-4.5,4.48-7.69,5.48Z'%3E%3C/path%3E%3C/svg%3E");
224
+ transform: rotate(90deg)
225
+ }
226
+ #delete_files_button {
227
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='36' viewBox='0 0 32 36'%3E%3Cpath d='m10.05,27.05l5.95-6.05,6,6.05,2.35-2.4-5.95-6.05,5.95-6.05-2.35-2.4-6,6.05-5.95-6.05-2.4,2.4,6,6.05-6,6.05,2.4,2.4Zm-5,8.95c-.8,0-1.5-.3-2.1-.9s-.9-1.3-.9-2.1V4.5H0V1.5h9.4V0h13.2v1.5h9.4v3h-2.05v28.5c0,.8-.3,1.5-.9,2.1s-1.3.9-2.1.9H5.05ZM26.95,4.5H5.05v28.5h21.9V4.5Zm-21.9,0v28.5V4.5Z' opacity='.5' fill='%23fff'/%3E%3C/svg%3E");
228
+ background-size: 62%;
229
+ }
230
+ #delete_files_button:hover {
231
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='36' viewBox='0 0 32 36'%3E%3Cpath d='m10.05,27.05l5.95-6.05,6,6.05,2.35-2.4-5.95-6.05,5.95-6.05-2.35-2.4-6,6.05-5.95-6.05-2.4,2.4,6,6.05-6,6.05,2.4,2.4Zm-5,8.95c-.8,0-1.5-.3-2.1-.9s-.9-1.3-.9-2.1V4.5H0V1.5h9.4V0h13.2v1.5h9.4v3h-2.05v28.5c0,.8-.3,1.5-.9,2.1s-1.3.9-2.1.9H5.05ZM26.95,4.5H5.05v28.5h21.9V4.5Zm-21.9,0v28.5V4.5Z' opacity='.7' fill='%23ff2727'/%3E%3C/svg%3E");
232
+ background-size: 62%;
233
+ }
234
+ #delete_files_button:active {
235
+ background-color: #00000078;
236
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='36' viewBox='0 0 32 36'%3E%3Cpath d='m10.05,27.05l5.95-6.05,6,6.05,2.35-2.4-5.95-6.05,5.95-6.05-2.35-2.4-6,6.05-5.95-6.05-2.4,2.4,6,6.05-6,6.05,2.4,2.4Zm-5,8.95c-.8,0-1.5-.3-2.1-.9s-.9-1.3-.9-2.1V4.5H0V1.5h9.4V0h13.2v1.5h9.4v3h-2.05v28.5c0,.8-.3,1.5-.9,2.1s-1.3.9-2.1.9H5.05ZM26.95,4.5H5.05v28.5h21.9V4.5Zm-21.9,0v28.5V4.5Z' opacity='1' fill='%23ff2727'/%3E%3C/svg%3E");
237
+ background-size: 62%;
238
+ }
239
+ .models_porgress_loader {
240
+ width: 70%;
241
+ height: 8px;
242
+ display: block;
243
+ background: var(--block-label-border-color);
244
+ position: relative;
245
+ top: 0px;
246
+ overflow: hidden;
247
+ margin: 0 auto;
248
+ }
249
+ .models_porgress_loader::after {
250
+ content: '';
251
+ width: 0%;
252
+ height: 8px;
253
+ background-color: #ffffff2e;
254
+ background-image: linear-gradient(45deg, rgba(0, 0, 0, .25) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, 0.25) 50%, rgba(0, 0, 0, 0.25) 75%, transparent 75%, transparent);
255
+ background-size: 15px 15px;
256
+ position: absolute;
257
+ top: 0;
258
+ left: 0;
259
+ box-sizing: border-box;
260
+ animation: models_progress_bar_anim 10s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
261
+ }
262
+ @keyframes models_progress_bar_anim {
263
+ 0% {
264
+ width: 0;
265
+ }
266
+ 100% {
267
+ width: 100%;
268
+ }
269
+ }
270
+ #downloads_result_text,
271
+ #downloads_start_text {
272
+ margin: 32px auto;
273
+ width: fit-content;
274
+ top: 100px;
275
+ font-size: 1.4em;
276
+ }
277
+ #downloads_start_text {
278
+ color: #81d0fb !important
279
+ }
280
+ #downloads_result_text > span {
281
+ display: block;
282
+ text-align: center;
283
+ }
284
+ #downloads_result_text > span.finish_dl_func {
285
+ color: #99fb99 !important
286
+ }
287
+ #downloads_result_text > span.dl_progress_info {
288
+ font-size: 0.7em!important;
289
+ margin-top: 10px;
290
+ }
291
+ #tab_models_list #colab_model_list.colab_model_list {
292
+ background: transparent !important;
293
+ background-image: none !important;
294
+ }
295
+ .request_new_models a {
296
+ color: var(--link-text-color) !important;
297
+ text-decoration: underline dashed !important
298
+ }
299
+ .request_new_models a:hover {
300
+ color: var(--link-text-color-hover) !important;
301
+ text-decoration: underline solid !important
302
+ }