dinhhan commited on
Commit
82c406c
·
verified ·
1 Parent(s): 25494c5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +52 -86
app.py CHANGED
@@ -15,13 +15,59 @@ load_dotenv()
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
@@ -45,7 +91,7 @@ def lipsync_api_call(video_url, audio_url):
45
  "output_format": "mp4",
46
  "sync_mode": "bounce",
47
  "fps": 24,
48
- "output_resolution": [1280, 720]
49
  }
50
  }
51
 
@@ -60,94 +106,14 @@ def lipsync_api_call(video_url, audio_url):
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),
118
- '-map', '0:v', '-map', '1:a',
119
- '-c:v', 'copy', '-c:a', 'aac',
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,
127
- '-t', str(audio_duration),
128
- '-map', '0:v', '-map', '1:a',
129
- '-c:v', 'copy', '-c:a', 'aac',
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}")
@@ -162,7 +128,7 @@ def create_video_from_image(image_url, output_path, duration=10):
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
 
 
15
  B_KEY = os.getenv("B_KEY")
16
 
17
  # URLs
18
+ API_URL = "https://api.sync.so/v2/generate"
19
+
20
+ def get_media_resolution(url):
21
+ print(f"\n[DEBUG] Getting resolution for: {url}")
22
+
23
+ # Download the file to a temporary location
24
+ response = requests.get(url)
25
+ if response.status_code != 200:
26
+ print(f"[ERROR] Failed to download media. Status code: {response.status_code}")
27
+ return None
28
+
29
+ temp_path = f"temp_media_{uuid.uuid4()}"
30
+ with open(temp_path, 'wb') as f:
31
+ f.write(response.content)
32
+
33
+ # Get resolution using FFprobe
34
+ cmd = [
35
+ 'ffprobe',
36
+ '-v', 'error',
37
+ '-select_streams', 'v:0',
38
+ '-show_entries', 'stream=width,height',
39
+ '-of', 'json',
40
+ temp_path
41
+ ]
42
+
43
+ try:
44
+ result = subprocess.run(cmd, capture_output=True, text=True)
45
+ os.remove(temp_path) # Clean up temp file
46
+
47
+ if result.returncode == 0:
48
+ data = json.loads(result.stdout)
49
+ if 'streams' in data and data['streams']:
50
+ width = data['streams'][0].get('width')
51
+ height = data['streams'][0].get('height')
52
+ if width and height:
53
+ print(f"[DEBUG] Detected resolution: {width}x{height}")
54
+ return [width, height]
55
+ except Exception as e:
56
+ print(f"[ERROR] Failed to get resolution: {str(e)}")
57
+ if os.path.exists(temp_path):
58
+ os.remove(temp_path)
59
+
60
+ print("[DEBUG] Failed to detect resolution, using default")
61
+ return [1280, 720] # Default resolution
62
 
63
  def lipsync_api_call(video_url, audio_url):
64
  print(f"\n[DEBUG] Starting lipsync_api_call")
65
  print(f"[DEBUG] Video URL: {video_url}")
66
  print(f"[DEBUG] Audio URL: {audio_url}")
67
 
68
+ # Get the resolution of the input video/image
69
+ resolution = get_media_resolution(video_url)
70
+
71
  headers = {
72
  "Content-Type": "application/json",
73
  "x-api-key": B_KEY
 
91
  "output_format": "mp4",
92
  "sync_mode": "bounce",
93
  "fps": 24,
94
+ "output_resolution": resolution
95
  }
96
  }
97
 
 
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}")
112
  print(f"[DEBUG] Output path: {output_path}")
113
 
114
+ # Get the resolution before creating the video
115
+ resolution = get_media_resolution(image_url)
116
+
117
  response = requests.get(image_url)
118
  if response.status_code != 200:
119
  print(f"[ERROR] Failed to download image. Status code: {response.status_code}")
 
128
  cmd = [
129
  'ffmpeg', '-loop', '1', '-i', temp_image_path,
130
  '-c:v', 'libx264', '-t', str(duration), '-pix_fmt', 'yuv420p',
131
+ '-vf', f'scale={resolution[0]}:{resolution[1]}',
132
  '-y', output_path
133
  ]
134