Spaces:
Running
Running
| // API base URL | |
| const API_BASE = ''; | |
| // DOM elements | |
| const videoForm = document.getElementById('videoForm'); | |
| const scenesContainer = document.getElementById('scenesContainer'); | |
| const addSceneBtn = document.getElementById('addScene'); | |
| const statusDiv = document.getElementById('status'); | |
| const videosListDiv = document.getElementById('videosList'); | |
| let sceneCount = 1; | |
| // Add new scene | |
| addSceneBtn.addEventListener('click', () => { | |
| sceneCount++; | |
| const sceneDiv = document.createElement('div'); | |
| sceneDiv.className = 'scene'; | |
| sceneDiv.innerHTML = ` | |
| <div class="form-group"> | |
| <label>Scene ${sceneCount} - Text to Narrate</label> | |
| <textarea class="scene-text input" rows="3" placeholder="Enter the narration text..." required></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label>Search Keywords (comma separated)</label> | |
| <input type="text" class="scene-keywords input" placeholder="nature, forest, trees" required> | |
| </div> | |
| `; | |
| scenesContainer.appendChild(sceneDiv); | |
| }); | |
| // Submit form | |
| videoForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| // Gather scenes | |
| const scenes = Array.from(document.querySelectorAll('.scene')).map(scene => { | |
| const text = scene.querySelector('.scene-text').value; | |
| const keywords = scene.querySelector('.scene-keywords').value; | |
| return { | |
| text, | |
| searchTerms: keywords.split(',').map(k => k.trim()).filter(k => k) | |
| }; | |
| }); | |
| // Gather config | |
| const config = { | |
| orientation: document.getElementById('orientation').value, | |
| voice: document.getElementById('voice').value, | |
| music: document.getElementById('music').value || null, | |
| musicVolume: document.getElementById('musicVolume').value, | |
| captionPosition: document.getElementById('captionPosition').value, | |
| paddingBack: 0 | |
| }; | |
| // Show processing status | |
| showStatus('Creating video... This may take a few minutes.', 'processing'); | |
| try { | |
| const response = await fetch(`${API_BASE}/api/short-video`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ scenes, config }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Failed to create video'); | |
| } | |
| const data = await response.json(); | |
| const videoId = data.videoId; | |
| showStatus(`Video queued! ID: ${videoId}`, 'success'); | |
| // Start polling for status | |
| pollVideoStatus(videoId); | |
| // Refresh video list | |
| setTimeout(loadVideos, 1000); | |
| } catch (error) { | |
| showStatus(`Error: ${error.message}`, 'error'); | |
| } | |
| }); | |
| // Poll video status | |
| async function pollVideoStatus(videoId) { | |
| const maxAttempts = 120; // 10 minutes | |
| let attempts = 0; | |
| const interval = setInterval(async () => { | |
| attempts++; | |
| if (attempts > maxAttempts) { | |
| clearInterval(interval); | |
| showStatus('Video processing timeout', 'error'); | |
| return; | |
| } | |
| try { | |
| const response = await fetch(`${API_BASE}/api/short-video/${videoId}/status`); | |
| const data = await response.json(); | |
| if (data.status === 'ready') { | |
| clearInterval(interval); | |
| showStatus('Video ready! Check your videos list below.', 'success'); | |
| loadVideos(); | |
| } else if (data.status === 'failed') { | |
| clearInterval(interval); | |
| showStatus('Video processing failed', 'error'); | |
| loadVideos(); | |
| } | |
| } catch (error) { | |
| console.error('Error polling status:', error); | |
| } | |
| }, 5000); // Poll every 5 seconds | |
| } | |
| // Show status message | |
| function showStatus(message, type) { | |
| statusDiv.textContent = message; | |
| statusDiv.className = `status ${type}`; | |
| statusDiv.classList.remove('hidden'); | |
| } | |
| // Load videos list | |
| async function loadVideos() { | |
| try { | |
| const response = await fetch(`${API_BASE}/api/short-videos`); | |
| const data = await response.json(); | |
| if (data.videos.length === 0) { | |
| videosListDiv.innerHTML = '<p class="loading">No videos yet. Create one above!</p>'; | |
| return; | |
| } | |
| videosListDiv.innerHTML = data.videos.map(video => ` | |
| <div class="video-card"> | |
| <h3>Video ID: ${video.id}</h3> | |
| <span class="video-status ${video.status}">${video.status.toUpperCase()}</span> | |
| <div class="video-actions"> | |
| ${video.status === 'ready' ? ` | |
| <a href="${API_BASE}/api/short-video/${video.id}" download class="btn btn-primary">Download</a> | |
| ` : ''} | |
| <button onclick="deleteVideo('${video.id}')" class="btn btn-secondary">Delete</button> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } catch (error) { | |
| console.error('Error loading videos:', error); | |
| videosListDiv.innerHTML = '<p class="loading">Error loading videos</p>'; | |
| } | |
| } | |
| // Delete video | |
| async function deleteVideo(videoId) { | |
| if (!confirm('Are you sure you want to delete this video?')) { | |
| return; | |
| } | |
| try { | |
| await fetch(`${API_BASE}/api/short-video/${videoId}`, { | |
| method: 'DELETE' | |
| }); | |
| loadVideos(); | |
| } catch (error) { | |
| alert('Error deleting video'); | |
| } | |
| } | |
| // Load videos on page load | |
| loadVideos(); | |
| // Auto-refresh videos list every 10 seconds | |
| setInterval(loadVideos, 10000); | |