arifardev commited on
Commit
0229683
·
verified ·
1 Parent(s): 52fd92f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -0
app.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 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, JSONResponse
8
+ from TTS.api import TTS
9
+
10
+ # --- KONFIGURASI FOLDER TEMP ---
11
+ TEMP_DIR = "/tmp/coqui_data"
12
+ AUDIO_DIR = "/tmp/audio_output"
13
+ os.makedirs(TEMP_DIR, exist_ok=True)
14
+ os.makedirs(AUDIO_DIR, exist_ok=True)
15
+
16
+ # Paksa environment variable untuk simpan modul bahasa ke /tmp
17
+ os.environ["TTS_HOME"] = TEMP_DIR
18
+
19
+ app = FastAPI(
20
+ title="Coqui TTS API Interaktif",
21
+ description="API lengkap untuk Coqui TTS dengan otentikasi. Semua cache dan unduhan diarahkan ke /tmp."
22
+ )
23
+ security = HTTPBasic()
24
+
25
+ # --- OTENTIKASI ---
26
+ def verify_auth(credentials: HTTPBasicCredentials = Depends(security)):
27
+ # Username default: admin
28
+ correct_username = secrets.compare_digest(credentials.username, "admin")
29
+ correct_password = secrets.compare_digest(credentials.password, "Rahasia1234")
30
+ if not (correct_username and correct_password):
31
+ raise HTTPException(
32
+ status_code=status.HTTP_401_UNAUTHORIZED,
33
+ detail="Autentikasi gagal. Cek username dan password.",
34
+ headers={"WWW-Authenticate": "Basic"},
35
+ )
36
+ return credentials.username
37
+
38
+ # --- HELPER FUNCTION ---
39
+ def get_tts_instance(model_name: str) -> TTS:
40
+ """Load model secara dinamis (akan download ke /tmp jika belum ada)"""
41
+ try:
42
+ # Gunakan CPU jika Space gratis (tidak ada GPU)
43
+ return TTS(model_name=model_name, progress_bar=False, gpu=False)
44
+ except Exception as e:
45
+ raise HTTPException(status_code=500, detail=f"Gagal memuat model: {str(e)}")
46
+
47
+ # --- ENDPOINTS ---
48
+
49
+ @app.get("/")
50
+ def root():
51
+ return {"message": "Coqui TTS API Aktif. Akses /docs untuk interaksi UI API."}
52
+
53
+ @app.get("/models", tags=["Info"])
54
+ def list_models(username: str = Depends(verify_auth)):
55
+ """Mengambil daftar semua model TTS dan Voice Conversion yang tersedia."""
56
+ try:
57
+ models = TTS().list_models()
58
+ return {"models": models}
59
+ except Exception as e:
60
+ raise HTTPException(status_code=500, detail=str(e))
61
+
62
+ @app.post("/tts", tags=["Generation"])
63
+ def generate_tts(
64
+ text: str = Form(..., description="Teks yang akan diubah menjadi suara"),
65
+ model_name: str = Form("tts_models/en/ljspeech/vits", description="Nama model dari endpoint /models"),
66
+ speaker: str = Form(None, description="Nama speaker (jika model multi-speaker)"),
67
+ language: str = Form(None, description="Kode bahasa (jika model multi-lingual)"),
68
+ username: str = Depends(verify_auth)
69
+ ):
70
+ """Sintesis teks ke suara dasar."""
71
+ tts = get_tts_instance(model_name)
72
+ output_path = os.path.join(AUDIO_DIR, f"tts_{uuid.uuid4().hex}.wav")
73
+
74
+ try:
75
+ tts.tts_to_file(text=text, speaker=speaker, language=language, file_path=output_path)
76
+ return FileResponse(output_path, media_type="audio/wav", filename="output.wav")
77
+ except Exception as e:
78
+ raise HTTPException(status_code=500, detail=f"Error sintesis TTS: {str(e)}")
79
+
80
+ @app.post("/tts_voice_clone", tags=["Generation"])
81
+ def generate_tts_voice_clone(
82
+ text: str = Form(...),
83
+ model_name: str = Form("tts_models/multilingual/multi-dataset/xtts_v2"),
84
+ language: str = Form("id", description="Kode bahasa output (misal: 'id', 'en')"),
85
+ reference_audio: UploadFile = File(..., description="File audio WAV singkat untuk di-clone suaranya"),
86
+ username: str = Depends(verify_auth)
87
+ ):
88
+ """Sintesis teks dengan meniru suara dari file audio referensi (Zero-shot Voice Cloning)."""
89
+ tts = get_tts_instance(model_name)
90
+
91
+ # Simpan file audio referensi ke /tmp
92
+ ref_path = os.path.join(AUDIO_DIR, f"ref_{uuid.uuid4().hex}.wav")
93
+ with open(ref_path, "wb") as buffer:
94
+ shutil.copyfileobj(reference_audio.file, buffer)
95
+
96
+ output_path = os.path.join(AUDIO_DIR, f"clone_{uuid.uuid4().hex}.wav")
97
+
98
+ try:
99
+ tts.tts_to_file(
100
+ text=text,
101
+ language=language,
102
+ speaker_wav=ref_path,
103
+ file_path=output_path
104
+ )
105
+ return FileResponse(output_path, media_type="audio/wav", filename="cloned_output.wav")
106
+ except Exception as e:
107
+ raise HTTPException(status_code=500, detail=f"Error Voice Cloning: {str(e)}")
108
+
109
+ @app.post("/voice_conversion", tags=["Generation"])
110
+ def voice_conversion(
111
+ source_audio: UploadFile = File(..., description="Audio sumber yang ingin diubah suaranya"),
112
+ reference_audio: UploadFile = File(..., description="Audio target/referensi (suara yang akan ditiru)"),
113
+ model_name: str = Form("voice_conversion_models/multilingual/vctk/freevc24"),
114
+ username: str = Depends(verify_auth)
115
+ ):
116
+ """Mengubah suara dari satu file audio menjadi suara di file audio referensi (Audio-to-Audio)."""
117
+ tts = get_tts_instance(model_name)
118
+
119
+ source_path = os.path.join(AUDIO_DIR, f"src_{uuid.uuid4().hex}.wav")
120
+ ref_path = os.path.join(AUDIO_DIR, f"ref_vc_{uuid.uuid4().hex}.wav")
121
+ output_path = os.path.join(AUDIO_DIR, f"vc_{uuid.uuid4().hex}.wav")
122
+
123
+ with open(source_path, "wb") as buffer:
124
+ shutil.copyfileobj(source_audio.file, buffer)
125
+ with open(ref_path, "wb") as buffer:
126
+ shutil.copyfileobj(reference_audio.file, buffer)
127
+
128
+ try:
129
+ tts.voice_conversion_to_file(
130
+ source_wav=source_path,
131
+ target_wav=ref_path,
132
+ file_path=output_path
133
+ )
134
+ return FileResponse(output_path, media_type="audio/wav", filename="converted_output.wav")
135
+ except Exception as e:
136
+ raise HTTPException(status_code=500, detail=f"Error Voice Conversion: {str(e)}")