coding-alt/IF
1
1#!/usr/bin/env python2 3import datetime4import hashlib5import json6import os7import random8import tempfile9import shortuuid10from apscheduler.schedulers.background import BackgroundScheduler11import shutil12 13import gradio as gr14import torch15from huggingface_hub import HfApi16from share_btn import community_icon_html, loading_icon_html, share_js17 18# isort: off19from model import Model20from settings import (21 DEBUG,22 DEFAULT_CUSTOM_TIMESTEPS_1,23 DEFAULT_CUSTOM_TIMESTEPS_2,24 DEFAULT_NUM_IMAGES,25 DEFAULT_NUM_STEPS_3,26 DISABLE_SD_X4_UPSCALER,27 GALLERY_COLUMN_NUM,28 HF_TOKEN,29 MAX_NUM_IMAGES,30 MAX_NUM_STEPS,31 MAX_QUEUE_SIZE,32 MAX_SEED,33 SHOW_ADVANCED_OPTIONS,34 SHOW_CUSTOM_TIMESTEPS_1,35 SHOW_CUSTOM_TIMESTEPS_2,36 SHOW_DEVICE_WARNING,37 SHOW_DUPLICATE_BUTTON,38 SHOW_NUM_IMAGES,39 SHOW_NUM_STEPS_1,40 SHOW_NUM_STEPS_2,41 SHOW_NUM_STEPS_3,42 SHOW_UPSCALE_TO_256_BUTTON,43 UPLOAD_REPO_ID,44 UPLOAD_RESULT_IMAGE,45)46# isort: on47 48TITLE = '# [DeepFloyd IF](https://github.com/deep-floyd/IF)'49DESCRIPTION = 'The DeepFloyd IF model has been initially released as a non-commercial research-only model. Please make sure you read and abide to the [LICENSE](https://huggingface.co/spaces/DeepFloyd/deepfloyd-if-license) before using it.'50DISCLAIMER = 'In this demo, the DeepFloyd team may collect prompts, and user preferences (which of the images the user chose to upscale) for improving future models'51FOOTER = """<div class="footer">52 <p>Model by <a href="https://huggingface.co/DeepFloyd" style="text-decoration: underline;" target="_blank">DeepFloyd</a> supported by <a href="https://huggingface.co/stabilityai" style="text-decoration: underline;" target="_blank">Stability AI</a>53 </p>54 </div>55 <div class="acknowledgments">56 <p><h4>LICENSE</h4>57The model is licensed with a bespoke non-commercial research-only license <a href="https://huggingface.co/spaces/DeepFloyd/deepfloyd-if-license" style="text-decoration: underline;" target="_blank">DeepFloyd IF Research License Agreement</a> license. The license forbids you from sharing any content for commercial use, or that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, spread misinformation and target vulnerable groups. For the full list of restrictions please <a href="https://huggingface.co/spaces/DeepFloyd/deepfloyd-if-license" style="text-decoration: underline;" target="_blank">read the license</a></p>58 <p><h4>Biases and content acknowledgment</h4>59Despite how impressive being able to turn text into image is, beware to the fact that this model may output content that reinforces or exacerbates societal biases, as well as realistic faces, explicit content and violence. The model was trained on a subset of the <a href="https://laion.ai/blog/laion-5b/" style="text-decoration: underline;" target="_blank">LAION-5B dataset</a> and is meant for research purposes. You can read more in the <a href="https://huggingface.co/DeepFloyd/IF-I-IF-v1.0" style="text-decoration: underline;" target="_blank">model card</a></p>60 </div>61 """62if SHOW_DUPLICATE_BUTTON:63 SPACE_ID = os.getenv('SPACE_ID')64 DESCRIPTION += f'\n<p><a href="https://huggingface.co/spaces/{SPACE_ID}?duplicate=true"><img src="https://img.shields.io/badge/-Duplicate%20Space%20to%20skip%20the%20queue-blue?labelColor=white&style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAP5JREFUOE+lk7FqAkEURY+ltunEgFXS2sZGIbXfEPdLlnxJyDdYB62sbbUKpLbVNhyYFzbrrA74YJlh9r079973psed0cvUD4A+4HoCjsA85X0Dfn/RBLBgBDxnQPfAEJgBY+A9gALA4tcbamSzS4xq4FOQAJgCDwV2CPKV8tZAJcAjMMkUe1vX+U+SMhfAJEHasQIWmXNN3abzDwHUrgcRGmYcgKe0bxrblHEB4E/pndMazNpSZGcsZdBlYJcEL9Afo75molJyM2FxmPgmgPqlWNLGfwZGG6UiyEvLzHYDmoPkDDiNm9JR9uboiONcBXrpY1qmgs21x1QwyZcpvxt9NS09PlsPAAAAAElFTkSuQmCC&logoWidth=14" alt="Duplicate Space"></a></p>'65 66if SHOW_DEVICE_WARNING and not torch.cuda.is_available():67 DESCRIPTION += '\n<p>Running on CPU 🥶 This demo does not work on CPU.</p>'68 69model = Model()70 71 72def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:73 if randomize_seed:74 seed = random.randint(0, MAX_SEED)75 return seed76 77 78def get_stage2_index(evt: gr.SelectData) -> int:79 return evt.index80 81 82def check_if_stage2_selected(index: int) -> None:83 if index == -1:84 raise gr.Error(85 'You need to select the image you would like to upscale from the Stage 1 results by clicking.'86 )87 88 89hf_api = HfApi(token=HF_TOKEN)90if UPLOAD_REPO_ID:91 hf_api.create_repo(repo_id=UPLOAD_REPO_ID,92 private=True,93 repo_type='dataset',94 exist_ok=True)95 96 97def get_param_file_hash_name(param_filepath: str) -> str:98 if not UPLOAD_REPO_ID:99 return ''100 with open(param_filepath, 'rb') as f:101 md5 = hashlib.md5(f.read()).hexdigest()102 utcnow = datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S-%f')103 return f'{utcnow}-{md5}'104 105 106def upload_stage1_result(stage1_param_path: str, stage1_result_path: str,107 save_name: str) -> None:108 if not UPLOAD_REPO_ID:109 return110 try:111 folder_params = "tmp/results/stage1_params"112 folder_results = "tmp/results/stage1_results"113 114 path_params = f"{folder_params}/{save_name}.json"115 path_results = f"{folder_results}/{save_name}.pth"116 117 os.makedirs(folder_params, exist_ok=True)118 os.makedirs(folder_results, exist_ok=True)119 120 shutil.copy(stage1_param_path, path_params)121 shutil.copy(stage1_result_path, path_results)122 123 except Exception as e:124 print(e)125 126 127def upload_stage2_info(stage1_param_file_hash_name: str,128 stage2_output_path: str,129 selected_index_for_upscale: int, seed_2: int,130 guidance_scale_2: float, custom_timesteps_2: str,131 num_inference_steps_2: int) -> None:132 if not UPLOAD_REPO_ID:133 return134 if not stage1_param_file_hash_name:135 raise ValueError136 137 stage2_params = {138 'stage1_param_file_hash_name': stage1_param_file_hash_name,139 'selected_index_for_upscale': selected_index_for_upscale,140 'seed_2': seed_2,141 'guidance_scale_2': guidance_scale_2,142 'custom_timesteps_2': custom_timesteps_2,143 'num_inference_steps_2': num_inference_steps_2,144 }145 with tempfile.NamedTemporaryFile(mode='w', delete=False) as param_file:146 param_file.write(json.dumps(stage2_params))147 stage2_param_file_hash_name = get_param_file_hash_name(param_file.name)148 save_name = f'{stage1_param_file_hash_name}_{stage2_param_file_hash_name}'149 150 try:151 folder_params = "tmp/results/stage2_params"152 153 os.makedirs(folder_params, exist_ok=True)154 path_params = f"{folder_params}/{save_name}.json"155 shutil.copy(param_file.name, path_params)156 157 if UPLOAD_RESULT_IMAGE:158 folder_results = "tmp/results/stage2_results"159 os.makedirs(folder_results, exist_ok=True)160 path_results = f"{folder_results}/{save_name}.png"161 shutil.copy(stage2_output_path, path_results)162 163 except Exception as e:164 print(e)165 166 167def upload_stage2_3_info(stage1_param_file_hash_name: str,168 stage2_3_output_path: str,169 selected_index_for_upscale: int, seed_2: int,170 guidance_scale_2: float, custom_timesteps_2: str,171 num_inference_steps_2: int, prompt: str,172 negative_prompt: str, seed_3: int,173 guidance_scale_3: float,174 num_inference_steps_3: int) -> None:175 if not UPLOAD_REPO_ID:176 return177 if not stage1_param_file_hash_name:178 raise ValueError179 180 stage2_3_params = {181 'stage1_param_file_hash_name': stage1_param_file_hash_name,182 'selected_index_for_upscale': selected_index_for_upscale,183 'seed_2': seed_2,184 'guidance_scale_2': guidance_scale_2,185 'custom_timesteps_2': custom_timesteps_2,186 'num_inference_steps_2': num_inference_steps_2,187 'prompt': prompt,188 'negative_prompt': negative_prompt,189 'seed_3': seed_3,190 'guidance_scale_3': guidance_scale_3,191 'num_inference_steps_3': num_inference_steps_3,192 }193 with tempfile.NamedTemporaryFile(mode='w', delete=False) as param_file:194 param_file.write(json.dumps(stage2_3_params))195 stage2_3_param_file_hash_name = get_param_file_hash_name(param_file.name)196 save_name = f'{stage1_param_file_hash_name}_{stage2_3_param_file_hash_name}'197 198 try:199 folder_params = "tmp/results/stage2_3_params"200 os.makedirs(folder_params, exist_ok=True)201 path_params = f"{folder_params}/{save_name}.json"202 shutil.copy(param_file.name, path_params)203 204 if UPLOAD_RESULT_IMAGE:205 folder_results = "tmp/results/stage2_3_results"206 os.makedirs(folder_results, exist_ok=True)207 path_results = f"{folder_results}/{save_name}.png"208 shutil.copy(stage2_3_output_path, path_results)209 except Exception as e:210 print(e)211 212 213def update_upscale_button(selected_index: int) -> tuple[dict, dict]:214 if selected_index == -1:215 return gr.update(interactive=False), gr.update(interactive=False)216 else:217 return gr.update(interactive=True), gr.update(interactive=True)218 219 220def _update_result_view(show_gallery: bool) -> tuple[dict, dict]:221 return gr.update(visible=show_gallery), gr.update(visible=not show_gallery)222 223 224def show_gallery_view() -> tuple[dict, dict]:225 return _update_result_view(True)226 227 228def show_upscaled_view() -> tuple[dict, dict]:229 return _update_result_view(False)230 231def upload_files():232 """Zips files and uploads to dataset. Local data is deleted233 """234 if os.path.exists("tmp/results") and os.path.isdir("tmp/results"):235 try:236 random_folder = random.randint(0,1000)237 shutil.make_archive("tmp/results", 'zip', "tmp/results")238 hf_api.upload_file(239 path_or_fileobj="tmp/results.zip",240 path_in_repo=f"{random_folder}/results_{shortuuid.uuid()}.zip",241 repo_id=UPLOAD_REPO_ID,242 repo_type="dataset",243 )244 shutil.rmtree("tmp/results")245 except Exception as e:246 print(e)247 248examples = [249 'high quality dslr photo, a photo product of a lemon inspired by natural and organic materials, wooden accents, intricately decorated with glowing vines of led lights, inspired by baroque luxury',250 'paper quilling, extremely detailed, paper quilling of a nordic mountain landscape, 8k rendering',251 'letters made of candy on a plate that says "diet"',252 'a photo of a violet baseball cap with yellow text: "deep floyd". 50mm lens, photo realism, cine lens. violet baseball cap says "deep floyd". reflections, render. yellow stitch text "deep floyd"',253 'ultra close-up color photo portrait of rainbow owl with deer horns in the woods',254 'a cloth embroidered with the text "laion" and an embroidered cute baby lion face',255 'product image of a crochet Cthulhu the great old one emerging from a spacetime wormhole made of wool',256 'a little green budgie parrot driving small red toy car in new york street, photo',257 'origami dancer in white paper, 3d render, ultra-detailed, on white background, studio shot.',258 'glowing mushrooms in a natural environment with smoke in the frame',259 'a subway train\'s digital sign saying "open source", vsco preset, 35mm photo, film grain, in a dim subway station',260 'a bowl full of few adorable golden doodle puppies, the doodles dusted in powdered sugar and look delicious, bokeh, cannon. professional macro photo, super detailed. cute sweet golden doodle confectionery, baking puppies in powdered sugar in the bowl',261 'a face of a woman made completely out of foliage, twigs, leaves and flowers, side view'262]263 264with gr.Blocks(css='style.css') as demo:265 gr.Markdown(TITLE)266 gr.Markdown(DESCRIPTION)267 with gr.Box():268 with gr.Row(elem_id='prompt-container').style(equal_height=True):269 with gr.Column():270 prompt = gr.Text(271 label='Prompt',272 show_label=False,273 max_lines=1,274 placeholder='Enter your prompt',275 elem_id='prompt-text-input',276 ).style(container=False)277 negative_prompt = gr.Text(278 label='Negative prompt',279 show_label=False,280 max_lines=1,281 placeholder='Enter a negative prompt',282 elem_id='negative-prompt-text-input',283 ).style(container=False)284 generate_button = gr.Button('Generate').style(full_width=False)285 286 with gr.Column() as gallery_view:287 gallery = gr.Gallery(label='Stage 1 results',288 show_label=False,289 elem_id='gallery').style(290 columns=GALLERY_COLUMN_NUM,291 object_fit='contain')292 gr.Markdown('Pick your favorite generation to upscale.')293 with gr.Row():294 upscale_to_256_button = gr.Button(295 'Upscale to 256px',296 visible=SHOW_UPSCALE_TO_256_BUTTON297 or DISABLE_SD_X4_UPSCALER,298 interactive=False)299 upscale_button = gr.Button('Upscale',300 interactive=False,301 visible=not DISABLE_SD_X4_UPSCALER)302 with gr.Column(visible=False) as upscale_view:303 result = gr.Image(label='Result',304 show_label=False,305 type='filepath',306 interactive=False,307 elem_id='upscaled-image').style(height=640)308 back_to_selection_button = gr.Button('Back to selection')309 with gr.Group(elem_id="share-btn-container"):310 community_icon = gr.HTML(community_icon_html)311 loading_icon = gr.HTML(loading_icon_html)312 share_button = gr.Button(313 "Share to community", elem_id="share-btn")314 share_button.click(None, [], [], _js=share_js)315 with gr.Accordion('Advanced options',316 open=False,317 visible=SHOW_ADVANCED_OPTIONS):318 with gr.Tabs():319 with gr.Tab(label='Generation'):320 seed_1 = gr.Slider(label='Seed',321 minimum=0,322 maximum=MAX_SEED,323 step=1,324 value=0)325 randomize_seed_1 = gr.Checkbox(label='Randomize seed',326 value=True)327 guidance_scale_1 = gr.Slider(label='Guidance scale',328 minimum=1,329 maximum=20,330 step=0.1,331 value=7.0)332 custom_timesteps_1 = gr.Dropdown(333 label='Custom timesteps 1',334 choices=[335 'none',336 'fast27',337 'smart27',338 'smart50',339 'smart100',340 'smart185',341 ],342 value=DEFAULT_CUSTOM_TIMESTEPS_1,343 visible=SHOW_CUSTOM_TIMESTEPS_1)344 num_inference_steps_1 = gr.Slider(345 label='Number of inference steps',346 minimum=1,347 maximum=MAX_NUM_STEPS,348 step=1,349 value=100,350 visible=SHOW_NUM_STEPS_1)351 num_images = gr.Slider(label='Number of images',352 minimum=1,353 maximum=MAX_NUM_IMAGES,354 step=1,355 value=DEFAULT_NUM_IMAGES,356 visible=SHOW_NUM_IMAGES)357 with gr.Tab(label='Super-resolution 1'):358 seed_2 = gr.Slider(label='Seed',359 minimum=0,360 maximum=MAX_SEED,361 step=1,362 value=0)363 randomize_seed_2 = gr.Checkbox(label='Randomize seed',364 value=True)365 guidance_scale_2 = gr.Slider(label='Guidance scale',366 minimum=1,367 maximum=20,368 step=0.1,369 value=4.0)370 custom_timesteps_2 = gr.Dropdown(371 label='Custom timesteps 2',372 choices=[373 'none',374 'fast27',375 'smart27',376 'smart50',377 'smart100',378 'smart185',379 ],380 value=DEFAULT_CUSTOM_TIMESTEPS_2,381 visible=SHOW_CUSTOM_TIMESTEPS_2)382 num_inference_steps_2 = gr.Slider(383 label='Number of inference steps',384 minimum=1,385 maximum=MAX_NUM_STEPS,386 step=1,387 value=50,388 visible=SHOW_NUM_STEPS_2)389 with gr.Tab(label='Super-resolution 2'):390 seed_3 = gr.Slider(label='Seed',391 minimum=0,392 maximum=MAX_SEED,393 step=1,394 value=0)395 randomize_seed_3 = gr.Checkbox(label='Randomize seed',396 value=True)397 guidance_scale_3 = gr.Slider(label='Guidance scale',398 minimum=1,399 maximum=20,400 step=0.1,401 value=9.0)402 num_inference_steps_3 = gr.Slider(403 label='Number of inference steps',404 minimum=1,405 maximum=MAX_NUM_STEPS,406 step=1,407 value=DEFAULT_NUM_STEPS_3,408 visible=SHOW_NUM_STEPS_3)409 410 gr.Examples(examples=examples, inputs=prompt, examples_per_page=4)411 412 with gr.Box(visible=DEBUG):413 with gr.Row():414 with gr.Accordion(label='Hidden params'):415 stage1_param_path = gr.Text(label='Stage 1 param path')416 stage1_result_path = gr.Text(label='Stage 1 result path')417 stage1_param_file_hash_name = gr.Text(418 label='Stage 1 param file hash name')419 selected_index_for_stage2 = gr.Number(420 label='Selected index for Stage 2', value=-1, precision=0)421 gr.Markdown(DISCLAIMER)422 gr.HTML(FOOTER)423 stage1_inputs = [424 prompt,425 negative_prompt,426 seed_1,427 num_images,428 guidance_scale_1,429 custom_timesteps_1,430 num_inference_steps_1,431 ]432 stage1_outputs = [433 gallery,434 stage1_param_path,435 stage1_result_path,436 ]437 438 prompt.submit(439 fn=randomize_seed_fn,440 inputs=[seed_1, randomize_seed_1],441 outputs=seed_1,442 queue=False,443 ).then(444 fn=lambda: -1,445 outputs=selected_index_for_stage2,446 queue=False,447 ).then(448 fn=show_gallery_view,449 outputs=[450 gallery_view,451 upscale_view,452 ],453 queue=False,454 ).then(455 fn=update_upscale_button,456 inputs=selected_index_for_stage2,457 outputs=[458 upscale_button,459 upscale_to_256_button,460 ],461 queue=False,462 ).then(463 fn=model.run_stage1,464 inputs=stage1_inputs,465 outputs=stage1_outputs,466 ).success(467 fn=get_param_file_hash_name,468 inputs=stage1_param_path,469 outputs=stage1_param_file_hash_name,470 queue=False,471 ).then(472 fn=upload_stage1_result,473 inputs=[474 stage1_param_path,475 stage1_result_path,476 stage1_param_file_hash_name,477 ],478 queue=False,479 )480 481 negative_prompt.submit(482 fn=randomize_seed_fn,483 inputs=[seed_1, randomize_seed_1],484 outputs=seed_1,485 queue=False,486 ).then(487 fn=lambda: -1,488 outputs=selected_index_for_stage2,489 queue=False,490 ).then(491 fn=show_gallery_view,492 outputs=[493 gallery_view,494 upscale_view,495 ],496 queue=False,497 ).then(498 fn=update_upscale_button,499 inputs=selected_index_for_stage2,500 outputs=[501 upscale_button,502 upscale_to_256_button,503 ],504 queue=False,505 ).then(506 fn=model.run_stage1,507 inputs=stage1_inputs,508 outputs=stage1_outputs,509 ).success(510 fn=get_param_file_hash_name,511 inputs=stage1_param_path,512 outputs=stage1_param_file_hash_name,513 queue=False,514 ).then(515 fn=upload_stage1_result,516 inputs=[517 stage1_param_path,518 stage1_result_path,519 stage1_param_file_hash_name,520 ],521 queue=False,522 )523 524 generate_button.click(525 fn=randomize_seed_fn,526 inputs=[seed_1, randomize_seed_1],527 outputs=seed_1,528 queue=False,529 ).then(530 fn=lambda: -1,531 outputs=selected_index_for_stage2,532 queue=False,533 ).then(534 fn=show_gallery_view,535 outputs=[536 gallery_view,537 upscale_view,538 ],539 queue=False,540 ).then(541 fn=update_upscale_button,542 inputs=selected_index_for_stage2,543 outputs=[544 upscale_button,545 upscale_to_256_button,546 ],547 queue=False,548 ).then(549 fn=model.run_stage1,550 inputs=stage1_inputs,551 outputs=stage1_outputs,552 api_name='generate64',553 ).success(554 fn=get_param_file_hash_name,555 inputs=stage1_param_path,556 outputs=stage1_param_file_hash_name,557 queue=False,558 ).then(559 fn=upload_stage1_result,560 inputs=[561 stage1_param_path,562 stage1_result_path,563 stage1_param_file_hash_name,564 ],565 queue=False,566 )567 568 gallery.select(569 fn=get_stage2_index,570 outputs=selected_index_for_stage2,571 queue=False,572 )573 574 selected_index_for_stage2.change(575 fn=update_upscale_button,576 inputs=selected_index_for_stage2,577 outputs=[578 upscale_button,579 upscale_to_256_button,580 ],581 queue=False,582 )583 584 stage2_inputs = [585 stage1_result_path,586 selected_index_for_stage2,587 seed_2,588 guidance_scale_2,589 custom_timesteps_2,590 num_inference_steps_2,591 ]592 593 upscale_to_256_button.click(594 fn=check_if_stage2_selected,595 inputs=selected_index_for_stage2,596 queue=False,597 ).then(598 fn=randomize_seed_fn,599 inputs=[seed_2, randomize_seed_2],600 outputs=seed_2,601 queue=False,602 ).then(603 fn=show_upscaled_view,604 outputs=[605 gallery_view,606 upscale_view,607 ],608 queue=False,609 ).then(610 fn=model.run_stage2,611 inputs=stage2_inputs,612 outputs=result,613 api_name='upscale256',614 ).success(615 fn=upload_stage2_info,616 inputs=[617 stage1_param_file_hash_name,618 result,619 selected_index_for_stage2,620 seed_2,621 guidance_scale_2,622 custom_timesteps_2,623 num_inference_steps_2,624 ],625 queue=False,626 )627 628 stage2_3_inputs = [629 stage1_result_path,630 selected_index_for_stage2,631 seed_2,632 guidance_scale_2,633 custom_timesteps_2,634 num_inference_steps_2,635 prompt,636 negative_prompt,637 seed_3,638 guidance_scale_3,639 num_inference_steps_3,640 ]641 642 upscale_button.click(643 fn=check_if_stage2_selected,644 inputs=selected_index_for_stage2,645 queue=False,646 ).then(647 fn=randomize_seed_fn,648 inputs=[seed_2, randomize_seed_2],649 outputs=seed_2,650 queue=False,651 ).then(652 fn=randomize_seed_fn,653 inputs=[seed_3, randomize_seed_3],654 outputs=seed_3,655 queue=False,656 ).then(657 fn=show_upscaled_view,658 outputs=[659 gallery_view,660 upscale_view,661 ],662 queue=False,663 ).then(664 fn=model.run_stage2_3,665 inputs=stage2_3_inputs,666 outputs=result,667 api_name='upscale1024',668 ).success(669 fn=upload_stage2_3_info,670 inputs=[671 stage1_param_file_hash_name,672 result,673 selected_index_for_stage2,674 seed_2,675 guidance_scale_2,676 custom_timesteps_2,677 num_inference_steps_2,678 prompt,679 negative_prompt,680 seed_3,681 guidance_scale_3,682 num_inference_steps_3,683 ],684 queue=False,685 )686 687 back_to_selection_button.click(688 fn=show_gallery_view,689 outputs=[690 gallery_view,691 upscale_view,692 ],693 queue=False,694 )695 696 if UPLOAD_REPO_ID:697 scheduler = BackgroundScheduler()698 scheduler.add_job(func=upload_files, trigger="interval", seconds=60*20)699 scheduler.start()700 701demo.queue(api_open=False, max_size=MAX_QUEUE_SIZE).launch(debug=DEBUG)702 