varunv2004 commited on
Commit
a66a94f
·
verified ·
1 Parent(s): 666a9a0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +184 -188
app.py CHANGED
@@ -1,28 +1,16 @@
1
  import os
2
  import gc
 
 
3
  import torch
4
  import numpy as np
5
  from PIL import Image
6
  import imageio
7
  import gradio as gr
8
- from huggingface_hub import hf_hub_download
9
-
10
- # ComfyUI imports (assumes ComfyUI folder is dedicated in repo)
11
- from comfy import model_management # may be needed for plugin system
12
- from nodes import (
13
- CheckpointLoaderSimple,
14
- CLIPLoader,
15
- CLIPTextEncode,
16
- VAELoader,
17
- VAEDecode,
18
- KSampler,
19
- )
20
- from custom_nodes.ComfyUI_GGUF.nodes import UnetLoaderGGUF
21
- from comfy_extras.nodes_hunyuan import EmptyHunyuanLatentVideo
22
- from comfy_extras.nodes_images import SaveAnimatedWEBP
23
- from comfy_extras.nodes_video import SaveWEBM
24
-
25
- # Globals
26
  unet_loader = None
27
  clip_loader = None
28
  clip_encode_positive = None
@@ -31,21 +19,114 @@ vae_loader = None
31
  empty_latent_video = None
32
  ksampler = None
33
  vae_decode = None
34
-
35
-
36
- # Ensure models are available via HF hub or local
37
- def ensure_model(repo_id, filename, folder):
38
- os.makedirs(f"ComfyUI/models/{folder}", exist_ok=True)
39
- local_path = os.path.join("ComfyUI", "models", folder, filename)
40
- if not os.path.isfile(local_path):
41
- hf_hub_download(repo_id=repo_id, filename=filename, local_dir=os.path.dirname(local_path))
42
- return local_path
43
-
44
-
45
- # 1️⃣ Initialize imports and model loader utilities
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  def imports_initialization():
47
  global unet_loader, clip_loader, clip_encode_positive, clip_encode_negative
48
- global vae_loader, empty_latent_video, ksampler, vae_decode
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  unet_loader = UnetLoaderGGUF()
51
  clip_loader = CLIPLoader()
@@ -55,172 +136,87 @@ def imports_initialization():
55
  empty_latent_video = EmptyHunyuanLatentVideo()
56
  ksampler = KSampler()
57
  vae_decode = VAEDecode()
 
 
58
 
59
- return "Imports done and models initialized."
60
 
61
-
62
- # Clean GPU memory
 
63
  def clear_memory():
64
  gc.collect()
65
  if torch.cuda.is_available():
66
  torch.cuda.empty_cache()
67
  torch.cuda.ipc_collect()
68
  for obj in list(globals().values()):
69
- try:
70
- if torch.is_tensor(obj) or (hasattr(obj, "data") and torch.is_tensor(obj.data)):
71
- del obj
72
- except:
73
- pass
74
  gc.collect()
75
 
