File size: 3,473 Bytes
fdd0daa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
90
91
92
93
94
95
96
97
98
document.addEventListener('DOMContentLoaded', () => {
    const textInput = document.getElementById('text-input');
    const charCurrent = document.getElementById('char-current');
    const voiceSelect = document.getElementById('voice-select');
    const generateBtn = document.getElementById('generate-btn');
    const btnText = document.querySelector('.btn-text');
    const btnLoader = document.querySelector('.btn-loader');
    const errorMessage = document.getElementById('error-message');
    const errorText = document.getElementById('error-text');
    const audioContainer = document.getElementById('audio-container');
    const audioPlayer = document.getElementById('audio-player');
    const downloadBtn = document.getElementById('download-btn');

    const MAX_CHARS = 1000;

    // Character counter
    textInput.addEventListener('input', () => {
        const text = textInput.value;
        const length = text.length;

        if (length > MAX_CHARS) {
            textInput.value = text.substring(0, MAX_CHARS);
            charCurrent.textContent = MAX_CHARS;
            charCurrent.style.color = 'var(--danger)';
        } else {
            charCurrent.textContent = length;
            charCurrent.style.color = 'var(--text-muted)';
        }
    });

    // Generate Button Click
    generateBtn.addEventListener('click', async () => {
        const text = textInput.value.trim();
        const voice = voiceSelect.value;

        if (!text) {
            showError("Vui lòng nhập văn bản tiếng Việt.");
            textInput.focus();
            return;
        }

        // Hide old errors/audio
        errorMessage.classList.add('hidden');
        audioContainer.classList.add('hidden');

        // Setup Loading State
        btnLoader.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Processing...';
        setLoading(true);

        try {
            const response = await fetch('/api/synthesize', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ text, voice })
            });

            const data = await response.json();

            if (!response.ok) {
                throw new Error(data.error || 'Failed to generate speech.');
            }

            // Success
            const uniqueUrl = data.audio_url + '?t=' + new Date().getTime(); // Prevent caching
            audioPlayer.src = uniqueUrl;
            downloadBtn.href = uniqueUrl;
            downloadBtn.download = `Vietnamese_TTS_${Date.now()}.mp3`;

            audioContainer.classList.remove('hidden');
            audioPlayer.play().catch(e => console.log("Auto-play blocked", e));

        } catch (error) {
            showError(error.message);
        } finally {
            setLoading(false);
        }
    });

    function setLoading(isLoading) {
        if (isLoading) {
            generateBtn.disabled = true;
            btnText.classList.add('hidden');
            btnLoader.classList.remove('hidden');
        } else {
            generateBtn.disabled = false;
            btnText.classList.remove('hidden');
            btnLoader.classList.add('hidden');
        }
    }

    function showError(message) {
        errorText.textContent = message;
        errorMessage.classList.remove('hidden');
    }
});