Muhammad Waqas commited on
Commit
346f789
Β·
1 Parent(s): 75efe1b

Added: Generate image to video

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
.env CHANGED
@@ -1,2 +1,5 @@
1
  SERVER_ADDRESS=https://gosign-de-image-to-video.hf.space
2
- WS_ADDRESS=wss://gosign-de-image-to-video.hf.space/ws
 
 
 
 
1
  SERVER_ADDRESS=https://gosign-de-image-to-video.hf.space
2
+ WS_ADDRESS=wss://gosign-de-image-to-video.hf.space/ws
3
+
4
+ # SERVER_ADDRESS=http://127.0.0.1:8188/
5
+ # WS_ADDRESS=ws://127.0.0.1:8188/ws
app.py CHANGED
@@ -12,6 +12,8 @@ from dotenv import load_dotenv
12
  from flask import Flask, request, jsonify, render_template, send_file, send_from_directory
13
  from PIL import Image
14
  from werkzeug.utils import secure_filename
 
 
15
 
16
  # Load environment variables from the .env file
17
  load_dotenv()
@@ -42,7 +44,7 @@ def get_image(filename, subfolder, image_type, token):
42
 
43
 
44
  def get_images(ws, workflow, token):
45
- prompt_id = queue_prompt(workflow, token)['prompt_id']
46
  output_images = {}
47
 
48
  while True:
@@ -66,13 +68,6 @@ def get_images(ws, workflow, token):
66
 
67
  return output_images
68
 
69
-
70
- def fetch_video(video_data, token):
71
- video_url = f"{server_address}/download?file={video_data['filename']}"
72
- req = urllib.request.Request(video_url)
73
- req.add_header("Authorization", f"Bearer {token}")
74
- return urllib.request.urlopen(req).read()
75
-
76
  # Default route for home welcome
77
  @app.route('/')
78
  def home():
@@ -226,18 +221,47 @@ def get_history(prompt_id, token):
226
  return make_request(f"{server_address}/history/{prompt_id}", headers=headers)
227
 
228
  def get_video_data(filename, subfolder, token):
229
- url_values = {'filename': filename, 'subfolder': subfolder}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  url = f"{server_address}/view?{urllib.parse.urlencode(url_values)}"
 
 
 
231
  req = urllib.request.Request(url)
232
  req.add_header("Authorization", f"Bearer {token}")
233
 
234
  try:
235
- return urllib.request.urlopen(req).read() # Return video content
 
 
 
236
  except urllib.error.HTTPError as e:
237
  print(f"HTTP Error: {e.code} - {e.reason}")
238
- print(e.read())
239
  raise
240
 
 
 
 
 
 
241
  # Route: Image to Video
242
  @app.route('/image_to_video', methods=['POST'])
243
  def image_to_video():
@@ -259,19 +283,31 @@ def image_to_video():
259
  base64_image = data.get('base64_image')
260
 
261
  if image_file:
262
- # Validate and save the uploaded image
263
  if not allowed_file(image_file.filename):
264
  return jsonify({'error': 'Unsupported image format'}), 400
 
 
265
  filename = secure_filename(image_file.filename)
266
- image_path = f"/tmp/{uuid.uuid4()}_{filename}"
267
 
 
 
 
268
 
 
 
 
 
269
  image_file.save(image_path)
 
 
 
 
270
  elif base64_image:
271
  # Save base64 image
272
  try:
273
- image_path = save_base64_image(base64_image)
274
- # return jsonify({'image_path': image_path})
275
 
276
  except Exception as e:
277
  return jsonify({'error': f'Invalid base64 image data: {str(e)}'}), 400
@@ -286,7 +322,7 @@ def image_to_video():
286
 
287
  # Modify workflow with inputs
288
  workflow["30"]["inputs"]["prompt"] = text_prompt
289
- # workflow["73"]["inputs"]["url"] = image_path
290
  workflow["31"]["inputs"]["prompt"] = "Low quality, watermark, strange motion"
291
  workflow["57"]["inputs"]["seed"] = random.randint(100000000000000, 999999999999999)
292
 
@@ -308,29 +344,45 @@ def image_to_video():
308
  if message.get('type') == 'executing' and message['data']['node'] is None and message['data']['prompt_id'] == prompt_id:
309
  break
310
 
311
- # Fetch video details
312
  history = get_history(prompt_id, token).get(prompt_id, {})
