charliebaby2023 commited on
Commit
0c1a5fe
·
verified ·
1 Parent(s): 0cf988f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +421 -93
app.py CHANGED
@@ -1,96 +1,424 @@
 
 
1
  import gradio as gr
2
- from civitai_to_hf import (search_civitai, download_civitai, select_civitai_item, add_civitai_item, get_civitai_tag, select_civitai_all_item,
3
- update_civitai_selection, update_civitai_checkbox, from_civitai_checkbox,
4
- CIVITAI_TYPE, CIVITAI_BASEMODEL, CIVITAI_SORT, CIVITAI_PERIOD, CIVITAI_FILETYPE)
5
-
6
- css = """
7
- .title { font-size: 3em; align-items: center; text-align: center; }
8
- .info { align-items: center; text-align: center; }
9
- .block.result { margin: 1em 0; padding: 1em; box-shadow: 0 0 3px 3px #664422, 0 0 3px 2px #664422 inset; border-radius: 6px; background: #665544; }
10
- .desc [src$='#float'] { float: right; margin: 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", fill_width=True, css=css, delete_cache=(60, 3600)) as demo:
14
- with gr.Column():
15
- gr.Markdown("# CivitAI to HF🤗 Downloader & Uploader", elem_classes="title")
16
- state = gr.State(value={})
17
- with gr.Accordion("Search Civitai", open=True):
18
- with gr.Row():
19
- search_civitai_type = gr.CheckboxGroup(label="Type", choices=CIVITAI_TYPE, value=["Checkpoint", "LORA"])
20
- search_civitai_basemodel = gr.CheckboxGroup(label="Base Model", choices=CIVITAI_BASEMODEL, value=[])
21
- search_civitai_filetype = gr.CheckboxGroup(label="File type", choices=CIVITAI_FILETYPE, value=["Model"])
22
- with gr.Row():
23
- search_civitai_sort = gr.Radio(label="Sort", choices=CIVITAI_SORT, value=CIVITAI_SORT[0])
24
- search_civitai_period = gr.Radio(label="Period", choices=CIVITAI_PERIOD, value="Month")
25
- search_civitai_limit = gr.Number(label="Limit", minimum=1, maximum=100, step=1, value=100)
26
- search_civitai_page = gr.Number(label="Page", info="If 0, retrieve all pages", minimum=0, maximum=10, step=1, value=1)
27
- with gr.Row(equal_height=True):
28
- search_civitai_query = gr.Textbox(label="Query", placeholder="flux", lines=1)
29
- search_civitai_tag = gr.Dropdown(label="Tag", choices=get_civitai_tag(), value=get_civitai_tag()[0], allow_custom_value=True)
30
- search_civitai_user = gr.Textbox(label="Username", lines=1)
31
- search_civitai_submit = gr.Button("Search on Civitai")
32
- with gr.Accordion("Results", open=True):
33
- with gr.Row():
34
- search_civitai_desc = gr.Markdown(value="", visible=False, elem_classes="desc")
35
- search_civitai_json = gr.JSON(value={}, visible=False)
36
- with gr.Row(equal_height=True):
37
- with gr.Column(scale=9):
38
- with gr.Accordion("Select from Gallery", open=False):
39
- search_civitai_gallery = gr.Gallery([], label="Results", allow_preview=False, columns=5, elem_id="gallery", show_share_button=False, interactive=False)
40
- with gr.Accordion("Select by Checkbox", open=False):
41
- search_civitai_result_checkbox = gr.CheckboxGroup(label="", choices=[], value=[])
42
- search_civitai_result = gr.Dropdown(label="Search Results", choices=[("", "")], value=[],
43
- allow_custom_value=True, visible=True, multiselect=True)
44
- search_civitai_result_info = gr.Markdown("Search result.", elem_classes="info")
45
- with gr.Column(scale=1):
46
- search_civitai_add = gr.Button("Add to download URLs")
47
- search_civitai_select_all = gr.Button("Select All", variant="secondary", size="sm")
48
- with gr.Group():
49
- dl_url = gr.Textbox(label="Download URL(s)", placeholder="https://civitai.com/api/download/models/28907\n...", value="", lines=3, max_lines=255)
50
- with gr.Column():
51
- civitai_key = gr.Textbox(label="Your Civitai Key", value="", max_lines=1)
52
- gr.Markdown("Your Civitai API key is available at [https://civitai.com/user/account](https://civitai.com/user/account).", elem_classes="info")
53
- with gr.Group():
54
- with gr.Row():
55
- with gr.Column():
56
- hf_token = gr.Textbox(label="Your HF write token", placeholder="hf_...", value="", max_lines=1)
57
- gr.Markdown("Your token is available at [hf.co/settings/tokens](https://huggingface.co/settings/tokens).", elem_classes="info")
58
- newrepo_id = gr.Textbox(label="Upload repo ID", placeholder="yourid/yourrepo", value="", max_lines=1)
59
- with gr.Row():
60
- newrepo_type = gr.Radio(label="Upload repo type", choices=["model", "dataset"], value="model")
61
- is_private = gr.Checkbox(label="Create private repo", value=True)
62
- is_info = gr.Checkbox(label="Upload Civitai information files", value=False)
63
- is_rename = gr.Checkbox(label="Auto rename", value=True)
64
- run_button = gr.Button(value="Download and Upload", variant="primary")
65
- uploaded_urls = gr.CheckboxGroup(visible=False, choices=[], value=None) # hidden
66
- urls_md = gr.Markdown("<br><br>", elem_classes="result")
67
- urls_remain = gr.Textbox("Remaining URLs", value="", show_copy_button=True, visible=False)
68
- gr.DuplicateButton(value="Duplicate Space")
69
-
70
- gr.on(
71
- triggers=[run_button.click],
72
- fn=download_civitai,
73
- inputs=[dl_url, civitai_key, hf_token, uploaded_urls, newrepo_id, newrepo_type, is_private, is_info, is_rename],
74
- outputs=[uploaded_urls, urls_md, urls_remain],
75
- queue=True,
76
- )
77
- gr.on(
78
- triggers=[search_civitai_submit.click, search_civitai_query.submit, search_civitai_user.submit],
79
- fn=search_civitai,
80
- inputs=[search_civitai_query, search_civitai_type, search_civitai_basemodel, search_civitai_sort,
81
- search_civitai_period, search_civitai_tag, search_civitai_user, search_civitai_limit,
82
- search_civitai_page, search_civitai_filetype, civitai_key, search_civitai_gallery, state],
83
- outputs=[search_civitai_result, search_civitai_desc, search_civitai_submit, search_civitai_query, search_civitai_gallery,
84
- search_civitai_result_checkbox, search_civitai_result_info, state],
85
- queue=False,
86
- show_api=False,
87
- )
88
- search_civitai_result.change(select_civitai_item, [search_civitai_result, state], [search_civitai_desc, search_civitai_json, state], queue=False, show_api=False)\
89
- .success(update_civitai_checkbox, [search_civitai_result], [search_civitai_result_checkbox], queue=True, show_api=False)
90
- search_civitai_result_checkbox.select(from_civitai_checkbox, [search_civitai_result_checkbox], [search_civitai_result], queue=False, show_api=False)
91
- search_civitai_add.click(add_civitai_item, [search_civitai_result, dl_url], [dl_url], queue=False, show_api=False)
92
- search_civitai_select_all.click(select_civitai_all_item, [search_civitai_select_all, state], [search_civitai_select_all, search_civitai_result], queue=False, show_api=False)
93
- search_civitai_gallery.select(update_civitai_selection, [search_civitai_result, state], [search_civitai_result], queue=False, show_api=False)
94
-
95
- demo.queue()
96
- demo.launch(ssr_mode=False)
 
1
+ import requests
2
+ import os
3
  import gradio as gr
4
+ from huggingface_hub import update_repo_visibility, whoami, upload_folder, create_repo, upload_file, update_repo_visibility
5
+ from slugify import slugify
6
+ import gradio as gr
7
+ import re
8
+ import uuid
9
+ from typing import Optional
10
+ import json
11
+ from bs4 import BeautifulSoup
12
+
13
+ TRUSTED_UPLOADERS = ["KappaNeuro", "CiroN2022", "multimodalart", "Norod78", "joachimsallstrom", "blink7630", "e-n-v-y", "DoctorDiffusion", "RalFinger", "artificialguybr"]
14
+
15
+ def get_json_data(url):
16
+ url_split = url.split('/')
17
+ api_url = f"https://civitai.com/api/v1/models/{url_split[4]}"
18
+ try:
19
+ response = requests.get(api_url)
20
+ response.raise_for_status()
21
+ return response.json()
22
+ except requests.exceptions.RequestException as e:
23
+ print(f"Error fetching JSON data: {e}")
24
+ return None
25
+
26
+ def check_nsfw(json_data, profile):
27
+ if json_data["nsfw"]:
28
+ return False
29
+ print(profile)
30
+ if(profile.username in TRUSTED_UPLOADERS):
31
+ return True
32
+ for model_version in json_data["modelVersions"]:
33
+ for image in model_version["images"]:
34
+ if image["nsfwLevel"] > 5:
35
+ return False
36
+ return True
37
+
38
+ def get_prompts_from_image(image_id):
39
+ print("image_id: ", image_id)
40
+ url = f'https://civitai.com/api/trpc/image.getGenerationData?input={{"json":{{"id":{image_id}}}}}'
41
+ print(url)
42
+ response = requests.get(url)
43
+ print(response)
44
+ prompt = ""
45
+ negative_prompt = ""
46
+ if response.status_code == 200:
47
+ data = response.json()
48
+ result = data['result']['data']['json']
49
+ if result['meta'] is not None and "prompt" in result['meta']:
50
+ prompt = result['meta']['prompt']
51
+ if result['meta'] is not None and "negativePrompt" in result['meta']:
52
+ negative_prompt = result["meta"]["negativePrompt"]
53
+
54
+ return prompt, negative_prompt
55
+
56
+ def extract_info(json_data):
57
+ if json_data["type"] == "LORA":
58
+ for model_version in json_data["modelVersions"]:
59
+ if model_version["baseModel"] in ["SDXL 1.0", "SDXL 0.9", "SD 1.5", "SD 1.4", "SD 2.1", "SD 2.0", "SD 2.0 768", "SD 2.1 768", "SD 3", "Flux.1 D", "Flux.1 S"]:
60
+ for file in model_version["files"]:
61
+ print(file)
62
+ if "primary" in file:
63
+ # Start by adding the primary file to the list
64
+ urls_to_download = [{"url": file["downloadUrl"], "filename": file["name"], "type": "weightName"}]
65
+
66
+ # Then append all image URLs to the list
67
+ for image in model_version["images"]:
68
+ image_id = image["url"].split("/")[-1].split(".")[0]
69
+ prompt, negative_prompt = get_prompts_from_image(image_id)
70
+ if image["nsfwLevel"] > 5:
71
+ pass #ugly before checking the actual logic
72
+ else:
73
+ urls_to_download.append({
74
+ "url": image["url"],
75
+ "filename": os.path.basename(image["url"]),
76
+ "type": "imageName",
77
+ "prompt": prompt, #if "meta" in image and "prompt" in image["meta"] else ""
78
+ "negative_prompt": negative_prompt
79
+ })
80
+ model_mapping = {
81
+ "SDXL 1.0": "stabilityai/stable-diffusion-xl-base-1.0",
82
+ "SDXL 0.9": "stabilityai/stable-diffusion-xl-base-1.0",
83
+ "SD 1.5": "runwayml/stable-diffusion-v1-5",
84
+ "SD 1.4": "CompVis/stable-diffusion-v1-4",
85
+ "SD 2.1": "stabilityai/stable-diffusion-2-1-base",
86
+ "SD 2.0": "stabilityai/stable-diffusion-2-base",
87
+ "SD 2.1 768": "stabilityai/stable-diffusion-2-1",
88
+ "SD 2.0 768": "stabilityai/stable-diffusion-2",
89
+ "SD 3": "stabilityai/stable-diffusion-3-medium-diffusers",
90
+ "Flux.1 D": "black-forest-labs/FLUX.1-dev",
91
+ "Flux.1 S": "black-forest-labs/FLUX.1-schnell"
92
+ }
93
+ base_model = model_mapping[model_version["baseModel"]]
94
+ info = {
95
+ "urls_to_download": urls_to_download,
96
+ "id": model_version["id"],
97
+ "baseModel": base_model,
98
+ "modelId": model_version.get("modelId", ""),
99
+ "name": json_data["name"],
100
+ "description": json_data["description"],
101
+ "trainedWords": model_version["trainedWords"] if "trainedWords" in model_version else [],
102
+ "creator": json_data["creator"]["username"],
103
+ "tags": json_data["tags"],
104
+ "allowNoCredit": json_data["allowNoCredit"],
105
+ "allowCommercialUse": json_data["allowCommercialUse"],
106
+ "allowDerivatives": json_data["allowDerivatives"],
107
+ "allowDifferentLicense": json_data["allowDifferentLicense"]
108
+ }
109
+ return info
110
+ return None
111
+
112
+ def download_files(info, folder="."):
113
+ downloaded_files = {
114
+ "imageName": [],
115
+ "imagePrompt": [],
116
+ "imageNegativePrompt": [],
117
+ "weightName": []
118
+ }
119
+ for item in info["urls_to_download"]:
120
+ download_file(item["url"], item["filename"], folder)
121
+ downloaded_files[item["type"]].append(item["filename"])
122
+ if(item["type"] == "imageName"):
123
+ prompt_clean = re.sub(r'<.*?>', '', item["prompt"])
124
+ negative_prompt_clean = re.sub(r'<.*?>', '', item["negative_prompt"])
125
+ downloaded_files["imagePrompt"].append(prompt_clean)
126
+ downloaded_files["imageNegativePrompt"].append(negative_prompt_clean)
127
+ return downloaded_files
128
+
129
+ def download_file(url, filename, folder="."):
130
+ headers = {}
131
+ try:
132
+ response = requests.get(url, headers=headers)
133
+ response.raise_for_status()
134
+ except requests.exceptions.HTTPError as e:
135
+ print(e)
136
+ if response.status_code == 401:
137
+ headers['Authorization'] = f'Bearer {os.environ["CIVITAI_API"]}'
138
+ try:
139
+ response = requests.get(url, headers=headers)
140
+ response.raise_for_status()
141
+ except requests.exceptions.RequestException as e:
142
+ raise gr.Error(f"Error downloading file: {e}")
143
+ else:
144
+ raise gr.Error(f"Error downloading file: {e}")
145
+ except requests.exceptions.RequestException as e:
146
+ raise gr.Error(f"Error downloading file: {e}")
147
+
148
+ with open(f"{folder}/{filename}", 'wb') as f:
149
+ f.write(response.content)
150
+
151
+ def process_url(url, profile, do_download=True, folder="."):
152
+ json_data = get_json_data(url)
153
+ if json_data:
154
+ if check_nsfw(json_data, profile):
155
+ info = extract_info(json_data)
156
+ if info:
157
+ if(do_download):
158
+ downloaded_files = download_files(info, folder)
159
+ else:
160
+ downloaded_files = []
161
+ return info, downloaded_files
162
+ else:
163
+ raise gr.Error("Only SDXL LoRAs are supported for now")
164
+ else:
165
+ raise gr.Error("This model has content tagged as unsafe by CivitAI")
166
+ else:
167
+ raise gr.Error("Something went wrong in fetching CivitAI API")
168
+
169
+ def create_readme(info, downloaded_files, user_repo_id, link_civit=False, is_author=True, folder="."):
170
+ readme_content = ""
171
+ original_url = f"https://civitai.com/models/{info['modelId']}"
172
+ link_civit_disclaimer = f'([CivitAI]({original_url}))'
173
+ non_author_disclaimer = f'This model was originally uploaded on [CivitAI]({original_url}), by [{info["creator"]}](https://civitai.com/user/{info["creator"]}/models). The information below was provided by the author on CivitAI:'
174
+ default_tags = ["text-to-image", "stable-diffusion", "lora", "diffusers", "template:sd-lora", "migrated"]
175
+ civit_tags = [t.replace(":", "") for t in info["tags"] if t not in default_tags]
176
+ tags = default_tags + civit_tags
177
+ unpacked_tags = "\n- ".join(tags)
178
+
179
+ trained_words = info['trainedWords'] if 'trainedWords' in info and info['trainedWords'] else []
180
+ formatted_words = ', '.join(f'`{word}`' for word in trained_words)
181
+ if formatted_words:
182
+ trigger_words_section = f"""## Trigger words
183
+ You should use {formatted_words} to trigger the image generation.
184
+ """
185
+ else:
186
+ trigger_words_section = ""
187
+
188
+ widget_content = ""
189
+ for index, (prompt, negative_prompt, image) in enumerate(zip(downloaded_files["imagePrompt"], downloaded_files["imageNegativePrompt"], downloaded_files["imageName"])):
190
+ escaped_prompt = prompt.replace("'", "''")
191
+ negative_prompt_content = f"""parameters:
192
+ negative_prompt: {negative_prompt}
193
+ """ if negative_prompt else ""
194
+ widget_content += f"""- text: '{escaped_prompt if escaped_prompt else ' ' }'
195
+ {negative_prompt_content}
196
+ output:
197
+ url: >-
198
+ {image}
199
  """
200
+ dtype = "torch.bfloat16" if info["baseModel"] == "black-forest-labs/FLUX.1-dev" or info["baseModel"] == "black-forest-labs/FLUX.1-schnell" else "torch.float16"
201
+
202
+ content = f"""---
203
+ license: other
204
+ license_name: bespoke-lora-trained-license
205
+ license_link: https://multimodal.art/civitai-licenses?allowNoCredit={info["allowNoCredit"]}&allowCommercialUse={info["allowCommercialUse"][0] if info["allowCommercialUse"] else 1}&allowDerivatives={info["allowDerivatives"]}&allowDifferentLicense={info["allowDifferentLicense"]}
206
+ tags:
207
+ - {unpacked_tags}
208
+ base_model: {info["baseModel"]}
209
+ instance_prompt: {info['trainedWords'][0] if 'trainedWords' in info and len(info['trainedWords']) > 0 else ''}
210
+ widget:
211
+ {widget_content}
212
+ ---
213
+ # {info["name"]}
214
+ <Gallery />
215
+ {non_author_disclaimer if not is_author else ''}
216
+ {link_civit_disclaimer if link_civit else ''}
217
+ ## Model description
218
+ {info["description"]}
219
+ {trigger_words_section}
220
+ ## Download model
221
+ Weights for this model are available in Safetensors format.
222
+ [Download](/{user_repo_id}/tree/main) them in the Files & versions tab.
223
+ ## Use it with the [🧨 diffusers library](https://github.com/huggingface/diffusers)
224
+ ```py
225
+ from diffusers import AutoPipelineForText2Image
226
+ import torch
227
+ device = "cuda" if torch.cuda.is_available() else "cpu"
228
+ pipeline = AutoPipelineForText2Image.from_pretrained('{info["baseModel"]}', torch_dtype={dtype}).to(device)
229
+ pipeline.load_lora_weights('{user_repo_id}', weight_name='{downloaded_files["weightName"][0]}')
230
+ image = pipeline('{prompt if prompt else (formatted_words if formatted_words else 'Your custom prompt')}').images[0]
231
+ ```
232
+ For more details, including weighting, merging and fusing LoRAs, check the [documentation on loading LoRAs in diffusers](https://huggingface.co/docs/diffusers/main/en/using-diffusers/loading_adapters)
233
+ """
234
+ #for index, (image, prompt) in enumerate(zip(downloaded_files["imageName"], downloaded_files["imagePrompt"])):
235
+ # if index == 1:
236
+ # content += f"## Image examples for the model:\n![Image {index}]({image})\n> {prompt}\n"
237
+ # elif index > 1:
238
+ # content += f"\n![Image {index}]({image})\n> {prompt}\n"
239
+ readme_content += content + "\n"
240
+ with open(f"{folder}/README.md", "w") as file:
241
+ file.write(readme_content)
242
+
243
+ def get_creator(username):
244
+ url = f"https://civitai.com/api/trpc/user.getCreator?input=%7B%22json%22%3A%7B%22username%22%3A%22{username}%22%2C%22authed%22%3Atrue%7D%7D"
245
+ headers = {
246
+ "authority": "civitai.com",
247
+ "accept": "*/*",
248
+ "accept-language": "en-BR,en;q=0.9,pt-BR;q=0.8,pt;q=0.7,es-ES;q=0.6,es;q=0.5,de-LI;q=0.4,de;q=0.3,en-GB;q=0.2,en-US;q=0.1,sk;q=0.1",
249
+ "content-type": "application/json",
250
+ "cookie": f'{os.environ["COOKIE_INFO"]}',
251
+ "if-modified-since": "Tue, 22 Aug 2023 07:18:52 GMT",
252
+ "referer": f"https://civitai.com/user/{username}/models",
253
+ "sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
254
+ "sec-ch-ua-mobile": "?0",
255
+ "sec-ch-ua-platform": "macOS",
256
+ "sec-fetch-dest": "empty",
257
+ "sec-fetch-mode": "cors",
258
+ "sec-fetch-site": "same-origin",
259
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
260
+ }
261
+ response = requests.get(url, headers=headers)
262
+
263
+ return response.json()
264
+
265
+ def extract_huggingface_username(username):
266
+ data = get_creator(username)
267
+ links = data.get('result', {}).get('data', {}).get('json', {}).get('links', [])
268
+ for link in links:
269
+ url = link.get('url', '')
270
+ if url.startswith('https://huggingface.co/') or url.startswith('https://www.huggingface.co/'):
271
+ username = url.split('/')[-1]
272
+ return username
273
+
274
+ return None
275
+
276
+
277
+ def check_civit_link(profile: Optional[gr.OAuthProfile], url):
278
+ info, _ = process_url(url, profile, do_download=False)
279
+ hf_username = extract_huggingface_username(info['creator'])
280
+ attributes_methods = dir(profile)
281
+
282
+ if(profile.username == "multimodalart"):
283
+ return '', gr.update(interactive=True), gr.update(visible=False), gr.update(visible=True)
284
+
285
+ if(not hf_username):
286
+ no_username_text = f'If you are {info["creator"]} on CivitAI, hi! Your CivitAI profile seems to not have information about your Hugging Face account. Please visit <a href="https://civitai.com/user/account" target="_blank">https://civitai.com/user/account</a> and include your 🤗 username there, here\'s mine:<br><img width="60%" src="https://i.imgur.com/hCbo9uL.png" /><br>(if you are not {info["creator"]}, you cannot submit their model at this time)'
287
+ return no_username_text, gr.update(interactive=False), gr.update(visible=True), gr.update(visible=False)
288
+ if(profile.username != hf_username):
289
+ unmatched_username_text = '<h4>Oops, the Hugging Face account in your CivitAI profile seems to be different than the one your are using here. Please visit <a href="https://civitai.com/user/account">https://civitai.com/user/account</a> and update it there to match your Hugging Face account<br><img src="https://i.imgur.com/hCbo9uL.png" /></h4>'
290
+ return unmatched_username_text, gr.update(interactive=False), gr.update(visible=True), gr.update(visible=False)
291
+ else:
292
+ return '', gr.update(interactive=True), gr.update(visible=False), gr.update(visible=True)
293
+
294
+ def swap_fill(profile: Optional[gr.OAuthProfile]):
295
+ if profile is None:
296
+ return gr.update(visible=True), gr.update(visible=False)
297
+ else:
298
+ return gr.update(visible=False), gr.update(visible=True)
299
+
300
+ def show_output():
301
+ return gr.update(visible=True)
302
+
303
+ def list_civit_models(username):
304
+ url = f"https://civitai.com/api/v1/models?username={username}&limit=100"
305
+ json_models_list = []
306
+
307
+ while url:
308
+ response = requests.get(url)
309
+ data = response.json()
310
+
311
+ # Add current page items to the list
312
+ json_models_list.extend(data.get('items', []))
313
+
314
+ # Check if there is a nextPage URL in the metadata
315
+ metadata = data.get('metadata', {})
316
+ url = metadata.get('nextPage', None)
317
+ urls = ""
318
+ for model in json_models_list:
319
+ urls += f'https://civitai.com/models/{model["id"]}/{slugify(model["name"])}\n'
320
+
321
+ return urls
322
+
323
+ def upload_civit_to_hf(profile: Optional[gr.OAuthProfile], oauth_token: gr.OAuthToken, url, link_civit=False):
324
+ if not profile.name:
325
+ return gr.Error("Are you sure you are logged in?")
326
+
327
+ folder = str(uuid.uuid4())
328
+ os.makedirs(folder, exist_ok=False)
329
+ gr.Info(f"Starting download of model {url}")
330
+ info, downloaded_files = process_url(url, profile, folder=folder)
331
+ username = {profile.username}
332
+ slug_name = slugify(info["name"])
333
+ user_repo_id = f"{profile.username}/{slug_name}"
334
+ create_readme(info, downloaded_files, user_repo_id, link_civit, folder=folder)
335
+ try:
336
+ create_repo(repo_id=user_repo_id, private=True, exist_ok=True, token=oauth_token.token)
337
+ gr.Info(f"Starting to upload repo {user_repo_id} to Hugging Face...")
338
+ upload_folder(
339
+ folder_path=folder,
340
+ repo_id=user_repo_id,
341
+ repo_type="model",
342
+ token=oauth_token.token
343
+ )
344
+ update_repo_visibility(repo_id=user_repo_id, private=False, token=oauth_token.token)
345
+ gr.Info(f"Model uploaded!")
346
+ except Exception as e:
347
+ print(e)
348
+ raise gr.Error("Your Hugging Face Token expired. Log out and in again to upload your models.")
349
+
350
+ return f'''# Model uploaded to 🤗!
351
+ ## Access it here [{user_repo_id}](https://huggingface.co/{user_repo_id}) '''
352
+
353
+ def bulk_upload(profile: Optional[gr.OAuthProfile], oauth_token: gr.OAuthToken, urls, link_civit=False):
354
+ urls = urls.split("\n")
355
+ print(urls)
356
+ upload_results = ""
357
+ for url in urls:
358
+ if(url):
359
+ try:
360
+ upload_result = upload_civit_to_hf(profile, oauth_token, url, link_civit)
361
+ upload_results += upload_result+"\n"
362
+ except Exception as e:
363
+ gr.Warning(f"Error uploading the model {url}")
364
+ return upload_results
365
+
366
+ css = '''
367
+ #login {
368
+ width: 100% !important;
369
+ margin: 0 auto;
370
+ }
371
+ #disabled_upload{
372
+ opacity: 0.5;
373
+ pointer-events:none;
374
+ }
375
+ '''
376
+
377
+ with gr.Blocks(css=css) as demo:
378
+ gr.Markdown('''# Upload your CivitAI LoRA to Hugging Face 🤗
379
+ By uploading your LoRAs to Hugging Face you get diffusers compatibility, a free GPU-based Inference Widget, you'll be listed in [LoRA Studio](https://lorastudio.co/models) after a short review, and get the possibility to submit your model to the [LoRA the Explorer](https://huggingface.co/spaces/multimodalart/LoraTheExplorer) ✨
380
+ ''')
381
+ gr.LoginButton(elem_id="login")
382
+ with gr.Column(elem_id="disabled_upload") as disabled_area:
383
+ with gr.Row():
384
+ submit_source_civit = gr.Textbox(
385
+ placeholder="https://civitai.com/models/144684/pixelartredmond-pixel-art-loras-for-sd-xl",
386
+ label="CivitAI model URL",
387
+ info="URL of the CivitAI LoRA",
388
+ )
389
+ submit_button_civit = gr.Button("Upload model to Hugging Face and submit", interactive=False)
390
+ with gr.Column(visible=False) as enabled_area:
391
+ with gr.Column():
392
+ submit_source_civit = gr.Textbox(
393
+ placeholder="https://civitai.com/models/144684/pixelartredmond-pixel-art-loras-for-sd-xl",
394
+ label="CivitAI model URL",
395
+ info="URL of the CivitAI LoRA",
396
+
397
+ )
398
+ with gr.Accordion("Bulk upload (bring in multiple LoRAs)", open=False):
399
+ civit_username_to_bulk = gr.Textbox(label="CivitAI username (optional)", info="Type your CivitAI username here to automagically fill the bulk models URLs list below (optional, you can paste links down here directly)")
400
+ submit_bulk_civit = gr.Textbox(
401
+ label="CivitAI bulk models URLs",
402
+ info="Add one URL per line",
403
+ lines=6,
404
+ )
405
+ link_civit = gr.Checkbox(label="Link back to CivitAI?", value=False)
406
+ bulk_button = gr.Button("Bulk upload")
407
+
408
+ instructions = gr.HTML("")
409
+ try_again_button = gr.Button("I have added my HF profile to my account (it may take 1 minute to refresh)", visible=False)
410
+ submit_button_civit = gr.Button("Upload model to Hugging Face", interactive=False)
411
+ output = gr.Markdown(label="Output progress", visible=False)
412
 
413
+ demo.load(fn=swap_fill, outputs=[disabled_area, enabled_area], queue=False)
414
+
415
+ submit_source_civit.change(fn=check_civit_link, inputs=[submit_source_civit], outputs=[instructions, submit_button_civit, try_again_button, submit_button_civit])
416
+ civit_username_to_bulk.change(fn=list_civit_models, inputs=[civit_username_to_bulk], outputs=[submit_bulk_civit])
417
+ try_again_button.click(fn=check_civit_link, inputs=[submit_source_civit], outputs=[instructions, submit_button_civit, try_again_button, submit_button_civit])
418
+
419
+ submit_button_civit.click(fn=show_output, inputs=[], outputs=[output]).then(fn=upload_civit_to_hf, inputs=[submit_source_civit, link_civit], outputs=[output])
420
+ bulk_button.click(fn=show_output, inputs=[], outputs=[output]).then(fn=bulk_upload, inputs=[submit_bulk_civit, link_civit], outputs=[output])
421
+ #gr.LogoutButton(elem_id="logout")
422
+
423
+ demo.queue(default_concurrency_limit=50)
424
+ demo.launch()