JBroDev commited on
Commit
2b8550d
·
verified ·
1 Parent(s): 999461e

Update run_comfyui.py

Browse files
Files changed (1) hide show
  1. run_comfyui.py +165 -255
run_comfyui.py CHANGED
@@ -1,255 +1,165 @@
1
- import requests
2
- import json
3
- import time
4
- import argparse
5
- import os
6
- import re
7
-
8
- def check_comfyui_started(url):
9
- """
10
- Checks if ComfyUI is running at the given URL.
11
- """
12
- try:
13
- response = requests.get(url)
14
- return response.status_code == 200
15
- except requests.ConnectionError:
16
- return False
17
-
18
- def load_and_update_prompt(file_path, width, height, length, batch_size, shift_amount, guidance_amount, steps_amount, denoise_amount, frame_rate, prompt):
19
- """
20
- Loads the prompt payload from a JSON file and updates the specified fields.
21
-
22
- Args:
23
- file_path (str): Path to the prompt-payload.json file.
24
- width (int): Width to set in the prompt.
25
- height (int): Height to set in the prompt.
26
- length (int): Length (frames) to set in the prompt.
27
- batch_size (int): Batch size to set in the prompt.
28
- shift_amount (float): Shift amount to set in the prompt.
29
- guidance_amount (float): Guidance amount to set in the prompt.
30
- steps_amount (int): Steps amount to set in the prompt.
31
- denoise_amount (float): Denoise amount to set in the prompt.
32
- frame_rate (int): Frame rate to set in the prompt.
33
- prompt (str): Prompt text to set in the prompt.
34
-
35
- Returns:
36
- dict: Updated prompt payload as a dictionary.
37
- """
38
- try:
39
- with open(file_path, 'r', encoding='utf-8') as f:
40
- payload = json.load(f)
41
- except Exception as e:
42
- print(f"Error loading JSON file: {e}")
43
- return None
44
-
45
- # Update width and height
46
- payload['prompt']['232']['inputs']['width'] = width
47
- payload['prompt']['232']['inputs']['height'] = height
48
-
49
- # Update length (frames)
50
- payload['prompt']['295']['inputs']['int'] = length
51
-
52
- # Update batch size
53
- payload['prompt']['232']['inputs']['batch_size'] = batch_size
54
-
55
- # Update shift amount
56
- payload['prompt']['67']['inputs']['shift'] = shift_amount
57
-
58
- # Update guidance amount
59
- payload['prompt']['26']['inputs']['guidance'] = guidance_amount
60
-
61
- # Update steps amount
62
- payload['prompt']['17']['inputs']['steps'] = steps_amount
63
-
64
- # Update denoise amount
65
- payload['prompt']['17']['inputs']['denoise'] = denoise_amount
66
-
67
- # Update frame rate
68
- payload['prompt']['82']['inputs']['frame_rate'] = frame_rate
69
-
70
- # Update prompt text
71
- payload['prompt']['44']['inputs']['text'] = prompt
72
-
73
- return payload
74
-
75
- def post_prompt(url, payload):
76
- """
77
- Posts the prompt payload to the ComfyUI API.
78
-
79
- Args:
80
- url (str): ComfyUI API endpoint URL.
81
- payload (dict): Prompt payload as a dictionary.
82
-
83
- Returns:
84
- str: Prompt ID if successful, None otherwise.
85
- """
86
- try:
87
- response = requests.post(url, json=payload)
88
- response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
89
- return response.json().get('prompt_id')
90
- except requests.exceptions.RequestException as e:
91
- print(f"Error posting prompt: {e}")
92
- return None
93
-
94
- def get_queue_progress(url):
95
- """
96
- Retrieves the queue progress from the ComfyUI API.
97
-
98
- Args:
99
- url (str): ComfyUI API queue endpoint URL.
100
-
101
- Returns:
102
- tuple: (queue_pending, queue_running) where queue_pending and queue_running are lists,
103
- or (None, None) if an error occurred.
104
- """
105
- try:
106
- response = requests.get(url)
107
- response.raise_for_status()
108
- queue_data = response.json()
109
- queue_pending = queue_data.get('queue_pending', [])
110
- queue_running = queue_data.get('queue_running', [])
111
- return queue_pending, queue_running
112
- except requests.exceptions.RequestException as e:
113
- print(f"Error getting queue progress: {e}")
114
- return None, None
115
-
116
- def get_history(url, prompt_id):
117
- """
118
- Retrieves the history from the ComfyUI API.
119
-
120
- Args:
121
- url (str): ComfyUI API history endpoint URL.
122
- prompt_id (str): Prompt ID to look for in the history.
123
-
124
- Returns:
125
- tuple: (status, video_path) where status is a string ("success" or "failure") and video_path is the path to the video,
126
- or (None, None) if an error occurred.
127
- """
128
- try:
129
- response = requests.get(url)
130
- response.raise_for_status()
131
- history_data = response.json()
132
-
133
- if history_data:
134
- # Find the history item with the matching prompt_id
135
- for history_item_id, history_item in history_data.items():
136
- if history_item_id == prompt_id:
137
- status = history_item['status']['status_str']
138
- completed = history_item['status']['completed']
139
-
140
- if status == "success" and completed:
141
- try:
142
- for output in history_item['outputs'].values():
143
- for gif in output.get('gifs', []):
144
- video_path = gif['fullpath']
145
- return "success", video_path
146
- except (KeyError, TypeError):
147
- print("Video path not found in history data.")
148
- return "failure", None
149
- else:
150
- print("Video generation failed according to history data.")
151
- return "failure", None
152
- print("Prompt ID not found in history data.")
153
- return "failure", None
154
- else:
155
- print("No history data found.")
156
- return "failure", None
157
-
158
- except requests.exceptions.RequestException as e:
159
- print(f"Error getting history: {e}")
160
- return None, None
161
-
162
- def extract_video_number(filename):
163
- """
164
- Extracts the video number from the filename using a regular expression.
165
- """
166
- match = re.search(r'_(\d+)\.mp4', filename)
167
- if match:
168
- return match.group(1)
169
- return None
170
-
171
- def main():
172
- parser = argparse.ArgumentParser(description="Run ComfyUI with specified parameters.")
173
- parser.add_argument("--width", type=int, default=320, help="Width of the generated image.")
174
- parser.add_argument("--height", type=int, default=480, help="Height of the generated image.")
175
- parser.add_argument("--length", type=int, default=45, help="Length (number of frames) of the video.")
176
- parser.add_argument("--batchSize", type=int, default=1, help="Batch size for video generation.")
177
- parser.add_argument("--shiftAmount", type=float, default=9.0, help="Shift amount for ModelSamplingSD3.")
178
- parser.add_argument("--guidanceAmount", type=float, default=7.5, help="Guidance amount for FluxGuidance.")
179
- parser.add_argument("--stepsAmount", type=int, default=12, help="Steps amount for BasicScheduler.")
180
- parser.add_argument("--denoiseAmount", type=float, default=1.0, help="Denoise amount for BasicScheduler.")
181
- parser.add_argument("--frameRate", type=int, default=24, help="Frame rate for video combine.")
182
- parser.add_argument("--prompt", type=str, required=True, help="Text prompt for video generation.")
183
- parser.add_argument("--upscale", type=bool, default=False, help="Whether to use upscale or not.")
184
- parser.add_argument("--comfyui_url", type=str, default="http://localhost:8188", help="ComfyUI base URL.")
185
-
186
- args = parser.parse_args()
187
-
188
- API_URL = f"{args.comfyui_url}/api/prompt"
189
- QUEUE_URL = f"{args.comfyui_url}/api/queue"
190
- HISTORY_URL = f"{args.comfyui_url}/api/history?max_items=1"
191
-
192
- if args.upscale:
193
- prompt_file = "prompt-payload.json"
194
- filename_prefix = "Hunyuan_upscaled"
195
- else:
196
- prompt_file = "prompt-payload-no-upscale.json"
197
- filename_prefix = "Hunyuan_raw"
198
-
199
- # Check if ComfyUI is running
200
- print("Checking if ComfyUI is running...")
201
- while not check_comfyui_started(args.comfyui_url):
202
- print("ComfyUI not yet running. Waiting...")
203
- time.sleep(5)
204
- print("ComfyUI is running.")
205
-
206
- # Load and update the prompt payload
207
- print("Loading and updating prompt payload...")
208
- prompt_file_path = os.path.join(os.path.dirname(__file__), prompt_file)
209
- try:
210
- prompt_payload = load_and_update_prompt(prompt_file_path, args.width, args.height, args.length, args.batchSize, args.shiftAmount, args.guidanceAmount, args.stepsAmount, args.denoiseAmount, args.frameRate, args.prompt)
211
- except Exception as e:
212
- print(f"Error loading and updating prompt: {e}")
213
- return
214
-
215
- # Post the prompt
216
- print("Posting prompt to ComfyUI...")
217
- prompt_id = post_prompt(API_URL, prompt_payload)
218
-
219
- if prompt_id:
220
- print(f"Prompt submitted successfully with ID: {prompt_id}")
221
-
222
- # Monitor the queue until it's empty
223
- print("Monitoring queue...")
224
- while True:
225
- queue_pending, queue_running = get_queue_progress(QUEUE_URL)
226
- if queue_pending is None or queue_running is None:
227
- print("Failed to get queue progress. Aborting.")
228
- break
229
-
230
- if not queue_pending and not queue_running:
231
- print("Queue is empty.")
232
- break
233
- else:
234
- print(f"Queue pending: {len(queue_pending)}, running: {len(queue_running)}. Waiting...")
235
- time.sleep(5)
236
-
237
- # Get the history
238
- print("Getting history...")
239
- status, video_path = get_history(HISTORY_URL, prompt_id)
240
-
241
- if status == "success":
242
- print(f"Video generation complete! Video path: {video_path}")
243
- video_number = extract_video_number(video_path)
244
- if video_number:
245
- output_filename = f"{filename_prefix}_{video_number}.mp4"
246
- else:
247
- output_filename = f"{filename_prefix}_latest.mp4"
248
- print(output_filename) # Print the output filename
249
- else:
250
- print("Video generation failed.")
251
- else:
252
- print("Failed to submit prompt.")
253
-
254
- if __name__ == "__main__":
255
- main()
 