313
- video_data = None
314
- for node_id, node_output in history.get('outputs', {}).items():
315
- if 'videos' in node_output:
316
- video = node_output['videos'][0]
317
- video_data = get_video_data(video['filename'], video['subfolder'], token)
318
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  if not video_data:
320
  return jsonify({'error': 'Failed to generate video'}), 500
321
 
322
- # Save and return the generated video
323
- local_video_path = f"/tmp/generated_video_{uuid.uuid4()}.mp4"
324
- with open(local_video_path, 'wb') as f:
325
- f.write(video_data)
326
 
327
- return send_file(
 
328
  io.BytesIO(video_data),
329
  mimetype='video/mp4',
330
  as_attachment=True,
331
  download_name='generated_video.mp4'
332
  )
333
 
 
 
 
 
 
334
 
335
  if __name__ == '__main__':
336
  app.run(host='0.0.0.0', port=7860, debug=True) # Removed 'debug=True'
 
12
  from flask import Flask, request, jsonify, render_template, send_file, send_from_directory
13
  from PIL import Image
14
  from werkzeug.utils import secure_filename
15
+ import urllib.parse
16
+ import urllib.request
17
 
18
  # Load environment variables from the .env file
19
  load_dotenv()
 
44
 
45
 
46
  def get_images(ws, workflow, token):
47
+ prompt_id = queue_prompt(workflow, token)
48
  output_images = {}
49
 
50
  while True:
 
68
 
69
  return output_images
70
 
 
 
 
 
 
 
 
71
  # Default route for home welcome
72
  @app.route('/')
73
  def home():
 
221
  return make_request(f"{server_address}/history/{prompt_id}", headers=headers)
222
 
223
  def get_video_data(filename, subfolder, token):
224
+ """
225
+ Retrieve a video from the server using filename, subfolder, and token.
226
+ """
227
+ # Handle empty subfolder case gracefully
228
+ subfolder = subfolder or '' # Default to empty string if None
229
+
230
+ # Construct query parameters
231
+ # url_values = {
232
+ # 'filename': filename,
233
+ # 'subfolder': subfolder,
234
+ # 'type': 'video'
235
+ # }
236
+
237
+ # Construct query parameters
238
+ url_values = {
239
+ 'filename': filename
240
+ }
241
+
242
+ # Build the URL with encoded query parameters
243
  url = f"{server_address}/view?{urllib.parse.urlencode(url_values)}"
244
+ print(f"Requesting URL: {url}")
245
+
246
+ # Prepare the request with authorization token
247
  req = urllib.request.Request(url)
248
  req.add_header("Authorization", f"Bearer {token}")
249
 
250
  try:
251
+ # Fetch and return the video data
252
+ response = urllib.request.urlopen(req)
253
+ return response.read()
254
+
255
  except urllib.error.HTTPError as e:
256
  print(f"HTTP Error: {e.code} - {e.reason}")
257
+ print(e.read().decode()) # Decode error message for readability
258
  raise
259
 
260
+ except urllib.error.URLError as e:
261
+ print(f"URL Error: {e.reason}")
262
+ raise
263
+
264
+
265
  # Route: Image to Video
266
  @app.route('/image_to_video', methods=['POST'])
267
  def image_to_video():
 
283
  base64_image = data.get('base64_image')
284
 
285
  if image_file:
286
+ # Check if the file has an allowed extension
287
  if not allowed_file(image_file.filename):
288
  return jsonify({'error': 'Unsupported image format'}), 400
289
+
290
+ # Secure the filename
291
  filename = secure_filename(image_file.filename)
 
292
 
293
+ # Generate a unique path for the image
294
+ unique_filename = f"{uuid.uuid4()}_{filename}"
295
+ image_path = os.path.join('static', unique_filename)
296
 
297
+ # Ensure the 'static' directory exists
298
+ os.makedirs('static', exist_ok=True)
299
+
300
+ # Save the image to the static directory
301
  image_file.save(image_path)
302
+
303
+ # Construct the public URL to access the image
304
+ image_url = f"https://gosign-de-comfyui-api.hf.space/{image_path}"
305
+
306
  elif base64_image:
307
  # Save base64 image
308
  try:
309
+ image_url = save_base64_image(base64_image)
310
+ # return jsonify({'image_url': image_url})
311
 
312
  except Exception as e:
313
  return jsonify({'error': f'Invalid base64 image data: {str(e)}'}), 400
 
322
 
323
  # Modify workflow with inputs
324
  workflow["30"]["inputs"]["prompt"] = text_prompt
