|
import time |
|
import requests |
|
import pathlib |
|
from io import BytesIO |
|
from flask import Flask, request, jsonify, send_file |
|
from concurrent.futures import ThreadPoolExecutor, as_completed |
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
ELEVENLABS_API_URL = "https://api.elevenlabs.io/v1/text-to-speech" |
|
HEADERS = {"User-Agent": "TTSApp"} |
|
|
|
CACHE_DIR = pathlib.Path("/tmp/audio_cache") |
|
CACHE_DIR.mkdir(parents=True, exist_ok=True) |
|
|
|
|
|
|
|
ALL_VOICES = { |
|
"Brian": "nPczCjzI2devNBz1zQrb", |
|
"Alice": "Xb7hH8MSUJpSbSDYk0k2", |
|
"Will": "bIHbv24MWmeRgasZH58o", |
|
} |
|
|
|
|
|
def split_sentences(text): |
|
return text.split(". ") |
|
|
|
|
|
def generate_audio(text, voice): |
|
if voice not in ALL_VOICES: |
|
return {"error": f"Invalid voice '{voice}'"} |
|
|
|
filename = CACHE_DIR / f"{int(time.time())}.mp3" |
|
sentences = split_sentences(text) |
|
|
|
def fetch_audio(sentence, part_number): |
|
try: |
|
response = requests.post( |
|
f"{ELEVENLABS_API_URL}/{ALL_VOICES[voice]}", |
|
headers=HEADERS, |
|
json={"text": sentence, "model_id": "eleven_multilingual_v2"}, |
|
timeout=20 |
|
) |
|
response.raise_for_status() |
|
return part_number, response.content |
|
except requests.RequestException: |
|
return part_number, None |
|
|
|
audio_chunks = {} |
|
with ThreadPoolExecutor() as executor: |
|
futures = {executor.submit(fetch_audio, sentence.strip(), i): i for i, sentence in enumerate(sentences)} |
|
|
|
for future in as_completed(futures): |
|
part_number, audio_data = future.result() |
|
if audio_data: |
|
audio_chunks[part_number] = audio_data |
|
|
|
combined_audio = BytesIO() |
|
for part_number in sorted(audio_chunks.keys()): |
|
combined_audio.write(audio_chunks[part_number]) |
|
|
|
with open(filename, "wb") as f: |
|
f.write(combined_audio.getvalue()) |
|
|
|
return filename.as_posix() |
|
|
|
|
|
@app.route("/") |
|
def home(): |
|
return ''' |
|
<h1>Text-to-Speech API</h1> |
|
<form action="/tts" method="post"> |
|
<label>Text:</label> |
|
<input type="text" name="text" required> |
|
<label>Voice:</label> |
|
<select name="voice"> |
|
<option value="Brian">Brian</option> |
|
<option value="Alice">Alice</option> |
|
<option value="Will">Will</option> |
|
</select> |
|
<button type="submit">Generate</button> |
|
</form> |
|
''' |
|
|
|
@app.route("/tts", methods=["POST"]) |
|
def tts(): |
|
text = request.form.get("text") |
|
voice = request.form.get("voice", "Brian") |
|
|
|
if not text: |
|
return jsonify({"error": "Text is required!"}) |
|
|
|
audio_file = generate_audio(text, voice) |
|
return send_file(audio_file, as_attachment=True) |
|
|
|
if __name__ == "__main__": |
|
app.run(debug=True, host="0.0.0.0", port=5000) |
|
|