dinhhan commited on
Commit
f9ec5ab
·
verified ·
1 Parent(s): e0936b5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -34
app.py CHANGED
@@ -15,54 +15,103 @@ load_dotenv()
15
  B_KEY = os.getenv("B_KEY")
16
 
17
  # URLs
18
- API_URL = os.getenv("API_URL")
19
- UPLOAD_URL = os.getenv("UPLOAD_URL") # Make sure to add this to your .env file
20
 
21
  def lipsync_api_call(video_url, audio_url):
 
 
 
 
22
  headers = {
23
  "Content-Type": "application/json",
24
  "x-api-key": B_KEY
25
  }
26
 
27
  data = {
28
- "audioUrl": audio_url,
29
- "videoUrl": video_url,
30
- "maxCredits": 1000,
31
  "model": "lipsync-1.8.0-beta",
32
- "synergize": True,
33
- "pads": [0, 5, 0, 0],
34
- "synergizerStrength": 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
 
37
- response = requests.post(API_URL, headers=headers, data=json.dumps(data))
38
- return response.json()
 
 
 
 
 
 
 
 
39
 
40
  def check_job_status(job_id):
 
41
  headers = {"x-api-key": B_KEY}
42
- max_attempts = 3000 # Limit the number of attempts
 
43
 
44
- for _ in range(max_attempts):
45
- response = requests.get(f"{API_URL}/{job_id}", headers=headers)
46
- data = response.json()
47
-
48
- if data["status"] == "COMPLETED":
49
- return data["videoUrl"]
50
- elif data["status"] == "FAILED":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  return None
52
-
53
- time.sleep(10)
54
  return None
55
 
56
  def get_media_duration(file_path):
 
57
  cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', file_path]
58
  result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
59
- return float(result.stdout.strip())
 
 
60
 
61
  def combine_audio_video(video_path, audio_path, output_path):
 
 
 
 
 
62
  video_duration = get_media_duration(video_path)
63
  audio_duration = get_media_duration(audio_path)
64
 
65
  if video_duration > audio_duration:
 
66
  cmd = [
67
  'ffmpeg', '-i', video_path, '-i', audio_path,
68
  '-t', str(audio_duration),
@@ -71,6 +120,7 @@ def combine_audio_video(video_path, audio_path, output_path):
71
  '-y', output_path
72
  ]
73
  else:
 
74
  loop_count = int(audio_duration // video_duration) + 1
75
  cmd = [
76
  'ffmpeg', '-stream_loop', str(loop_count), '-i', video_path, '-i', audio_path,
@@ -80,54 +130,84 @@ def combine_audio_video(video_path, audio_path, output_path):
80
  '-shortest', '-y', output_path
81
  ]
82
 
83
- subprocess.run(cmd, check=True)
 
 
 
84
 
85
  def is_image_url(url):
86
  parsed = urlparse(url)
87
  path = parsed.path.lower()
88
- return path.endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff', '.webp', '.heic', '.svg', '.ico'))
 
 
 
89
 
90
  def create_video_from_image(image_url, output_path, duration=10):
91
- # Download the image
 
 
 
92
  response = requests.get(image_url)
93
  if response.status_code != 200:
 
94
  raise Exception("Failed to download the image")
95
 
96
  temp_image_path = f"temp_image_{uuid.uuid4()}.jpg"
 
 
97
  with open(temp_image_path, 'wb') as f:
98
  f.write(response.content)
99
 
100
- # Create a 10-second video from the image
101
  cmd = [
102
  'ffmpeg', '-loop', '1', '-i', temp_image_path,
103
  '-c:v', 'libx264', '-t', str(duration), '-pix_fmt', 'yuv420p',
104
  '-vf', 'scale=trunc(iw/2)*2:trunc(ih/2)*2',
105
  '-y', output_path
106
  ]
107
- subprocess.run(cmd, check=True)
108
 
109
- # Clean up the temporary image file
 
 
 
 
110
  os.remove(temp_image_path)
 
111
 
112
  return output_path
113
 
114
  def upload_file(file_path):
 
 
115
  with open(file_path, 'rb') as file:
116
  files = {'fileToUpload': (os.path.basename(file_path), file)}
117
  data = {'reqtype': 'fileupload'}
118
- response = requests.post(UPLOAD_URL, files=files, data=data)
119
-
120
- if response.status_code == 200:
121
- return response.text.strip()
122
- return None
 
 
 
 
 
 
123
 
124
  def process_video(video_url, audio_url, progress=gr.Progress()):
 
 
 
 
125
  if not audio_url:
 
126
  return None, "No audio URL provided"
127
  if not video_url:
 
128
  return None, "No video URL provided"
129
 
130
  session_id = str(uuid.uuid4())
 
131
 
132
  progress(0.2, desc="Processing media...")
133
 
@@ -146,27 +226,36 @@ def process_video(video_url, audio_url, progress=gr.Progress()):
146
  job_data = lipsync_api_call(video_url, audio_url)
147
 
148
  if "error" in job_data or "message" in job_data:
149
- raise Exception(job_data.get("error", job_data.get("message", "Unknown error")))
 
 
150
 
151
  job_id = job_data["id"]
 
152
 
153
  progress(0.6, desc="Processing lipsync...")
154
  result_url = check_job_status(job_id)
155
 
156
  if result_url:
157
  progress(0.9, desc="Downloading result...")
 
158
  response = requests.get(result_url)
159
  output_path = f"output_{session_id}.mp4"
 
160
  with open(output_path, "wb") as f:
161
  f.write(response.content)
 
 
162
  progress(1.0, desc="Complete!")
163
  return output_path, "Lipsync completed successfully!"
164
  else:
165
  raise Exception("Lipsync processing failed or timed out")
166
 
167
  except Exception as e:
 
168
  progress(0.8, desc="Falling back to simple combination...")
169
  try:
 
170
  video_response = requests.get(video_url)
171
  temp_video_path = f"temp_video_{session_id}.mp4"
172
  with open(temp_video_path, "wb") as f:
@@ -186,6 +275,7 @@ def process_video(video_url, audio_url, progress=gr.Progress()):
186
  progress(1.0, desc="Complete!")
187
  return output_path, f"Used fallback method. Original error: {str(e)}"
188
  except Exception as fallback_error:
 
189
  return None, f"All methods failed. Error: {str(fallback_error)}"
190
 
191
  def create_interface():
@@ -193,7 +283,7 @@ def create_interface():
193
 
194
  """
195
  with gr.Blocks(css=css) as app:
196
- gr.Markdown("# JSON 2")
197
  with gr.Row():
198
  with gr.Column():
199
  video_url_input = gr.Textbox(label="Video or Image URL")
@@ -212,5 +302,6 @@ def create_interface():
212
  return app
213
 
214
  if __name__ == "__main__":
 
215
  app = create_interface()
216
  app.launch()
 
15
  B_KEY = os.getenv("B_KEY")
16
 
17
  # URLs
18
+ API_URL = "https://api.sync.so/v2/generate" # Updated API endpoint
 
19
 
20
  def lipsync_api_call(video_url, audio_url):
21
+ print(f"\n[DEBUG] Starting lipsync_api_call")
22
+ print(f"[DEBUG] Video URL: {video_url}")
23
+ print(f"[DEBUG] Audio URL: {audio_url}")
24
+
25
  headers = {
26
  "Content-Type": "application/json",
27
  "x-api-key": B_KEY
28
  }
29
 
30
  data = {
 
 
 
31
  "model": "lipsync-1.8.0-beta",
32
+ "input": [
33
+ {
34
+ "type": "video",
35
+ "url": video_url
36
+ },
37
+ {
38
+ "type": "audio",
39
+ "url": audio_url
40
+ }
41
+ ],
42
+ "options": {
43
+ "pads": [0, 5, 0, 0],
44
+ "speedup": 1,
45
+ "output_format": "mp4",
46
+ "sync_mode": "bounce",
47
+ "fps": 24,
48
+ "output_resolution": [1280, 720]
49
+ }
50
  }
51
 
52
+ print(f"[DEBUG] Request payload: {json.dumps(data, indent=2)}")
53
+
54
+ try:
55
+ response = requests.post(API_URL, headers=headers, data=json.dumps(data))
56
+ print(f"[DEBUG] API Response status code: {response.status_code}")
57
+ print(f"[DEBUG] API Response: {response.text}")
58
+ return response.json()
59
+ except Exception as e:
60
+ print(f"[ERROR] API call failed: {str(e)}")
61
+ raise
62
 
63
  def check_job_status(job_id):
64
+ print(f"\n[DEBUG] Checking job status for ID: {job_id}")
65
  headers = {"x-api-key": B_KEY}
66
+ max_attempts = 3000
67
+ attempt = 0
68
 
69
+ while attempt < max_attempts:
70
+ try:
71
+ response = requests.get(f"{API_URL}/{job_id}", headers=headers)
72
+ print(f"[DEBUG] Status check attempt {attempt + 1}")
73
+ print(f"[DEBUG] Status response: {response.text}")
74
+
75
+ data = response.json()
76
+ status = data.get("status")
77
+ print(f"[DEBUG] Current status: {status}")
78
+
79
+ if status == "COMPLETED":
80
+ print(f"[DEBUG] Job completed. Output URL: {data.get('outputUrl')}")
81
+ return data.get("outputUrl")
82
+ elif status == "FAILED" or status == "CANCELED":
83
+ print(f"[ERROR] Job failed or was canceled. Error: {data.get('error')}")
84
+ return None
85
+
86
+ attempt += 1
87
+ time.sleep(10)
88
+
89
+ except Exception as e:
90
+ print(f"[ERROR] Status check failed: {str(e)}")
91
  return None
92
+
93
+ print("[ERROR] Max attempts reached")
94
  return None
95
 
96
  def get_media_duration(file_path):
97
+ print(f"\n[DEBUG] Getting duration for: {file_path}")
98
  cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', file_path]
99
  result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
100
+ duration = float(result.stdout.strip())
101
+ print(f"[DEBUG] Media duration: {duration} seconds")
102
+ return duration
103
 
104
  def combine_audio_video(video_path, audio_path, output_path):
105
+ print(f"\n[DEBUG] Combining audio and video")
106
+ print(f"[DEBUG] Video path: {video_path}")
107
+ print(f"[DEBUG] Audio path: {audio_path}")
108
+ print(f"[DEBUG] Output path: {output_path}")
109
+
110
  video_duration = get_media_duration(video_path)
111
  audio_duration = get_media_duration(audio_path)
112
 
113
  if video_duration > audio_duration:
114
+ print("[DEBUG] Video longer than audio - trimming video")
115
  cmd = [
116
  'ffmpeg', '-i', video_path, '-i', audio_path,
117
  '-t', str(audio_duration),
 
120
  '-y', output_path
121
  ]
122
  else:
123
+ print("[DEBUG] Audio longer than video - looping video")
124
  loop_count = int(audio_duration // video_duration) + 1
125
  cmd = [
126
  'ffmpeg', '-stream_loop', str(loop_count), '-i', video_path, '-i', audio_path,
 
130
  '-shortest', '-y', output_path
131
  ]
132
 
133
+ print(f"[DEBUG] FFmpeg command: {' '.join(cmd)}")
134
+ result = subprocess.run(cmd, capture_output=True, text=True)
135
+ print(f"[DEBUG] FFmpeg stdout: {result.stdout}")
136
+ print(f"[DEBUG] FFmpeg stderr: {result.stderr}")
137
 
138
  def is_image_url(url):
139
  parsed = urlparse(url)
140
  path = parsed.path.lower()
141
+ result = path.endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff', '.webp', '.heic', '.svg', '.ico'))
142
+ print(f"\n[DEBUG] Checking if URL is image: {url}")
143
+ print(f"[DEBUG] Result: {result}")
144
+ return result
145
 
146
  def create_video_from_image(image_url, output_path, duration=10):
147
+ print(f"\n[DEBUG] Creating video from image")
148
+ print(f"[DEBUG] Image URL: {image_url}")
149
+ print(f"[DEBUG] Output path: {output_path}")
150
+
151
  response = requests.get(image_url)
152
  if response.status_code != 200:
153
+ print(f"[ERROR] Failed to download image. Status code: {response.status_code}")
154
  raise Exception("Failed to download the image")
155
 
156
  temp_image_path = f"temp_image_{uuid.uuid4()}.jpg"
157
+ print(f"[DEBUG] Temporary image path: {temp_image_path}")
158
+
159
  with open(temp_image_path, 'wb') as f:
160
  f.write(response.content)
161
 
 
162
  cmd = [
163
  'ffmpeg', '-loop', '1', '-i', temp_image_path,
164
  '-c:v', 'libx264', '-t', str(duration), '-pix_fmt', 'yuv420p',
165
  '-vf', 'scale=trunc(iw/2)*2:trunc(ih/2)*2',
166
  '-y', output_path
167
  ]
 
168
 
169
+ print(f"[DEBUG] FFmpeg command: {' '.join(cmd)}")
170
+ result = subprocess.run(cmd, capture_output=True, text=True)
171
+ print(f"[DEBUG] FFmpeg stdout: {result.stdout}")
172
+ print(f"[DEBUG] FFmpeg stderr: {result.stderr}")
173
+
174
  os.remove(temp_image_path)
175
+ print(f"[DEBUG] Temporary image removed")
176
 
177
  return output_path
178
 
179
  def upload_file(file_path):
180
+ print(f"\n[DEBUG] Uploading file: {file_path}")
181
+
182
  with open(file_path, 'rb') as file:
183
  files = {'fileToUpload': (os.path.basename(file_path), file)}
184
  data = {'reqtype': 'fileupload'}
185
+ try:
186
+ response = requests.post(UPLOAD_URL, files=files, data=data)
187
+ print(f"[DEBUG] Upload response status code: {response.status_code}")
188
+ print(f"[DEBUG] Upload response: {response.text}")
189
+
190
+ if response.status_code == 200:
191
+ return response.text.strip()
192
+ return None
193
+ except Exception as e:
194
+ print(f"[ERROR] File upload failed: {str(e)}")
195
+ return None
196
 
197
  def process_video(video_url, audio_url, progress=gr.Progress()):
198
+ print(f"\n[DEBUG] Starting video processing")
199
+ print(f"[DEBUG] Video URL: {video_url}")
200
+ print(f"[DEBUG] Audio URL: {audio_url}")
201
+
202
  if not audio_url:
203
+ print("[ERROR] No audio URL provided")
204
  return None, "No audio URL provided"
205
  if not video_url:
206
+ print("[ERROR] No video URL provided")
207
  return None, "No video URL provided"
208
 
209
  session_id = str(uuid.uuid4())
210
+ print(f"[DEBUG] Session ID: {session_id}")
211
 
212
  progress(0.2, desc="Processing media...")
213
 
 
226
  job_data = lipsync_api_call(video_url, audio_url)
227
 
228
  if "error" in job_data or "message" in job_data:
229
+ error_msg = job_data.get("error", job_data.get("message", "Unknown error"))
230
+ print(f"[ERROR] API error: {error_msg}")
231
+ raise Exception(error_msg)
232
 
233
  job_id = job_data["id"]
234
+ print(f"[DEBUG] Job ID: {job_id}")
235
 
236
  progress(0.6, desc="Processing lipsync...")
237
  result_url = check_job_status(job_id)
238
 
239
  if result_url:
240
  progress(0.9, desc="Downloading result...")
241
+ print(f"[DEBUG] Downloading from: {result_url}")
242
  response = requests.get(result_url)
243
  output_path = f"output_{session_id}.mp4"
244
+
245
  with open(output_path, "wb") as f:
246
  f.write(response.content)
247
+
248
+ print(f"[DEBUG] Result saved to: {output_path}")
249
  progress(1.0, desc="Complete!")
250
  return output_path, "Lipsync completed successfully!"
251
  else:
252
  raise Exception("Lipsync processing failed or timed out")
253
 
254
  except Exception as e:
255
+ print(f"[ERROR] Main process failed: {str(e)}")
256
  progress(0.8, desc="Falling back to simple combination...")
257
  try:
258
+ print("[DEBUG] Attempting fallback method")
259
  video_response = requests.get(video_url)
260
  temp_video_path = f"temp_video_{session_id}.mp4"
261
  with open(temp_video_path, "wb") as f:
 
275
  progress(1.0, desc="Complete!")
276
  return output_path, f"Used fallback method. Original error: {str(e)}"
277
  except Exception as fallback_error:
278
+ print(f"[ERROR] Fallback method failed: {str(fallback_error)}")
279
  return None, f"All methods failed. Error: {str(fallback_error)}"
280
 
281
  def create_interface():
 
283
 
284
  """
285
  with gr.Blocks(css=css) as app:
286
+ gr.Markdown("# Lipsync Video Generator")
287
  with gr.Row():
288
  with gr.Column():
289
  video_url_input = gr.Textbox(label="Video or Image URL")
 
302
  return app
303
 
304
  if __name__ == "__main__":
305
+ print("[DEBUG] Starting application")
306
  app = create_interface()
307
  app.launch()