76
-
77
- # Save utility functions
78
- def save_as_mp4(images, prefix, fps):
79
- os.makedirs("output", exist_ok=True)
80
- path = f"output/{prefix}.mp4"
81
- writer = imageio.get_writer(path, fps=fps)
82
- for img in images:
83
- writer.append_data((img.cpu().numpy() * 255).astype(np.uint8))
84
- writer.close()
85
- return path
86
-
87
- def save_as_webm(images, prefix, fps):
88
- os.makedirs("output", exist_ok=True)
89
- path = f"output/{prefix}.webm"
90
- writer = imageio.get_writer(
91
- path, format='FFMPEG', fps=fps,
92
- codec='vp9', quality=20
93
- )
94
- for img in images:
95
- writer.append_data((img.cpu().numpy() * 255).astype(np.uint8))
96
- writer.close()
97
- return path
98
-
99
- def save_as_image(img, prefix):
100
- os.makedirs("output", exist_ok=True)
101
- path = f"output/{prefix}.png"
102
- pil = Image.fromarray((img.cpu().numpy() * 255).astype(np.uint8))
103
- pil.save(path)
104
- return path
105
-
106
-
107
- # 2️⃣ Text-to-Video generation pipeline
108
- def generate_video(
109
- positive_prompt, negative_prompt,
110
- width, height, seed, steps, cfg_scale,
111
- sampler_name, scheduler, frames, fps, output_format, use_q6
112
- ):
113
- log = []
114
-
115
- # 2a. Download or load model files
116
- unet_file = ensure_model(
117
- "city96/Wan2.1-T2V-14B-gguf",
118
- "wan2.1-t2v-14b-Q6_K.gguf" if use_q6 else "wan2.1-t2v-14b-Q5_0.gguf",
119
- "unet"
120
- )
121
- text_enc_file = ensure_model(
122
- "Comfy-Org/Wan_2.1_ComfyUI_repackaged",
123
- "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
124
- "text_encoders"
125
- )
126
- vae_file = ensure_model(
127
- "Comfy-Org/Wan_2.1_ComfyUI_repackaged",
128
- "wan_2.1_vae.safetensors",
129
- "vae"
130
- )
131
-
132
- # 2b. Encode text prompts
133
- log.append("🔧 Encoding prompts...")
134
- clip_model = clip_loader.load_clip(text_enc_file, "wan", "default")[0]
135
- pos = clip_encode_positive.encode(clip_model, positive_prompt)[0]
136
- neg = clip_encode_negative.encode(clip_model, negative_prompt)[0]
137
- del clip_model
138
- clear_memory()
139
-
140
- # 2c. Setup latent video
141
- latent = empty_latent_video.generate(width, height, frames, 1)[0]
142
-
143
- # 2d. Sample using UNet
144
- model = unet_loader.load_unet(unet_file)[0]
145
- log.append("🎥 Sampling latents...")
146
- sampled = ksampler.sample(
147
- model=model,
148
- seed=seed,
149
- steps=steps,
150
- cfg=cfg_scale,
151
- sampler_name=sampler_name,
152
- scheduler=scheduler,
153
- positive=pos,
154
- negative=neg,
155
- latent_image=latent
156
- )[0]
157
- del model
158
- clear_memory()
159
-
160
- # 2e. Decode via VAE
161
- log.append("🔓 Decoding with VAE...")
162
- vae_model = vae_loader.load_vae(vae_file)[0]
163
- decoded = vae_decode.decode(vae_model, sampled)[0]
164
- del vae_model
165
- clear_memory()
166
-
167
- # 2f. Save output
168
- filename = "hf_gen"
169
- if frames == 1:
170
- log.append("💾 Saving single frame...")
171
- out = save_as_image(decoded[0], filename)
172
- else:
173
- if output_format == "webm":
174
- log.append("💾 Saving as WEBM...")
175
- out = save_as_webm(decoded, filename, fps)
176
- else:
177
- log.append("💾 Saving as MP4...")
178
- out = save_as_mp4(decoded, filename, fps)
179
-
180
- log.append(f"✅ Saved: {out}")
181
- clear_memory()
182
- return "\n".join(log), out
183
-
184
-
185
- # 3️⃣ Gradio UI
186
-
187
- app = gr.Blocks()
188
- with app:
189
- gr.Markdown("# ComfyUI Text‑to‑Video on Hugging Face Spaces")
190
-
191
- with gr.Tab("Initialize"):
192
- init_btn = gr.Button("Initialize Models")
193
- init_out = gr.Textbox(lines=3, interactive=False, label="Status")
194
- init_btn.click(imports_initialization, None, init_out)
195
-
196
- with gr.Tab("Generate"):
197
- with gr.Row():
198
- pos = gr.Textbox(label="Positive Prompt", value="lion")
199
- neg = gr.Textbox(label="Negative Prompt", value="")
200
- with gr.Row():
201
- w = gr.Slider(64, 1024, step=8, value=400, label="Width")
202
- h = gr.Slider(64, 1024, step=8, value=400, label="Height")
203
- with gr.Row():
204
- se = gr.Number(label="Seed", value=0)
205
- st = gr.Slider(1, 100, value=10, label="Steps")
206
- cf = gr.Slider(1, 20, step=0.1, value=3, label="CFG Scale")
207
- with gr.Row():
208
- samp = gr.Dropdown(["uni_pc", "euler", "dpmpp_2m", "ddim", "lms"], value="uni_pc", label="Sampler")
209
- sched = gr.Dropdown(["simple", "normal", "karras", "exponential"], value="normal", label="Scheduler")
210
- with gr.Row():
211
- fr = gr.Slider(1, 60, value=2, label="Frames")
212
- fps = gr.Slider(1, 60, value=10, label="FPS")
213
- fmt = gr.Radio(["mp4", "webm"], value="webm", label="Output Format")
214
- q6 = gr.Checkbox(label="Use Q6 UNet model", value=False)
215
- gen_btn = gr.Button("Generate")
216
- gen_log = gr.Textbox(lines=10, interactive=False, label="Log")
217
- gen_out = gr.Video(label="Output Video/Image")
218
-
219
- gen_btn.click(
220
- fn=generate_video,
221
- inputs=[pos, neg, w, h, se, st, cf, samp, sched, fr, fps, fmt, q6],
222
- outputs=[gen_log, gen_out]
223
- )
224
 
