arifardev commited on
Commit
41d1149
·
verified ·
1 Parent(s): 0850bc4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -14
app.py CHANGED
@@ -2,6 +2,7 @@ import os
2
  import uuid
3
  import shutil
4
  import secrets
 
5
  from fastapi import FastAPI, Depends, HTTPException, status, File, UploadFile, Form
6
  from fastapi.security import HTTPBasic, HTTPBasicCredentials
7
  from fastapi.responses import FileResponse
@@ -13,11 +14,10 @@ AUDIO_DIR = "/tmp/audio_output"
13
  os.makedirs(TEMP_DIR, exist_ok=True)
14
  os.makedirs(AUDIO_DIR, exist_ok=True)
15
 
16
- # Paksa semua unduhan model masuk ke /tmp agar sesuai dengan penyimpanan ephemeral
17
  os.environ["TTS_HOME"] = TEMP_DIR
18
 
19
- # --- PERSETUJUAN LISENSI OTOMATIS ---
20
- # Baris ini mencegah munculnya prompt persetujuan di terminal yang menyebabkan error EOF
21
  os.environ["COQUI_TOS_AGREED"] = "1"
22
 
23
  app = FastAPI(
@@ -26,16 +26,18 @@ app = FastAPI(
26
  )
27
  security = HTTPBasic()
28
 
29
- # Global variable untuk lazy loading agar proses startup kontainer tidak mengalami timeout
30
  tts_model = None
31
 
32
  def get_xtts_instance() -> TTS:
33
  global tts_model
34
  if tts_model is None:
35
  try:
36
- # Memuat XTTS-v2 secara lokal menggunakan CPU
37
  tts_model = TTS(model_name="tts_models/multilingual/multi-dataset/xtts_v2", gpu=False)
 
38
  except Exception as e:
 
 
39
  raise HTTPException(status_code=500, detail=f"Gagal memuat model XTTS-v2: {str(e)}")
40
  return tts_model
41
 
@@ -59,35 +61,52 @@ def root():
59
 
60
  @app.get("/speakers", tags=["Info"])
61
  def list_speakers(username: str = Depends(verify_auth)):
62
- """Melihat daftar nama karakter suara (speakers) bawaan XTTS-v2 yang bisa digunakan."""
63
  tts = get_xtts_instance()
64
  return {"speakers": tts.speakers}
65
 
66
  @app.post("/tts", tags=["Generation"])
67
  def generate_tts(
68
  text: str = Form(..., description="Teks Syarat & Ketentuan PasBlast"),
69
- speaker: str = Form("Ana Mendes", description="Pilih nama speaker dari endpoint /speakers"),
70
  language: str = Form("id", description="Gunakan 'id' untuk Bahasa Indonesia"),
71
  username: str = Depends(verify_auth)
72
  ):
73
- """Sintesis teks menggunakan suara karakter bawaan XTTS-v2."""
74
  tts = get_xtts_instance()
75
  output_path = os.path.join(AUDIO_DIR, f"tts_{uuid.uuid4().hex}.wav")
76
 
 
 
 
 
 
 
77
  try:
78
- tts.tts_to_file(text=text, speaker=speaker, language=language, file_path=output_path)
 
 
 
 
 
 
 
 
 
79
  return FileResponse(output_path, media_type="audio/wav", filename="pasblast_normal.wav")
80
  except Exception as e:
81
- raise HTTPException(status_code=500, detail=f"Gagal memproses TTS: {str(e)}")
 
 
82
 
83
  @app.post("/tts_voice_clone", tags=["Generation"])
84
  def generate_tts_voice_clone(
85
  text: str = Form(...),
86
  language: str = Form("id"),
87
- reference_audio: UploadFile = File(..., description="File WAV suara target (durasi 5-10 detik) untuk ditiru"),
88
  username: str = Depends(verify_auth)
89
  ):
90
- """Sintesis teks dengan meniru (cloning) suara dari file audio yang Anda unggah."""
91
  tts = get_xtts_instance()
92
 
93
  ref_path = os.path.join(AUDIO_DIR, f"ref_{uuid.uuid4().hex}.wav")
@@ -97,7 +116,16 @@ def generate_tts_voice_clone(
97
  output_path = os.path.join(AUDIO_DIR, f"clone_{uuid.uuid4().hex}.wav")
98
 
99
  try:
100
- tts.tts_to_file(text=text, language=language, speaker_wav=ref_path, file_path=output_path)
 
 
 
 
 
 
 
101
  return FileResponse(output_path, media_type="audio/wav", filename="pasblast_cloned.wav")
102
  except Exception as e:
103
- raise HTTPException(status_code=500, detail=f"Gagal melakukan Voice Cloning: {str(e)}")
 
 
 
2
  import uuid
3
  import shutil
4
  import secrets
5
+ import traceback
6
  from fastapi import FastAPI, Depends, HTTPException, status, File, UploadFile, Form
7
  from fastapi.security import HTTPBasic, HTTPBasicCredentials
8
  from fastapi.responses import FileResponse
 
14
  os.makedirs(TEMP_DIR, exist_ok=True)
15
  os.makedirs(AUDIO_DIR, exist_ok=True)
16
 
17
+ # Paksa semua unduhan model masuk ke /tmp
18
  os.environ["TTS_HOME"] = TEMP_DIR
19
 
20
+ # Persetujuan Lisensi Otomatis
 
21
  os.environ["COQUI_TOS_AGREED"] = "1"
22
 
23
  app = FastAPI(
 
26
  )
27
  security = HTTPBasic()
28
 
 
29
  tts_model = None
30
 
31
  def get_xtts_instance() -> TTS:
32
  global tts_model
33
  if tts_model is None:
34
  try:
35
+ print("Memuat model XTTS-v2 ke RAM...")
36
  tts_model = TTS(model_name="tts_models/multilingual/multi-dataset/xtts_v2", gpu=False)
37
+ print("Model berhasil dimuat ke memori!")
38
  except Exception as e:
39
+ error_trace = traceback.format_exc()
40
+ print(f"Error Load Model: {error_trace}")
41
  raise HTTPException(status_code=500, detail=f"Gagal memuat model XTTS-v2: {str(e)}")
42
  return tts_model
43
 
 
61
 
62
  @app.get("/speakers", tags=["Info"])
63
  def list_speakers(username: str = Depends(verify_auth)):
64
+ """Melihat daftar karakter suara bawaan XTTS-v2."""
65
  tts = get_xtts_instance()
66
  return {"speakers": tts.speakers}
67
 
68
  @app.post("/tts", tags=["Generation"])
69
  def generate_tts(
70
  text: str = Form(..., description="Teks Syarat & Ketentuan PasBlast"),
71
+ speaker: str = Form("Ana Florence", description="Pilih nama speaker"),
72
  language: str = Form("id", description="Gunakan 'id' untuk Bahasa Indonesia"),
73
  username: str = Depends(verify_auth)
74
  ):
75
+ """Sintesis teks dengan proteksi panjang karakter dan validasi speaker."""
76
  tts = get_xtts_instance()
77
  output_path = os.path.join(AUDIO_DIR, f"tts_{uuid.uuid4().hex}.wav")
78
 
79
+ # 1. Validasi Speaker (Pencegah Error 500)
80
+ available_speakers = tts.speakers
81
+ if speaker not in available_speakers:
82
+ print(f"Peringatan: Speaker '{speaker}' tidak ada. Menggunakan default: '{available_speakers[0]}'")
83
+ speaker = available_speakers[0]
84
+
85
  try:
86
+ print(f"Memulai sintesis suara untuk teks ({len(text)} karakter)...")
87
+ # 2. split_sentences=True wajib aktif untuk mencegah crash pada teks panjang
88
+ tts.tts_to_file(
89
+ text=text,
90
+ speaker=speaker,
91
+ language=language,
92
+ file_path=output_path,
93
+ split_sentences=True
94
+ )
95
+ print("Sintesis berhasil, mengirim file audio ke klien.")
96
  return FileResponse(output_path, media_type="audio/wav", filename="pasblast_normal.wav")
97
  except Exception as e:
98
+ error_trace = traceback.format_exc()
99
+ print(f"FATAL ERROR saat sintesis: {error_trace}")
100
+ raise HTTPException(status_code=500, detail=f"Gagal memproses TTS: {str(e)}\nTrace: {error_trace}")
101
 
102
  @app.post("/tts_voice_clone", tags=["Generation"])
103
  def generate_tts_voice_clone(
104
  text: str = Form(...),
105
  language: str = Form("id"),
106
+ reference_audio: UploadFile = File(..., description="File WAV suara target (5-10 detik)"),
107
  username: str = Depends(verify_auth)
108
  ):
109
+ """Sintesis teks dengan meniru (cloning) suara dari file audio unggahan."""
110
  tts = get_xtts_instance()
111
 
112
  ref_path = os.path.join(AUDIO_DIR, f"ref_{uuid.uuid4().hex}.wav")
 
116
  output_path = os.path.join(AUDIO_DIR, f"clone_{uuid.uuid4().hex}.wav")
117
 
118
  try:
119
+ print("Memulai proses kloning suara...")
120
+ tts.tts_to_file(
121
+ text=text,
122
+ language=language,
123
+ speaker_wav=ref_path,
124
+ file_path=output_path,
125
+ split_sentences=True
126
+ )
127
  return FileResponse(output_path, media_type="audio/wav", filename="pasblast_cloned.wav")
128
  except Exception as e:
129
+ error_trace = traceback.format_exc()
130
+ print(f"FATAL ERROR saat kloning: {error_trace}")
131
+ raise HTTPException(status_code=500, detail=f"Gagal melakukan Voice Cloning: {str(e)}\nTrace: {error_trace}")