Update app.py
Browse files
app.py
CHANGED
@@ -232,9 +232,11 @@ def process_entry(entry, i, add_voiceover, target_language):
|
|
232 |
font="./NotoSansSC-Regular.ttf",
|
233 |
method='caption',
|
234 |
color='yellow',
|
235 |
-
|
|
|
|
|
236 |
size=(int(video.w * 0.8), None)
|
237 |
-
).with_start(entry["start"]).with_duration(entry["end"] - entry["start"]).with_position(('bottom')).with_opacity(0.
|
238 |
|
239 |
audio_segment = None
|
240 |
if add_voiceover:
|
@@ -248,54 +250,36 @@ def add_transcript_voiceover(video_path, translated_json, output_path, add_voice
|
|
248 |
"""
|
249 |
Add transcript and voiceover to a video, segment by segment.
|
250 |
"""
|
251 |
-
# Load the video file
|
252 |
video = VideoFileClip(video_path)
|
|
|
253 |
|
254 |
-
# Create text clips based on timestamps
|
255 |
text_clips = []
|
256 |
audio_segments = []
|
257 |
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
color='yellow',
|
275 |
-
font_size=subtitle_font_size,
|
276 |
-
size=(max_subtitle_width, None)
|
277 |
-
).with_start(entry["start"]).with_duration(entry["end"] - entry["start"]).with_position(('bottom')).with_opacity(0.7)
|
278 |
-
text_clips.append(txt_clip)
|
279 |
-
|
280 |
-
# Generate voiceover for this segment, if needed
|
281 |
-
if add_voiceover:
|
282 |
-
segment_audio_path = f"segment_{i}_voiceover.wav"
|
283 |
-
generate_voiceover([entry], target_language, segment_audio_path)
|
284 |
-
audio_segment = AudioFileClip(segment_audio_path).set_duration(entry["end"] - entry["start"])
|
285 |
-
audio_segments.append(audio_segment)
|
286 |
-
else:
|
287 |
-
raise ValueError(f"Invalid entry format: {entry}")
|
288 |
-
|
289 |
-
# Combine the text clips
|
290 |
final_video = CompositeVideoClip([video] + text_clips)
|
291 |
|
292 |
-
|
293 |
-
|
294 |
-
final_audio =
|
295 |
-
final_audio = final_audio.set_duration(video.duration)
|
296 |
final_video = final_video.set_audio(final_audio)
|
297 |
|
298 |
-
# Write the result to a file
|
299 |
logger.info(f"Saving the final video to: {output_path}")
|
300 |
final_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
|
301 |
|
|
|
232 |
font="./NotoSansSC-Regular.ttf",
|
233 |
method='caption',
|
234 |
color='yellow',
|
235 |
+
stroke_color='black', # Border color
|
236 |
+
stroke_width=2, # Border thickness
|
237 |
+
font_size=int(video.h // 20),
|
238 |
size=(int(video.w * 0.8), None)
|
239 |
+
).with_start(entry["start"]).with_duration(entry["end"] - entry["start"]).with_position(('bottom')).with_opacity(0.8)
|
240 |
|
241 |
audio_segment = None
|
242 |
if add_voiceover:
|
|
|
250 |
"""
|
251 |
Add transcript and voiceover to a video, segment by segment.
|
252 |
"""
|
|
|
253 |
video = VideoFileClip(video_path)
|
254 |
+
font_path = "./NotoSansSC-Regular.ttf"
|
255 |
|
|
|
256 |
text_clips = []
|
257 |
audio_segments = []
|
258 |
|
259 |
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
260 |
+
futures = [executor.submit(process_entry, entry, i, video.w, video.h, font_path, add_voiceover, target_language)
|
261 |
+
for i, entry in enumerate(translated_json)]
|
262 |
+
|
263 |
+
for future in concurrent.futures.as_completed(futures):
|
264 |
+
try:
|
265 |
+
txt_clip, audio_segment = future.result()
|
266 |
+
text_clips.append(txt_clip)
|
267 |
+
if add_voiceover and audio_segment:
|
268 |
+
audio_segments.append(audio_segment)
|
269 |
+
except Exception as e:
|
270 |
+
logger.error(f"Error processing entry: {e}")
|
271 |
+
|
272 |
+
# Sort text clips and audio segments based on their start times
|
273 |
+
text_clips.sort(key=lambda clip: clip.start)
|
274 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
275 |
final_video = CompositeVideoClip([video] + text_clips)
|
276 |
|
277 |
+
if add_voiceover and audio_segments:
|
278 |
+
audio_segments.sort(key=lambda segment: segment.start)
|
279 |
+
final_audio = concatenate_audioclips(audio_segments)
|
280 |
+
final_audio = final_audio.set_duration(video.duration)
|
281 |
final_video = final_video.set_audio(final_audio)
|
282 |
|
|
|
283 |
logger.info(f"Saving the final video to: {output_path}")
|
284 |
final_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
|
285 |
|