225
  if __name__ == "__main__":
226
- app.launch()
 
1
  import os
2
  import gc
3
+ import sys
4
+ import subprocess
5
  import torch
6
  import numpy as np
7
  from PIL import Image
8
  import imageio
9
  import gradio as gr
10
+ from base64 import b64encode
11
+ import requests
12
+
13
+ # Globals for model loaders and flags
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  unet_loader = None
15
  clip_loader = None
16
  clip_encode_positive = None
 
19
  empty_latent_video = None
20
  ksampler = None
21
  vae_decode = None
22
+ save_webp = None
23
+ save_webm = None
24
+ useQ6 = False
25
+
26
+ # -------- Helper function to download a file using requests --------
27
+ def download_file(url, dest_path):
28
+ os.makedirs(os.path.dirname(dest_path), exist_ok=True)
29
+ if os.path.exists(dest_path):
30
+ return f"File already exists: {dest_path}"
31
+ with requests.get(url, stream=True) as r:
32
+ r.raise_for_status()
33
+ with open(dest_path, 'wb') as f:
34
+ for chunk in r.iter_content(chunk_size=8192):
35
+ f.write(chunk)
36
+ return f"Downloaded {url} to {dest_path}"
37
+
38
+ # -------------------------
39
+ # 1. Environment Setup (without aria2c)
40
+ # -------------------------
41
+ def environment_setup(use_q6: bool):
42
+ global useQ6
43
+ useQ6 = use_q6
44
+ output = []
45
+
46
+ # Install Python packages
47
+ setup_cmds = [
48
+ "pip install torch==2.6.0 torchvision==0.21.0 -q",
49
+ "pip install torchsde einops diffusers accelerate xformers==0.0.29.post2 -q",
50
+ "pip install av -q",
51
+ "pip install gradio==5.38.0 imageio numpy Pillow requests -q"
52
+ ]
53
+ for cmd in setup_cmds:
54
+ output.append(f"Running: {cmd}")
55
+ proc = subprocess.run(cmd, shell=True, capture_output=True, text=True)
56
+ output.append(proc.stdout)
57
+ output.append(proc.stderr)
58
+
59
+ # Clone ComfyUI if missing
60
+ if not os.path.isdir("/content/ComfyUI"):
61
+ output.append("Cloning ComfyUI repo...")
62
+ proc = subprocess.run("git clone https://github.com/Isi-dev/ComfyUI /content/ComfyUI", shell=True, capture_output=True, text=True)
63
+ output.append(proc.stdout + proc.stderr)
64
+ else:
65
+ output.append("ComfyUI repo already exists")
66
+
67
+ # Clone custom nodes repo
68
+ if not os.path.isdir("/content/ComfyUI/custom_nodes/ComfyUI_GGUF"):
69
+ output.append("Cloning ComfyUI_GGUF repo...")
70
+ proc = subprocess.run("cd /content/ComfyUI/custom_nodes && git clone https://github.com/Isi-dev/ComfyUI_GGUF.git", shell=True, capture_output=True, text=True)
71
+ output.append(proc.stdout + proc.stderr)
72
+ # Install requirements for custom nodes
73
+ proc = subprocess.run("pip install -r /content/ComfyUI/custom_nodes/ComfyUI_GGUF/requirements.txt", shell=True, capture_output=True, text=True)
74
+ output.append(proc.stdout + proc.stderr)
75
+ else:
76
+ output.append("ComfyUI_GGUF repo already exists")
77
+
78
+ # Ensure model directories exist
79
+ model_unet_dir = "/content/ComfyUI/models/unet"
80
+ text_enc_dir = "/content/ComfyUI/models/text_encoders"
81
+ vae_dir = "/content/ComfyUI/models/vae"
82
+ os.makedirs(model_unet_dir, exist_ok=True)
83
+ os.makedirs(text_enc_dir, exist_ok=True)
84
+ os.makedirs(vae_dir, exist_ok=True)
85
+
86
+ # Download UNet model using requests fallback
87
+ if useQ6:
88
+ model_url = "https://huggingface.co/city96/Wan2.1-T2V-14B-gguf/resolve/main/wan2.1-t2v-14b-Q6_K.gguf"
89
+ model_name = "wan2.1-t2v-14b-Q6_K.gguf"
90
+ else:
91
+ model_url = "https://huggingface.co/city96/Wan2.1-T2V-14B-gguf/resolve/main/wan2.1-t2v-14b-Q5_0.gguf"
92
+ model_name = "wan2.1-t2v-14b-Q5_0.gguf"
93
+ unet_path = os.path.join(model_unet_dir, model_name)
94
+ output.append(download_file(model_url, unet_path))
95
+
96
+ # Download text encoder and VAE
97
+ te_url = "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors"
98
+ vae_url = "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/vae/wan_2.1_vae.safetensors"
99
+ te_path = os.path.join(text_enc_dir, "umt5_xxl_fp8_e4m3fn_scaled.safetensors")
100
+ vae_path = os.path.join(vae_dir, "wan_2.1_vae.safetensors")
101
+ output.append(download_file(te_url, te_path))
102
+ output.append(download_file(vae_url, vae_path))
103
+
104
+ return "\n".join(output)
105
+
106
+ # -------------------------
107
+ # 2. Imports & Initialization
108
+ # -------------------------
109
  def imports_initialization():
