(messageId, messageText)
| 1769 | } |
| 1770 | |
| 1771 | const handleTTSClick = async (messageId, messageText) => { |
| 1772 | if (isTTSLoading[messageId]) return |
| 1773 | |
| 1774 | if (isTTSPlaying[messageId] || ttsAudio[messageId]) { |
| 1775 | handleTTSStop(messageId) |
| 1776 | return |
| 1777 | } |
| 1778 | |
| 1779 | setTTSAction(true) |
| 1780 | |
| 1781 | // abort all ongoing streams and clear audio sources |
| 1782 | await handleTTSAbortAll() |
| 1783 | stopAllTTS() |
| 1784 | |
| 1785 | handleTTSStart({ chatMessageId: messageId, format: 'mp3' }) |
| 1786 | try { |
| 1787 | const abortController = new AbortController() |
| 1788 | setTtsStreamingState((prev) => ({ ...prev, abortController })) |
| 1789 | |
| 1790 | const response = await fetch('/api/v1/text-to-speech/generate', { |
| 1791 | method: 'POST', |
| 1792 | headers: { |
| 1793 | 'Content-Type': 'application/json', |
| 1794 | 'x-request-from': 'internal' |
| 1795 | }, |
| 1796 | credentials: 'include', |
| 1797 | signal: abortController.signal, |
| 1798 | body: JSON.stringify({ |
| 1799 | chatflowId: chatflowid, |
| 1800 | chatId: chatId, |
| 1801 | chatMessageId: messageId, |
| 1802 | text: messageText |
| 1803 | }) |
| 1804 | }) |
| 1805 | |
| 1806 | if (!response.ok) { |
| 1807 | throw new Error(`TTS request failed: ${response.status}`) |
| 1808 | } |
| 1809 | |
| 1810 | const reader = response.body.getReader() |
| 1811 | const decoder = new TextDecoder() |
| 1812 | let buffer = '' |
| 1813 | |
| 1814 | let done = false |
| 1815 | while (!done) { |
| 1816 | if (abortController.signal.aborted) { |
| 1817 | break |
| 1818 | } |
| 1819 | |
| 1820 | const result = await reader.read() |
| 1821 | done = result.done |
| 1822 | if (done) { |
| 1823 | break |
| 1824 | } |
| 1825 | const value = result.value |
| 1826 | const chunk = decoder.decode(value, { stream: true }) |
| 1827 | buffer += chunk |
| 1828 |
no test coverage detected