325
+ workflow["73"]["inputs"]["url"] = image_url
326
  workflow["31"]["inputs"]["prompt"] = "Low quality, watermark, strange motion"
327
  workflow["57"]["inputs"]["seed"] = random.randint(100000000000000, 999999999999999)
328
 
 
344
  if message.get('type') == 'executing' and message['data']['node'] is None and message['data']['prompt_id'] == prompt_id:
345
  break
346
 
347
+ # Fetch the complete history for the provided prompt_id
348
  history = get_history(prompt_id, token).get(prompt_id, {})
349
+ print(f"History for prompt {prompt_id}: {history}")
350
+
351
+ video_data = None # Initialize video data
 
 
352
 
353
+ # Loop through history outputs to find video or gif data
354
+ for node_id, node_output in history.get('outputs', {}).items():
355
+ if 'gifs' in node_output:
356
+ video = node_output['gifs'][0] # Take the first GIF or video
357
+ try:
358
+ print(f"Fetching video: {video['filename']} from {video['subfolder']}")
359
+ video_data = get_video_data(video['filename'], video['subfolder'], token)
360
+ break # Stop after successfully fetching the first video or GIF
361
+ except Exception as e:
362
+ print(f"Failed to retrieve video: {str(e)}")
363
+
364
+ # Check if video data was retrieved successfully
365
  if not video_data:
366
  return jsonify({'error': 'Failed to generate video'}), 500
367
 
368
+ # Save the video locally
369
+ # local_video_path = f"/tmp/generated_video_{uuid.uuid4()}.mp4"
370
+ # with open(local_video_path, 'wb') as f:
371
+ # f.write(video_data)
372
 
373
+ # Return the video as an HTTP response
374
+ response = send_file(
375
  io.BytesIO(video_data),
376
  mimetype='video/mp4',
377
  as_attachment=True,
378
  download_name='generated_video.mp4'
379
  )
380
 
381
+ # Delete the saved image after sending the response
382
+ if image_path and os.path.exists(image_path):
383
+ os.remove(image_path) # Remove the image file
384
+
385
+ return response
386
 
387
  if __name__ == '__main__':
388
  app.run(host='0.0.0.0', port=7860, debug=True) # Removed 'debug=True'