110
  global unet_loader, clip_loader, clip_encode_positive, clip_encode_negative
111
+ global vae_loader, empty_latent_video, ksampler, vae_decode, save_webp, save_webm
112
+
113
+ sys.path.insert(0, '/content/ComfyUI')
114
+
115
+ from comfy import model_management
116
+ from nodes import (
117
+ CheckpointLoaderSimple,
118
+ CLIPLoader,
119
+ CLIPTextEncode,
120
+ VAEDecode,
121
+ VAELoader,
122
+ KSampler,
123
+ UNETLoader
124
+ )
125
+ from custom_nodes.ComfyUI_GGUF.nodes import UnetLoaderGGUF
126
+ from comfy_extras.nodes_model_advanced import ModelSamplingSD3
127
+ from comfy_extras.nodes_hunyuan import EmptyHunyuanLatentVideo
128
+ from comfy_extras.nodes_images import SaveAnimatedWEBP
129
+ from comfy_extras.nodes_video import SaveWEBM
130
 
131
  unet_loader = UnetLoaderGGUF()
132
  clip_loader = CLIPLoader()
 
136
  empty_latent_video = EmptyHunyuanLatentVideo()
137
  ksampler = KSampler()
138
  vae_decode = VAEDecode()
139
+ save_webp = SaveAnimatedWEBP()
140
+ save_webm = SaveWEBM()
141
 
142
+ return "Imports done and models initialized."
143
 
144
+ # -------------------------
145
+ # 3. Utility Functions
146
+ # -------------------------
147
  def clear_memory():
