Update app.py
Browse files
app.py
CHANGED
@@ -106,6 +106,89 @@ def lipsync_api_call(video_url, audio_url):
|
|
106 |
print(f"[ERROR] API call failed: {str(e)}")
|
107 |
raise
|
108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
def create_video_from_image(image_url, output_path, duration=10):
|
110 |
print(f"\n[DEBUG] Creating video from image")
|
111 |
print(f"[DEBUG] Image URL: {image_url}")
|
@@ -252,7 +335,6 @@ def process_video(video_url, audio_url, progress=gr.Progress()):
|
|
252 |
|
253 |
def create_interface():
|
254 |
css = """
|
255 |
-
|
256 |
"""
|
257 |
with gr.Blocks(css=css) as app:
|
258 |
gr.Markdown("# Lipsync Video Generator")
|
|
|
106 |
print(f"[ERROR] API call failed: {str(e)}")
|
107 |
raise
|
108 |
|
109 |
+
def check_job_status(job_id):
|
110 |
+
print(f"\n[DEBUG] Checking job status for ID: {job_id}")
|
111 |
+
headers = {"x-api-key": B_KEY}
|
112 |
+
max_attempts = 3000
|
113 |
+
attempt = 0
|
114 |
+
|
115 |
+
while attempt < max_attempts:
|
116 |
+
try:
|
117 |
+
response = requests.get(f"{API_URL}/{job_id}", headers=headers)
|
118 |
+
print(f"[DEBUG] Status check attempt {attempt + 1}")
|
119 |
+
print(f"[DEBUG] Status response: {response.text}")
|
120 |
+
|
121 |
+
data = response.json()
|
122 |
+
status = data.get("status")
|
123 |
+
print(f"[DEBUG] Current status: {status}")
|
124 |
+
|
125 |
+
if status == "COMPLETED":
|
126 |
+
print(f"[DEBUG] Job completed. Output URL: {data.get('outputUrl')}")
|
127 |
+
return data.get("outputUrl")
|
128 |
+
elif status == "FAILED" or status == "CANCELED":
|
129 |
+
print(f"[ERROR] Job failed or was canceled. Error: {data.get('error')}")
|
130 |
+
return None
|
131 |
+
|
132 |
+
attempt += 1
|
133 |
+
time.sleep(10)
|
134 |
+
|
135 |
+
except Exception as e:
|
136 |
+
print(f"[ERROR] Status check failed: {str(e)}")
|
137 |
+
return None
|
138 |
+
|
139 |
+
print("[ERROR] Max attempts reached")
|
140 |
+
return None
|
141 |
+
|
142 |
+
def get_media_duration(file_path):
|
143 |
+
print(f"\n[DEBUG] Getting duration for: {file_path}")
|
144 |
+
cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', file_path]
|
145 |
+
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
146 |
+
duration = float(result.stdout.strip())
|
147 |
+
print(f"[DEBUG] Media duration: {duration} seconds")
|
148 |
+
return duration
|
149 |
+
|
150 |
+
def combine_audio_video(video_path, audio_path, output_path):
|
151 |
+
print(f"\n[DEBUG] Combining audio and video")
|
152 |
+
print(f"[DEBUG] Video path: {video_path}")
|
153 |
+
print(f"[DEBUG] Audio path: {audio_path}")
|
154 |
+
print(f"[DEBUG] Output path: {output_path}")
|
155 |
+
|
156 |
+
video_duration = get_media_duration(video_path)
|
157 |
+
audio_duration = get_media_duration(audio_path)
|
158 |
+
|
159 |
+
if video_duration > audio_duration:
|
160 |
+
print("[DEBUG] Video longer than audio - trimming video")
|
161 |
+
cmd = [
|
162 |
+
'ffmpeg', '-i', video_path, '-i', audio_path,
|
163 |
+
'-t', str(audio_duration),
|
164 |
+
'-map', '0:v', '-map', '1:a',
|
165 |
+
'-c:v', 'copy', '-c:a', 'aac',
|
166 |
+
'-y', output_path
|
167 |
+
]
|
168 |
+
else:
|
169 |
+
print("[DEBUG] Audio longer than video - looping video")
|
170 |
+
loop_count = int(audio_duration // video_duration) + 1
|
171 |
+
cmd = [
|
172 |
+
'ffmpeg', '-stream_loop', str(loop_count), '-i', video_path, '-i', audio_path,
|
173 |
+
'-t', str(audio_duration),
|
174 |
+
'-map', '0:v', '-map', '1:a',
|
175 |
+
'-c:v', 'copy', '-c:a', 'aac',
|
176 |
+
'-shortest', '-y', output_path
|
177 |
+
]
|
178 |
+
|
179 |
+
print(f"[DEBUG] FFmpeg command: {' '.join(cmd)}")
|
180 |
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
181 |
+
print(f"[DEBUG] FFmpeg stdout: {result.stdout}")
|
182 |
+
print(f"[DEBUG] FFmpeg stderr: {result.stderr}")
|
183 |
+
|
184 |
+
def is_image_url(url):
|
185 |
+
parsed = urlparse(url)
|
186 |
+
path = parsed.path.lower()
|
187 |
+
result = path.endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff', '.webp', '.heic', '.svg', '.ico'))
|
188 |
+
print(f"\n[DEBUG] Checking if URL is image: {url}")
|
189 |
+
print(f"[DEBUG] Result: {result}")
|
190 |
+
return result
|
191 |
+
|
192 |
def create_video_from_image(image_url, output_path, duration=10):
|
193 |
print(f"\n[DEBUG] Creating video from image")
|
194 |
print(f"[DEBUG] Image URL: {image_url}")
|
|
|
335 |
|
336 |
def create_interface():
|
337 |
css = """
|
|
|
338 |
"""
|
339 |
with gr.Blocks(css=css) as app:
|
340 |
gr.Markdown("# Lipsync Video Generator")
|