File size: 3,046 Bytes
dc0538c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import time
import requests
from io import BytesIO
from pathlib import Path
from typing import List
import re
from flask import Flask, request, render_template, send_file

app = Flask(__name__)

class SentenceTokenizer:
    """Advanced sentence tokenizer with support for complex cases."""
    
    def __init__(self):
        self.SENTENCE_END = re.compile(
            r'(?<=[.!?])\s+(?=[A-Z])|(?<=[。!?])\s+',
            re.VERBOSE
        )

    def tokenize(self, text: str) -> List[str]:
        if not text or not text.strip():
            return []
        # Simple sentence splitting
        sentences = self.SENTENCE_END.split(text.strip())
        return [s.strip() for s in sentences if s.strip()]

def split_sentences(text: str) -> List[str]:
    tokenizer = SentenceTokenizer()
    return tokenizer.tokenize(text)

class ElevenlabsTTS:
    """Text-to-speech provider using Elevenlabs API."""
    
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({"User-Agent": "Mozilla/5.0"})
        self.cache_dir = Path("./audio_cache")
        self.all_voices = {
            "Brian": "nPczCjzI2devNBz1zQrb",
            "Alice": "Xb7hH8MSUJpSbSDYk0k2",
            # Add other voices as needed
        }
        self.params = {'allow_unauthenticated': '1'}

    def tts(self, text: str, voice: str = "Brian") -> str:
        if voice not in self.all_voices:
            raise ValueError(f"Voice '{voice}' not available")

        filename = self.cache_dir / f"{int(time.time())}.mp3"
        sentences = split_sentences(text)

        audio_chunks = {}
        for i, sentence in enumerate(sentences, 1):
            json_data = {'text': sentence, 'model_id': 'eleven_multilingual_v2'}
            response = self.session.post(
                f'https://api.elevenlabs.io/v1/text-to-speech/{self.all_voices[voice]}',
                params=self.params,
                json=json_data,
                timeout=20
            )
            response.raise_for_status()
            audio_chunks[i] = response.content

        self.cache_dir.mkdir(parents=True, exist_ok=True)
        combined_audio = BytesIO()
        for i in sorted(audio_chunks.keys()):
            combined_audio.write(audio_chunks[i])

        with open(filename, 'wb') as f:
            f.write(combined_audio.getvalue())
        return filename.as_posix()

# Web Interface
tts_provider = ElevenlabsTTS()

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        text = request.form.get('text')
        voice = request.form.get('voice', 'Brian')
        try:
            audio_file = tts_provider.tts(text, voice)
            return send_file(audio_file, mimetype='audio/mpeg', as_attachment=True)
        except Exception as e:
            return render_template('index.html', error=str(e), voices=tts_provider.all_voices.keys())
    return render_template('index.html', voices=tts_provider.all_voices.keys())

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)