148
  gc.collect()
149
  if torch.cuda.is_available():
150
  torch.cuda.empty_cache()
151
  torch.cuda.ipc_collect()
152
  for obj in list(globals().values()):
153
+ if torch.is_tensor(obj) or (hasattr(obj, "data") and torch.is_tensor(obj.data)):
154
+ del obj
 
 
 
155
  gc.collect()
156
 
157
+ def save_as_mp4(images, filename_prefix, fps, output_dir="/content/ComfyUI/output"):
158
+ os.makedirs(output_dir, exist_ok=True)
159
+ output_path = f"{output_dir}/{filename_prefix}.mp4"
160
+ frames = [(img.cpu().numpy() * 255).astype(np.uint8) for img in images]
161
+ with imageio.get_writer(output_path, fps=fps) as writer:
162
+ for frame in frames:
163
+ writer.append_data(frame)
164
+ return output_path
165
+
166
+ def save_as_webp(images, filename_prefix, fps, quality=90, lossless=False, method=4, output_dir="/content/ComfyUI/output"):
167
+ os.makedirs(output_dir, exist_ok=True)
168
+ output_path = f"{output_dir}/{filename_prefix}.webp"
169
+ frames = [(img.cpu().numpy() * 255).astype(np.uint8) for img in images]
170
+ kwargs = {'fps': int(fps), 'quality': int(quality), 'lossless': bool(lossless), 'method': int(method)}
171
+ with imageio.get_writer(output_path, format='WEBP', mode='I', **kwargs) as writer:
172
+ for frame in frames:
173
+ writer.append_data(frame)
174
+ return output_path
175
+
176
+ def save_as_webm(images, filename_prefix, fps, codec="vp9", quality=32, output_dir="/content/ComfyUI/output"):
177
+ os.makedirs(output_dir, exist_ok=True)
178
+ output_path = f"{output_dir}/{filename_prefix}.webm"
179
+ frames = [(img.cpu().numpy() * 255).astype(np.uint8) for img in images]
180
+ kwargs = {'fps': int(fps), 'quality': int(quality), 'codec': str(codec), 'output_params': ['-crf', str(int(quality))]}
181
+ with imageio.get_writer(output_path, format='FFMPEG', mode='I', **kwargs) as writer:
182
+ for frame in frames:
183
+ writer.append_data(frame)
184
+ return output_path
185
+
186
+ def save_as_image(image, filename_prefix, output_dir="/content/ComfyUI/output"):
187
+ os.makedirs(output_dir, exist_ok=True)
188
+ output_path = f"{output_dir}/{filename_prefix}.png"
189
+ frame = (image.cpu().numpy() * 255).astype(np.uint8)
190
+ Image.fromarray(frame).save(output_path)
191
+ return output_path
192
+
193
+ def display_video_gradio(video_path):
194
+ # Return path for Gradio video component
195
+ return video_path
196
+
197
+ # -------------------------
198
+ # 4. Example Gradio interface setup (simplified)
199
+ # -------------------------
200
+ def dummy_inference(prompt):
201
+ # Placeholder for inference logic
202
+ return f"Prompt received: {prompt}"
203
+
204
+ def main():
205
+ with gr.Blocks() as demo:
206
+ gr.Markdown("# ComfyUI Integration Demo")
207
+
208
+ use_q6_checkbox = gr.Checkbox(label="Use Q6 Model", value=False)
209
+ setup_button = gr.Button("Setup Environment & Download Models")
210
+ setup_output = gr.Textbox(label="Setup Log", lines=15)
211
+
212
+ prompt_input = gr.Textbox(label="Prompt")
213
+ run_button = gr.Button("Run Inference")
214
+ result_output = gr.Textbox(label="Output")
215
+
216
+ setup_button.click(fn=environment_setup, inputs=[use_q6_checkbox], outputs=[setup_output])
217
+ run_button.click(fn=dummy_inference, inputs=[prompt_input], outputs=[result_output])
218
+
219
+ demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
  if __name__ == "__main__":
222
+ main()