diff --git a/links/templates/links/mini_apps/tts.html b/links/templates/links/mini_apps/tts.html index b9dac46..6eaae86 100644 --- a/links/templates/links/mini_apps/tts.html +++ b/links/templates/links/mini_apps/tts.html @@ -331,6 +331,52 @@ border-color: #9ca3af; } + /* Loading overlay styles */ + .loading-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(4px); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 9999; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0.3s ease; + } + + .loading-overlay.show { + opacity: 1; + visibility: visible; + } + + .loading-spinner { + width: 60px; + height: 60px; + border: 4px solid #e5e7eb; + border-top: 4px solid #8e44ad; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 1rem; + } + + .loading-text { + color: #374151; + font-size: 1.125rem; + font-weight: 600; + text-align: center; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + @media (max-width: 768px) { .tts-container { padding: 1rem; @@ -354,6 +400,15 @@ {% endblock %} {% block content %} + +
+
+
+
{% trans "Preparing Text-to-Speech..." %}
+
{% trans "Please wait while we process your text" %}
+
+
+
@@ -777,7 +832,140 @@ function downloadAudio() { // Auto-focus on text input when page loads document.addEventListener('DOMContentLoaded', function() { - document.getElementById('text-input').focus(); + const textInput = document.getElementById('text-input'); + + // Check for URL parameters + const urlParams = new URLSearchParams(window.location.search); + const textParam = urlParams.get('text'); + + if (textParam) { + // Show loading overlay + showLoadingOverlay(); + + // Decode and set the text + const decodedText = decodeURIComponent(textParam); + textInput.value = decodedText; + textInput.dispatchEvent(new Event('input')); // Update character counter + + // Auto-generate TTS after a short delay to ensure UI is ready + setTimeout(() => { + updateLoadingMessage('{% trans "Generating speech audio..." %}'); + generateSpeechFromURL(); + }, 500); + } else { + // Normal behavior - just focus the input + textInput.focus(); + } }); + +function showLoadingOverlay() { + const overlay = document.getElementById('loading-overlay'); + overlay.classList.add('show'); +} + +function hideLoadingOverlay() { + const overlay = document.getElementById('loading-overlay'); + overlay.classList.remove('show'); +} + +function updateLoadingMessage(message) { + const loadingMessage = document.getElementById('loading-message'); + loadingMessage.textContent = message; +} + +function generateSpeechFromURL() { + const textInput = document.getElementById('text-input'); + const text = textInput.value.trim(); + + if (!text) { + hideLoadingOverlay(); + alert('{% trans "No text found in URL parameter." %}'); + return; + } + + // Use the existing generateSpeech logic but with URL-specific handling + if (isLoading) return; + + isLoading = true; + const speakBtn = document.getElementById('speak-btn'); + const audioPlayer = document.getElementById('audio-player'); + const status = document.getElementById('audio-status'); + const playBtn = document.getElementById('play-pause-btn'); + + // Show audio player and update status (but keep loading overlay) + audioPlayer.style.display = 'block'; + status.textContent = '{% trans "Generating..." %}'; + status.className = 'audio-status status-generating'; + speakBtn.disabled = true; + speakBtn.innerHTML = '{% trans "Generating..." %}'; + playBtn.disabled = true; + + fetch('{% url "tts-api" %}', { + method: 'POST', + headers: { + 'X-CSRFToken': '{{ csrf_token }}', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + text: text + }) + }) + .then(response => { + if (!response.ok) { + throw new Error('TTS generation failed'); + } + return response.blob(); + }) + .then(blob => { + // Store the blob for download functionality + currentAudioBlob = blob; + + const audioUrl = URL.createObjectURL(blob); + const audio = document.getElementById('tts-audio'); + + audio.src = audioUrl; + currentAudio = audio; + + audio.addEventListener('loadedmetadata', function() { + status.textContent = '{% trans "Ready" %}'; + status.className = 'audio-status status-ready'; + playBtn.disabled = false; + document.getElementById('download-btn').disabled = false; + updateTotalTime(); + setupAudioEvents(); + + // Hide loading overlay - audio is ready + updateLoadingMessage('{% trans "Audio ready! Click play to start..." %}'); + setTimeout(() => { + hideLoadingOverlay(); + }, 500); + }); + + audio.addEventListener('error', function() { + status.textContent = '{% trans "Error" %}'; + status.className = 'audio-status status-error'; + playBtn.disabled = true; + document.getElementById('download-btn').disabled = true; + hideLoadingOverlay(); + alert('{% trans "Failed to generate audio. Please try again." %}'); + }); + + audio.load(); + }) + .catch(error => { + console.error('TTS Error:', error); + status.textContent = '{% trans "Failed" %}'; + status.className = 'audio-status status-error'; + playBtn.disabled = true; + document.getElementById('download-btn').disabled = true; + hideLoadingOverlay(); + alert('{% trans "Failed to generate audio. Please try again." %}'); + }) + .finally(() => { + isLoading = false; + speakBtn.disabled = false; + speakBtn.innerHTML = '{% trans "Speak" %}'; + }); +} {% endblock %}