VoiceClips commited on
Commit
845c48a
·
verified ·
1 Parent(s): 068ff59

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -6
app.py CHANGED
@@ -32,6 +32,10 @@ class TranslationJob(BaseModel):
32
  supabase_anon_key: Optional[str] = None
33
  gemini_api_key: Optional[str] = None
34
  elevenlabs_api_key: Optional[str] = None
 
 
 
 
35
 
36
  def load_env_local():
37
  """Load environment variables from local .env.local file if it exists."""
@@ -374,11 +378,18 @@ def run_production_pipeline(job: TranslationJob, gemini_key: str, elevenlabs_key
374
  # 1.5. Standardize video (H.264/AAC, 25 FPS, YUV420p) for OpenCV / Wav2Lip stability
375
  print(f"[{video_id}] Standardizing downloaded video to MP4 format...")
376
  cmd_standardize = [
377
- "ffmpeg", "-y", "-i", downloaded_video_path,
 
 
 
 
 
 
 
378
  "-c:v", "libx264", "-pix_fmt", "yuv420p", "-r", "25",
379
  "-c:a", "aac", "-ar", "16000", "-ac", "1",
380
  input_video_path
381
- ]
382
  subprocess.run(cmd_standardize, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
383
 
384
  # 2. Extract audio from video using FFmpeg
@@ -438,11 +449,53 @@ def run_production_pipeline(job: TranslationJob, gemini_key: str, elevenlabs_key
438
 
439
  # 4. Synthesize voice cloning using ElevenLabs API
440
  print(f"[{video_id}] Calling ElevenLabs TTS Voice Cloning with tone '{job.voice_tone}'...")
441
- # We can use the default standard voice (e.g. Rachel: 21m00Tcm4TlvDq8ikWAM)
442
- voice_id = "21m00Tcm4TlvDq8ikWAM"
443
- elevenlabs_url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}"
444
 
445
  elevenlabs_headers = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
  "xi-api-key": elevenlabs_key,
447
  "Content-Type": "application/json"
448
  }
@@ -466,12 +519,25 @@ def run_production_pipeline(job: TranslationJob, gemini_key: str, elevenlabs_key
466
  }
467
  }
468
 
469
- tts_res = requests.post(elevenlabs_url, headers=elevenlabs_headers, json=elevenlabs_payload)
470
  tts_res.raise_for_status()
471
 
472
  with open(synthesized_audio_path, "wb") as f:
473
  f.write(tts_res.content)
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
  # 5. Merge synthesized audio back with the original video (or run Lip-Sync if selected)
476
  lipsync_output_path = f"temp_{video_id}_lipsync.mp4"
477
 
 
32
  supabase_anon_key: Optional[str] = None
33
  gemini_api_key: Optional[str] = None
34
  elevenlabs_api_key: Optional[str] = None
35
+ use_voice_cloning: Optional[bool] = True
36
+ default_voice_id: Optional[str] = 'Xb7hH8MSUJpSbSDYk0k2'
37
+ trim_start: Optional[float] = None
38
+ trim_end: Optional[float] = None
39
 
40
  def load_env_local():
41
  """Load environment variables from local .env.local file if it exists."""
 
378
  # 1.5. Standardize video (H.264/AAC, 25 FPS, YUV420p) for OpenCV / Wav2Lip stability
379
  print(f"[{video_id}] Standardizing downloaded video to MP4 format...")