1
+ import os
2
+ import subprocess
3
+ import boto3
4
+ from datetime import datetime
5
+ from typing import Optional
6
+ from src.models import GenerateVideoRequest
7
+ import modal
8
+ import time
9
+ import requests
10
+
11
+ # Define Modal stub and AWS secrets
12
+ stub = modal.App("cui-hunyuan-video")
13
+ aws_secret = modal.Secret.from_name("aws-s3-video-bucket")
14
+
15
+ # Define Docker image
16
+ dockerfile_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../models/CUI-Hunyuan/resources/Dockerfile"))
17
+ image = modal.Image.from_dockerfile(dockerfile_path).pip_install("boto3")
18
+
19
+ # AWS S3 Configuration
20
+ S3_BUCKET = "flickify-generated-videos"
21
+
22
+ def check_comfyui_started(url):
23
+ """
24
+ Checks if ComfyUI is running at the given URL.
25
+ """
26
+ try:
27
+ response = requests.get(url)
28
+ print(f"COMFYUI STATUS CHECK: ComfyUI response: {response.status_code}")
29
+ return response.status_code == 200
30
+ except requests.ConnectionError:
31
+ return False
32
+
33
+ @stub.function(
34
+ image=image,
35
+ gpu="a10g",
36
+ timeout=1800,
37
+ secrets=[aws_secret]
38
+ )
39
+ def run_cui_hunyuan_video(prompt: str, width: Optional[int] = None, height: Optional[int] = None, length: Optional[int] = None, batch_size: Optional[int] = None, shift_amount: Optional[float] = None, guidance_amount: Optional[float] = None, steps_amount: Optional[int] = None, denoise_amount: Optional[float] = None, frame_rate: Optional[int] = None, upscale: Optional[bool] = None):
40
+
41
+ """
42
+ Runs the CUI-Hunyuan Video model, uploads to S3, and returns a pre-signed URL.
43
+ """
44
+ print('Starting ComfyUI...')
45
+ # Start ComfyUI as a background process
46
+ comfyui_process = subprocess.Popen(
47
+ ["python", "main.py", "--port", "8188", "--listen"],
48
+ cwd="/workspace/ComfyUI"
49
+ )
50
+
51
+ try:
52
+ # Check if ComfyUI is running
53
+ comfyui_url = "http://localhost:8188"
54
+ for _ in range(20):
55
+ if check_comfyui_started(comfyui_url):
56
+ print("ComfyUI is running.")
57
+ break
58
+ time.sleep(5)
59
+ else:
60
+ print("Failed to start ComfyUI.")
61
+ raise RuntimeError("Failed to start ComfyUI.")
62
+
63
+ print('Running CUI-Hunyuan Video model...')
64
+ # Construct the command to run the video generation script
65
+ command = [
66
+ "python3", "run_comfyui.py",
67
+ "--prompt", prompt
68
+ ]
69
+
70
+ if width is not None:
71
+ command.extend(["--width", str(width)])
72
+ if height is not None:
73
+ command.extend(["--height", str(height)])
74
+ if length is not None:
75
+ command.extend(["--length", str(length)])
76
+ if batch_size is not None:
77
+ command.extend(["--batchSize", str(batch_size)])
78
+ if shift_amount is not None:
79
+ command.extend(["--shiftAmount", str(shift_amount)])
80
+ if guidance_amount is not None:
81
+ command.extend(["--guidanceAmount", str(guidance_amount)])
82
+ if steps_amount is not None:
83
+ command.extend(["--stepsAmount", str(steps_amount)])
84
+ if denoise_amount is not None:
85
+ command.extend(["--denoiseAmount", str(denoise_amount)])
86
+ if frame_rate is not None:
87
+ command.extend(["--frameRate", str(frame_rate)])
88
+ if upscale is not None:
89
+ command.extend(["--upscale", str(upscale)])
90
+
91
+ # Run the model
92
+ process = subprocess.run(command, shell=False, capture_output=True, text=True)
93
+
94
+ if process.returncode != 0:
95
+ raise RuntimeError(f"Error running model: {process.stderr}")
96
+
97
+ # Capture the output filename from the script
98
+ output_lines = process.stdout.strip().split('\n')
99
+ output_filename = output_lines[-1] # Assuming the last line is the filename
100
+
101
+ # Check if the output filename is valid
102
+ if not output_filename or "Error" in output_filename:
103
+ raise RuntimeError(f"Error in output: {process.stdout}")
104
+
105
+ output_file = os.path.join("/workspace/ComfyUI/output", output_filename)
106
+
107
+ # Check if the output file exists
108
+ if not os.path.exists(output_file):
109
+ raise RuntimeError(f"Output file not found: {output_file}")
110
+
111
+ # Upload to S3
112
+ try:
113
+ s3 = boto3.client("s3")
114
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
115
+ new_filename = f"{timestamp}_{output_filename}"
116
+ with open(output_file, 'rb') as file_obj:
117
+ s3.upload_fileobj(
118
+ file_obj,
119
+ S3_BUCKET,
120
+ f"videos/{new_filename}"
121
+ )
122
+
123
+ # Generate a pre-signed URL that expires in 1 hour (3600 seconds)
124
+ video_url = s3.generate_presigned_url(
125
+ 'get_object',
126
+ Params={'Bucket': S3_BUCKET, 'Key': f"videos/{new_filename}"},
127
+ ExpiresIn=3600
128
+ )
129
+
130
+ return {
131
+ "message": "Video generation successful",
132
+ "video_url": video_url
133
+ }
134
+
135
+ except Exception as e:
136
+ raise RuntimeError(f"Failed to upload video to S3: {str(e)}")
137
+
138
+ finally:
139
+ comfyui_process.terminate()
140
+
141
+ async def generate_video_cui_hunyuan(request: GenerateVideoRequest) -> dict:
142
+ """
143
+ Handles the video generation request and returns the result.
144
+ """
145
+ try:
146
+ async with stub.run():
147
+ print("Running CUI-Hunyuan Video model...")
148
+ # Call remote function without await, as it returns a Modal handle
149
+ result = run_cui_hunyuan_video.remote(
150
+ prompt=request.prompt,
151
+ width=request.width,
152
+ height=request.height,
153
+ length=request.length,
154
+ batch_size=request.batch_size,
155
+ shift_amount=request.shift_amount,
156
+ guidance_amount=request.guidance_amount,
157
+ steps_amount=request.steps_amount,
158
+ denoise_amount=request.denoise_amount,
159
+ frame_rate=request.frame_rate,
160
+ upscale=request.upscale
161
+ )
162
+ print("Result: ", await result)
163
+ return await result
164
+ except Exception as e:
165
+ raise RuntimeError(f"Error generating video: {str(e)}")