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

Update run_comfyui.py

Browse files
Files changed (1) hide show
  1. run_comfyui.py +234 -144
run_comfyui.py CHANGED
@@ -1,23 +1,9 @@
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
  """
@@ -25,141 +11,245 @@ def check_comfyui_started(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)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
  """
 
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()