380
  cmd_standardize = [
381
+ "ffmpeg", "-y", "-i", downloaded_video_path
382
+ ]
383
+ if job.trim_start is not None and job.trim_start > 0:
384
+ cmd_standardize.extend(["-ss", str(job.trim_start)])
385
+ if job.trim_end is not None and job.trim_end > 0:
386
+ cmd_standardize.extend(["-to", str(job.trim_end)])
387
+
388
+ cmd_standardize.extend([
389
  "-c:v", "libx264", "-pix_fmt", "yuv420p", "-r", "25",
390
  "-c:a", "aac", "-ar", "16000", "-ac", "1",
391
  input_video_path
392
+ ])
393
  subprocess.run(cmd_standardize, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
394
 
395
  # 2. Extract audio from video using FFmpeg
 
449
 
450
  # 4. Synthesize voice cloning using ElevenLabs API
451
  print(f"[{video_id}] Calling ElevenLabs TTS Voice Cloning with tone '{job.voice_tone}'...")
 
 
 
452
 
453
  elevenlabs_headers = {
454
+ "xi-api-key": elevenlabs_key
455
+ }
456
+
457
+ voice_id = None
458
+ cloned_voice_created = False
459
+
460
+ # Try to dynamically clone the voice using the extracted audio if requested
461
+ if job.use_voice_cloning:
462
+ try:
463
+ print(f"[{video_id}] Attempting to create a temporary voice clone from extracted audio...")
464
+ add_voice_url = "https://api.elevenlabs.io/v1/voices/add"
465
+
466
+ # Open audio file for upload
467
+ with open(extracted_audio_path, "rb") as audio_file:
468
+ files = {
469
+ "files": (os.path.basename(extracted_audio_path), audio_file, "audio/mpeg")
470
+ }
471
+ data = {
472
+ "name": f"VoiceClips_{video_id}",
473
+ "description": f"Temporary cloned voice for job {video_id}"
474
+ }
475
+
476
+ add_res = requests.post(add_voice_url, headers=elevenlabs_headers, files=files, data=data)
477
+
478
+ if add_res.status_code == 200:
479
+ voice_id = add_res.json().get("voice_id")
480
+ cloned_voice_created = True
481
+ print(f"[{video_id}] Voice clone created successfully. Voice ID: {voice_id}")
482
+ else:
483
+ print(f"[{video_id}] Voice cloning failed with status code {add_res.status_code}: {add_res.text}")
484
+ print(f"[{video_id}] Falling back to standard pre-made voice ({job.default_voice_id}).")
485
+ except Exception as clone_err:
486
+ print(f"[{video_id}] Exception during voice cloning: {clone_err}")
487
+ print(f"[{video_id}] Falling back to standard pre-made voice ({job.default_voice_id}).")
488
+ else:
489
+ print(f"[{video_id}] Voice cloning disabled by user. Using standard pre-made voice ({job.default_voice_id}).")
490
+
491
+ # Fallback to default voice if cloning wasn't successful
492
+ if not voice_id:
493
+ voice_id = job.default_voice_id or "Xb7hH8MSUJpSbSDYk0k2" # Alice (pre-made voice, works on free tier)
494
+
495
+ elevenlabs_url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}"
496
+
497
+ # Content-Type header is needed for the JSON payload of the TTS request
498
+ tts_headers = {
499
  "xi-api-key": elevenlabs_key,
500
  "Content-Type": "application/json"
501
  }
 
519
  }
520
  }
521
 
522
+ tts_res = requests.post(elevenlabs_url, headers=tts_headers, json=elevenlabs_payload)
523
  tts_res.raise_for_status()
524
 
525
  with open(synthesized_audio_path, "wb") as f:
526
  f.write(tts_res.content)
527
 
528
+ # If we successfully created a temporary cloned voice, delete it now to free up slots
529
+ if cloned_voice_created and voice_id:
530
+ try:
531
+ print(f"[{video_id}] Deleting temporary cloned voice {voice_id}...")
532
+ delete_url = f"https://api.elevenlabs.io/v1/voices/{voice_id}"
533
+ del_res = requests.delete(delete_url, headers=elevenlabs_headers)
534
+ if del_res.status_code == 200:
535
+ print(f"[{video_id}] Temporary cloned voice deleted successfully.")
536
+ else:
537
+ print(f"[{video_id}] Failed to delete voice: {del_res.text}")
538
+ except Exception as del_err:
539
+ print(f"[{video_id}] Exception deleting voice: {del_err}")
540
+
541
  # 5. Merge synthesized audio back with the original video (or run Lip-Sync if selected)
542
  lipsync_output_path = f"temp_{video_id}_lipsync.mp4"
543