sample-response.json ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {'prompt': [
2
+ 3, '2d7683a1-f132-4f75-bb02-ced4975a745b',
3
+ {'1': {'inputs': {'model': 'THUDM/CogVideoX-5b-I2V', 'precision': 'bf16', 'fp8_transformer': 'disabled', 'compile': 'disabled', 'enable_sequential_cpu_offload': False
4
+ }, 'class_type': 'DownloadAndLoadCogVideoModel', '_meta': {'title': '(Down)load CogVideo Model'
5
+ }
6
+ }, '20': {'inputs': {'clip_name': 't5\\google_t5-v1_1-xxl_encoderonly-fp8_e4m3fn.safetensors', 'type': 'sd3'
7
+ }, 'class_type': 'CLIPLoader', '_meta': {'title': 'Load CLIP'
8
+ }
9
+ }, '30': {'inputs': {'prompt': 'A beautiful sunset over the mountains', 'strength': 1.0, 'force_offload': True, 'clip': ['20',
10
+ 0
11
+ ]
12
+ }, 'class_type': 'CogVideoTextEncode', '_meta': {'title': 'CogVideo TextEncode'
13
+ }
14
+ }, '31': {'inputs': {'prompt': 'Low quality, watermark, strange motion', 'strength': 1.0, 'force_offload': True, 'clip': ['20',
15
+ 0
16
+ ]
17
+ }, 'class_type': 'CogVideoTextEncode', '_meta': {'title': 'CogVideo TextEncode'
18
+ }
19
+ }, '37': {'inputs': {'width': 720, 'height': 480, 'upscale_method': 'lanczos', 'keep_proportion': False, 'divisible_by': 16, 'crop': 'disabled', 'image': ['73',
20
+ 0
21
+ ]
22
+ }, 'class_type': 'ImageResizeKJ', '_meta': {'title': 'Resize Image'
23
+ }
24
+ }, '44': {'inputs': {'frame_rate': 12.0, 'loop_count': 0, 'filename_prefix': 'CogVideoX-I2V', 'format': 'video/h264-mp4', 'pix_fmt': 'yuv420p', 'crf': 19, 'save_metadata': True, 'pingpong': False, 'save_output': True, 'images': ['56',
25
+ 0
26
+ ]
27
+ }, 'class_type': 'VHS_VideoCombine', '_meta': {'title': 'Video Combine πŸŽ₯πŸ…₯πŸ…—πŸ…’'
28
+ }
29
+ }, '56': {'inputs': {'enable_vae_tiling': False, 'tile_sample_min_height': 96, 'tile_sample_min_width': 96, 'tile_overlap_factor_height': 0.083, 'tile_overlap_factor_width': 0.083, 'auto_tile_size': True, 'pipeline': ['57',
30
+ 0
31
+ ], 'samples': ['57',
32
+ 1
33
+ ]
34
+ }, 'class_type': 'CogVideoDecode', '_meta': {'title': 'CogVideo Decode'
35
+ }
36
+ }, '57': {'inputs': {'height': 480, 'width': 720, 'num_frames': 49, 'steps': 5, 'cfg': 6.0, 'seed': 660821088584312, 'scheduler': 'DPM++', 'denoise_strength': 1.0, 'pipeline': ['1',
37
+ 0
38
+ ], 'positive': ['30',
39
+ 0
40
+ ], 'negative': ['31',
41
+ 0
42
+ ], 'image_cond_latents': ['58',
43
+ 0
44
+ ]
45
+ }, 'class_type': 'CogVideoSampler', '_meta': {'title': 'CogVideo Sampler'
46
+ }
47
+ }, '58': {'inputs': {'chunk_size': 16, 'enable_tiling': True, 'pipeline': ['1',
48
+ 0
49
+ ], 'image': ['37',
50
+ 0
51
+ ]
52
+ }, 'class_type': 'CogVideoImageEncode', '_meta': {'title': 'CogVideo ImageEncode'
53
+ }
54
+ }, '73': {'inputs': {'url': 'https: //huggingface.co/spaces/gosign-de/comfyui-api/resolve/main/images/iStock_000014226797_Small.jpg', 'cache': True}, 'class_type': 'LoadImageByUrl //Browser', '_meta': {'title': 'Load Image By URL'}}, '75': {'inputs': {'images': ['73', 0]}, 'class_type': 'PreviewImage', '_meta': {'title': 'Preview Image'}}}, {'client_id': 'bb7de022-33ad-4c01-b272-bf2b1eb05e4e'}, ['75', '44']], 'outputs': {'44': {'gifs': [{'filename': 'CogVideoX-I2V_00003.mp4', 'subfolder': '', 'type': 'output', 'format': 'video/h264-mp4', 'frame_rate': 12.0}]}, '75': {'images': [{'filename': 'ComfyUI_temp_favdf_00001_.png', 'subfolder': '', 'type': 'temp'}]}}, 'status': {'status_str': 'success', 'completed': True, 'messages': [['execution_start', {'prompt_id': '2d7683a1-f132-4f75-bb02-ced4975a745b', 'timestamp': 1729158615147}], ['execution_cached', {'nodes': ['1', '20', '30', '31', '37', '58', '73', '75'], 'prompt_id': '2d7683a1-f132-4f75-bb02-ced4975a745b', 'timestamp': 1729158615162}], ['execution_success', {'prompt_id': '2d7683a1-f132-4f75-bb02-ced4975a745b', 'timestamp': 1729158723213}]]}, 'meta': {'44': {'node_id': '44', 'display_node': '44', 'parent_node': None, 'real_node_id': '44'}, '75': {'node_id': '75', 'display_node': '75', 'parent_node': None, 'real_node_id': '75'}}}
static/0e65b3a0-cc05-4f5c-8b20-d5854b2fd6d7.jpeg DELETED
Binary file (708 kB)
 
static/127a05b0-0395-45a5-a0c0-ad8ffad070b5.jpeg DELETED
Binary file (708 kB)
 
static/71a7077f-cd9c-4d9c-8da8-4975f8853bc5.jpeg DELETED
Binary file (708 kB)
 
static/eefb530f-7841-4050-9b04-c51444e1a6eb.jpeg DELETED
Binary file (708 kB)
 
workflows/cogvideox_image_to_video_workflow_api.json CHANGED
@@ -118,7 +118,7 @@
118
  "height": 480,
119
  "width": 720,
120
  "num_frames": 49,
121
- "steps": 10,
122
  "cfg": 6,
123
  "seed": 65334758276105,
124
  "scheduler": "DPM++",
 
118
  "height": 480,
119
  "width": 720,
120
  "num_frames": 49,
121
+ "steps": 2,
122
  "cfg": 6,
123
  "seed": 65334758276105,
124
  "scheduler": "DPM++",