Spaces:
Paused
Paused
Update app_vince.py
Browse files- app_vince.py +132 -145
app_vince.py
CHANGED
|
@@ -1,194 +1,181 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
-
VINCIE Service UI (Gradio)
|
| 4 |
-
|
| 5 |
-
-
|
| 6 |
-
-
|
| 7 |
-
-
|
| 8 |
-
-
|
| 9 |
-
- Modelo funcional de referência: ByteDance-Seed/VINCIE.
|
| 10 |
-
- Interface desenvolvida por Carlex ([email protected]).
|
| 11 |
"""
|
| 12 |
|
| 13 |
import os
|
| 14 |
from pathlib import Path
|
| 15 |
from typing import List, Tuple, Optional
|
|
|
|
| 16 |
import gradio as gr
|
| 17 |
|
| 18 |
-
#
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
#
|
| 22 |
svc = VincieService()
|
| 23 |
-
DEFAULT_NEGATIVE_PROMPT = "Worst quality, Normal quality, Low quality, Low res, Blurry, Jpeg artifacts, Grainy, text, logo, watermark, banner, extra digits, signature, subtitling, Bad anatomy, Bad proportions, Deformed, Disconnected limbs, Disfigured, Extra arms, Extra limbs, Extra hands, Fused fingers, Gross proportions, Long neck, Malformed limbs, Mutated, Mutated hands, Mutated limbs, Missing arms, Missing fingers, Poorly drawn hands, Poorly drawn face, Nsfw, Uncensored, Cleavage, Nude, Nipples, Overexposed, Plain background, Grainy, Underexposed, Deformed structures"
|
| 24 |
|
| 25 |
-
|
| 26 |
def setup_auto() -> str:
|
| 27 |
"""
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
| 30 |
"""
|
| 31 |
try:
|
| 32 |
svc.ensure_repo()
|
| 33 |
svc.ensure_model()
|
| 34 |
-
return
|
|
|
|
|
|
|
|
|
|
| 35 |
except Exception as e:
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
print(traceback.format_exc())
|
| 39 |
-
return f"A configuração encontrou um erro: {e}"
|
| 40 |
|
| 41 |
def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
|
| 42 |
"""
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
"""
|
| 45 |
img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
)
|
| 51 |
-
image_paths = [str(p) for p in images[-max_images:]]
|
| 52 |
videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime)
|
| 53 |
video_path = str(videos[-1]) if videos else None
|
| 54 |
return image_paths, video_path
|
| 55 |
|
| 56 |
-
|
| 57 |
-
def ui_multi_turn(input_image, turns_text
|
| 58 |
-
"""
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
if not turns_text or not turns_text.strip():
|
| 62 |
-
return [], None, "
|
| 63 |
|
| 64 |
turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
|
| 65 |
try:
|
| 66 |
-
out_dir = svc.multi_turn_edit(
|
| 67 |
-
input_image, turns,
|
| 68 |
-
negative_prompt=negative_prompt, seed=int(seed), steps=int(steps),
|
| 69 |
-
cfg_scale=float(cfg_scale), resolution=int(resolution), use_vae_slicing=use_vae_slicing,
|
| 70 |
-
num_gpus=int(num_gpus), batch_size=int(batch_size)
|
| 71 |
-
)
|
| 72 |
-
imgs, vid = _list_media(Path(out_dir))
|
| 73 |
-
return imgs, vid, f"Saídas salvas em: {out_dir}"
|
| 74 |
except Exception as e:
|
| 75 |
-
|
| 76 |
-
print(traceback.format_exc())
|
| 77 |
-
return [], None, f"Erro na geração: {e}"
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
try:
|
| 87 |
-
out_dir = svc.text_to_video(
|
| 88 |
-
input_image, prompt,
|
| 89 |
-
negative_prompt=negative_prompt, seed=int(seed), steps=int(steps),
|
| 90 |
-
cfg_scale=float(cfg_scale), resolution=int(resolution), fps=int(fps), use_vae_slicing=use_vae_slicing,
|
| 91 |
-
num_gpus=int(num_gpus), batch_size=int(batch_size)
|
| 92 |
-
)
|
| 93 |
-
_, vid = _list_media(Path(out_dir))
|
| 94 |
-
return vid, f"Vídeo salvo em: {out_dir}"
|
| 95 |
-
except Exception as e:
|
| 96 |
-
import traceback
|
| 97 |
-
print(traceback.format_exc())
|
| 98 |
-
return None, f"Erro na geração: {e}"
|
| 99 |
-
|
| 100 |
-
def ui_multi_concept(files, descs_text, final_prompt):
|
| 101 |
-
"""Handler para a aba de Composição Multi-Conceito."""
|
| 102 |
-
if not files: return [], None, "Por favor, faça o upload das imagens de conceito."
|
| 103 |
-
if not descs_text: return [], None, "Por favor, forneça as descrições (uma por linha)."
|
| 104 |
-
if not final_prompt: return [], None, "Por favor, forneça um prompt final."
|
| 105 |
-
|
| 106 |
descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
|
| 107 |
-
if len(descs) != len(files):
|
|
|
|
| 108 |
|
| 109 |
try:
|
| 110 |
out_dir = svc.multi_concept_compose(files, descs, final_prompt)
|
| 111 |
-
imgs, vid = _list_media(Path(out_dir))
|
| 112 |
-
return imgs, vid, f"Saídas salvas em: {out_dir}"
|
| 113 |
except Exception as e:
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
with gr.Row():
|
| 123 |
-
setup_out = gr.Textbox(label="Status
|
| 124 |
|
| 125 |
-
with gr.Tab("
|
| 126 |
-
with gr.Row():
|
| 127 |
-
img_mt = gr.Image(type="filepath", label="Imagem de Entrada")
|
| 128 |
-
with gr.Column():
|
| 129 |
-
turns_mt = gr.Textbox(lines=5, label="Instruções de Edição (uma por linha)", placeholder="Ex: adicione um chapéu azul\nagora, mude o fundo para uma praia")
|
| 130 |
-
with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
|
| 131 |
-
with gr.Row():
|
| 132 |
-
num_gpus_mt = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
|
| 133 |
-
batch_size_mt = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
|
| 134 |
-
resolution_mt = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512, info="Maior resolução exige mais VRAM e tempo.")
|
| 135 |
-
use_vae_slicing_mt = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
|
| 136 |
-
neg_prompt_mt = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
|
| 137 |
-
seed_mt = gr.Number(label="Seed (Semente)", value=1, precision=0)
|
| 138 |
-
steps_mt = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50, info="Menos passos = mais rápido.")
|
| 139 |
-
cfg_mt = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5, info="Quão forte o modelo segue o prompt.")
|
| 140 |
-
run_mt = gr.Button("Executar Edição Multi-Turno", variant="primary")
|
| 141 |
-
gallery_mt = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
|
| 142 |
-
video_mt = gr.Video(label="Vídeo da Sequência (se disponível)")
|
| 143 |
-
status_mt = gr.Textbox(label="Status da Saída", interactive=False)
|
| 144 |
-
run_mt.click(ui_multi_turn,
|
| 145 |
-
inputs=[img_mt, turns_mt, neg_prompt_mt, seed_mt, steps_mt, cfg_mt, resolution_mt, use_vae_slicing_mt, num_gpus_mt, batch_size_mt],
|
| 146 |
-
outputs=[gallery_mt, video_mt, status_mt])
|
| 147 |
-
|
| 148 |
-
with gr.Tab("Texto-para-Vídeo"):
|
| 149 |
-
with gr.Row():
|
| 150 |
-
img_vid = gr.Image(type="filepath", label="Frame Inicial")
|
| 151 |
-
with gr.Column():
|
| 152 |
-
prompt_vid = gr.Textbox(lines=2, label="Prompt do Vídeo", placeholder="Ex: um gato andando pela sala")
|
| 153 |
-
with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
|
| 154 |
-
with gr.Row():
|
| 155 |
-
num_gpus_vid = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
|
| 156 |
-
batch_size_vid = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
|
| 157 |
-
resolution_vid = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512)
|
| 158 |
-
fps_vid = gr.Slider(label="Frames por Segundo (FPS)", minimum=1, maximum=24, step=1, value=2)
|
| 159 |
-
use_vae_slicing_vid = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
|
| 160 |
-
neg_prompt_vid = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
|
| 161 |
-
seed_vid = gr.Number(label="Seed (Semente)", value=1, precision=0)
|
| 162 |
-
steps_vid = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50)
|
| 163 |
-
cfg_vid = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5)
|
| 164 |
-
run_vid = gr.Button("Gerar Vídeo", variant="primary")
|
| 165 |
-
video_vid = gr.Video(label="Vídeo Gerado")
|
| 166 |
-
status_vid = gr.Textbox(label="Status da Saída", interactive=False)
|
| 167 |
-
run_vid.click(ui_text_to_video,
|
| 168 |
-
inputs=[img_vid, prompt_vid, neg_prompt_vid, seed_vid, steps_vid, cfg_vid, resolution_vid, fps_vid, use_vae_slicing_vid, num_gpus_vid, batch_size_vid],
|
| 169 |
-
outputs=[video_vid, status_vid])
|
| 170 |
-
|
| 171 |
-
with gr.Tab("Composição Multi-Conceito"):
|
| 172 |
-
gr.Markdown("Nota: A composição multi-conceito está atualmente configurada para rodar em uma única GPU para garantir estabilidade.")
|
| 173 |
with gr.Row():
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
demo.load(fn=setup_auto, outputs=setup_out)
|
| 189 |
|
| 190 |
if __name__ == "__main__":
|
| 191 |
demo.launch(
|
| 192 |
server_name="0.0.0.0",
|
| 193 |
server_port=int(os.getenv("PORT", "7860")),
|
| 194 |
-
|
|
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
+
VINCIE Service UI (Gradio)
|
| 4 |
+
- Automatic setup runs on app load (no manual setup button).
|
| 5 |
+
- Multi-turn editing and multi-concept composition front-end.
|
| 6 |
+
- Designed for NVIDIA L40S (SM 8.9) environments aligned with CUDA 12.x.
|
| 7 |
+
- Functional reference: ByteDance-Seed/VINCIE.
|
| 8 |
+
- Space and Docker developed by Carlex (contact below).
|
|
|
|
|
|
|
| 9 |
"""
|
| 10 |
|
| 11 |
import os
|
| 12 |
from pathlib import Path
|
| 13 |
from typing import List, Tuple, Optional
|
| 14 |
+
|
| 15 |
import gradio as gr
|
| 16 |
|
| 17 |
+
# Adapt this import to the project layout.
|
| 18 |
+
# Provide a VincieService with:
|
| 19 |
+
# - ensure_repo(): clones/updates upstream repo if missing
|
| 20 |
+
# - ensure_model(): downloads/validates checkpoints to /app/ckpt/VINCIE-3B
|
| 21 |
+
# - multi_turn_edit(image_path: str, turns: List[str]) -> str (output dir)
|
| 22 |
+
# - multi_concept_compose(files: List[str], descs: List[str], final_prompt: str) -> str (output dir)
|
| 23 |
+
from services.vincie import VincieService # change path if needed
|
| 24 |
|
| 25 |
+
# Instantiate the service (defaults to /app/VINCIE and /app/ckpt/VINCIE-3B)
|
| 26 |
svc = VincieService()
|
|
|
|
| 27 |
|
| 28 |
+
|
| 29 |
def setup_auto() -> str:
|
| 30 |
"""
|
| 31 |
+
Run an idempotent setup on interface load:
|
| 32 |
+
- Ensure the upstream repository is present and ready.
|
| 33 |
+
- Ensure the model checkpoint is downloaded and ready.
|
| 34 |
+
Returns an English status string for the UI.
|
| 35 |
"""
|
| 36 |
try:
|
| 37 |
svc.ensure_repo()
|
| 38 |
svc.ensure_model()
|
| 39 |
+
return (
|
| 40 |
+
"Setup completed successfully: repository and checkpoint are ready "
|
| 41 |
+
"for inference on an NVIDIA L40S environment."
|
| 42 |
+
)
|
| 43 |
except Exception as e:
|
| 44 |
+
return f"Setup encountered an error: {e}"
|
| 45 |
+
|
|
|
|
|
|
|
| 46 |
|
| 47 |
def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
|
| 48 |
"""
|
| 49 |
+
Enumerate resulting images and the most recent video from an output directory.
|
| 50 |
+
|
| 51 |
+
Args:
|
| 52 |
+
out_dir: Path to the directory where the service wrote its results.
|
| 53 |
+
max_images: Upper bound on how many images to surface in the gallery.
|
| 54 |
+
|
| 55 |
+
Returns:
|
| 56 |
+
A tuple (images, video) where:
|
| 57 |
+
- images is a list of file paths to images sorted by modified time,
|
| 58 |
+
- video is the path to the latest .mp4 if found, otherwise None.
|
| 59 |
"""
|
| 60 |
img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
|
| 61 |
+
images: List[Path] = []
|
| 62 |
+
for pat in img_globs:
|
| 63 |
+
images += list(out_dir.rglob(pat))
|
| 64 |
+
images = sorted(images, key=lambda p: p.stat().st_mtime)
|
| 65 |
+
image_paths = [str(p) for p in images[-max_images:]] if images else []
|
|
|
|
| 66 |
videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime)
|
| 67 |
video_path = str(videos[-1]) if videos else None
|
| 68 |
return image_paths, video_path
|
| 69 |
|
| 70 |
+
|
| 71 |
+
def ui_multi_turn(input_image: Optional[str], turns_text: Optional[str]):
|
| 72 |
+
"""
|
| 73 |
+
Multi-turn image editing entrypoint for the UI.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
input_image: Path to a single input image on disk.
|
| 77 |
+
turns_text: User-provided editing turns, one instruction per line.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
(gallery, video, status) for Gradio components.
|
| 81 |
+
"""
|
| 82 |
+
if not input_image or not str(input_image).strip():
|
| 83 |
+
return [], None, "Please provide an input image."
|
| 84 |
if not turns_text or not turns_text.strip():
|
| 85 |
+
return [], None, "Please provide edit turns (one per line)."
|
| 86 |
|
| 87 |
turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
|
| 88 |
try:
|
| 89 |
+
out_dir = svc.multi_turn_edit(input_image, turns)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
except Exception as e:
|
| 91 |
+
return [], None, f"Generation error: {e}"
|
|
|
|
|
|
|
| 92 |
|
| 93 |
+
imgs, vid = _list_media(Path(out_dir))
|
| 94 |
+
status = f"Outputs saved to: {out_dir}"
|
| 95 |
+
return imgs, vid, status
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def ui_multi_concept(files: Optional[List[str]], descs_text: Optional[str], final_prompt: Optional[str]):
|
| 99 |
+
"""
|
| 100 |
+
Multi-concept composition entrypoint for the UI.
|
| 101 |
+
|
| 102 |
+
Args:
|
| 103 |
+
files: List of paths to concept images on disk.
|
| 104 |
+
descs_text: Per-image descriptions (one line per image, in the same order).
|
| 105 |
+
final_prompt: A final composition prompt that aggregates the concepts.
|
| 106 |
+
|
| 107 |
+
Returns:
|
| 108 |
+
(gallery, video, status) for Gradio components.
|
| 109 |
+
"""
|
| 110 |
+
if not files:
|
| 111 |
+
return [], None, "Please upload concept images."
|
| 112 |
+
if not descs_text or not descs_text.strip():
|
| 113 |
+
return [], None, "Please provide descriptions (one per line)."
|
| 114 |
+
if not final_prompt or not final_prompt.strip():
|
| 115 |
+
return [], None, "Please provide a final prompt."
|
| 116 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
|
| 118 |
+
if len(descs) != len(files):
|
| 119 |
+
return [], None, f"Descriptions count ({len(descs)}) must match images count ({len(files)})."
|
| 120 |
|
| 121 |
try:
|
| 122 |
out_dir = svc.multi_concept_compose(files, descs, final_prompt)
|
|
|
|
|
|
|
| 123 |
except Exception as e:
|
| 124 |
+
return [], None, f"Generation error: {e}"
|
| 125 |
+
|
| 126 |
+
imgs, vid = _list_media(Path(out_dir))
|
| 127 |
+
status = f"Outputs saved to: {out_dir}"
|
| 128 |
+
return imgs, vid, status
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
with gr.Blocks(title="VINCIE Service") as demo:
|
| 132 |
+
# Header and credits
|
| 133 |
+
gr.Markdown(
|
| 134 |
+
"\n".join(
|
| 135 |
+
[
|
| 136 |
+
"# VINCIE Service — Multi-turn Editing and Multi-concept Composition",
|
| 137 |
+
"- Automatic setup runs at startup; setup status appears below.",
|
| 138 |
+
"- Hardware requirement: NVIDIA L40S (SM 8.9) is recommended for this build.",
|
| 139 |
+
"- Functional upstream model: ByteDance-Seed/VINCIE (see project repository).",
|
| 140 |
+
"- Space and Docker were developed by Carlex.",
|
| 141 |
+
"- Contact: Email: [email protected] | GitHub: carlex22",
|
| 142 |
+
]
|
| 143 |
+
)
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
with gr.Row():
|
| 147 |
+
setup_out = gr.Textbox(label="Setup Status", interactive=False)
|
| 148 |
|
| 149 |
+
with gr.Tab("Multi-turn Editing"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
with gr.Row():
|
| 151 |
+
img = gr.Image(type="filepath", label="Input image")
|
| 152 |
+
turns = gr.Textbox(lines=8, label="Turns (one per line)")
|
| 153 |
+
run1 = gr.Button("Run")
|
| 154 |
+
out_gallery = gr.Gallery(label="Images", columns=4, height="auto")
|
| 155 |
+
out_video = gr.Video(label="Video (if available)")
|
| 156 |
+
out_status = gr.Textbox(label="Output", interactive=False)
|
| 157 |
+
run1.click(ui_multi_turn, inputs=[img, turns], outputs=[out_gallery, out_video, out_status])
|
| 158 |
+
|
| 159 |
+
with gr.Tab("Multi-concept Composition"):
|
| 160 |
+
files = gr.File(file_count="multiple", type="filepath", label="Concept images")
|
| 161 |
+
descs = gr.Textbox(lines=8, label="Descriptions (one per line, same order as images)")
|
| 162 |
+
final_prompt = gr.Textbox(lines=2, label="Final prompt")
|
| 163 |
+
run2 = gr.Button("Run")
|
| 164 |
+
out_gallery2 = gr.Gallery(label="Images", columns=4, height="auto")
|
| 165 |
+
out_video2 = gr.Video(label="Video (if available)")
|
| 166 |
+
out_status2 = gr.Textbox(label="Output", interactive=False)
|
| 167 |
+
run2.click(
|
| 168 |
+
ui_multi_concept,
|
| 169 |
+
inputs=[files, descs, final_prompt],
|
| 170 |
+
outputs=[out_gallery2, out_video2, out_status2],
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
# Auto-setup on load (no manual button)
|
| 174 |
demo.load(fn=setup_auto, outputs=setup_out)
|
| 175 |
|
| 176 |
if __name__ == "__main__":
|
| 177 |
demo.launch(
|
| 178 |
server_name="0.0.0.0",
|
| 179 |
server_port=int(os.getenv("PORT", "7860")),
|
| 180 |
+
allowed_paths=["/app/outputs", "/app/ckpt"],
|
| 181 |
+
)
|