// Helper function to convert file to base64 function fileToBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { // Remove data:image/...;base64, prefix if present const base64 = reader.result.split(',')[1] || reader.result; resolve(base64); }; reader.onerror = reject; reader.readAsDataURL(file); }); } function genVideo(event) { event.preventDefault(); promptVideo(); } async function promptVideo() { const loader = document.getElementById("loader"); const input = document.getElementById("input"); const generateBtn = document.getElementById("generate-btn"); const resultDiv = document.getElementById("result"); const resultPlaceholder = document.getElementById("result-placeholder"); // Show loader and disable form loader.classList.remove("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } input.disabled = true; generateBtn.disabled = true; // Store the prompt for later restoration const prompt = input.value.trim(); if (!prompt) { alert("Please enter a prompt"); loader.classList.add("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "flex"; } input.disabled = false; generateBtn.disabled = false; return; } // Collect all form values const model = document.getElementById("video-model").value; const size = document.getElementById("video-size").value; const negativePrompt = document.getElementById("negative-prompt").value.trim(); // Parse size into width and height const sizeParts = size.split("x"); let width = 512; let height = 512; if (sizeParts.length === 2) { width = parseInt(sizeParts[0]) || 512; height = parseInt(sizeParts[1]) || 512; } // Video-specific parameters const secondsInput = document.getElementById("video-seconds").value.trim(); const seconds = secondsInput ? secondsInput : undefined; const fpsInput = document.getElementById("video-fps").value.trim(); const fps = fpsInput ? parseInt(fpsInput) : 16; const framesInput = document.getElementById("video-frames").value.trim(); const numFrames = framesInput ? parseInt(framesInput) : undefined; // Advanced parameters const stepInput = document.getElementById("video-steps").value.trim(); const step = stepInput ? parseInt(stepInput) : undefined; const seedInput = document.getElementById("video-seed").value.trim(); const seed = seedInput ? parseInt(seedInput) : undefined; const cfgScaleInput = document.getElementById("video-cfg-scale").value.trim(); const cfgScale = cfgScaleInput ? parseFloat(cfgScaleInput) : undefined; // Prepare request body const requestBody = { model: model, prompt: prompt, width: width, height: height, fps: fps, }; if (negativePrompt) { requestBody.negative_prompt = negativePrompt; } if (seconds !== undefined) { requestBody.seconds = seconds; } if (numFrames !== undefined) { requestBody.num_frames = numFrames; } if (step !== undefined) { requestBody.step = step; } if (seed !== undefined) { requestBody.seed = seed; } if (cfgScale !== undefined) { requestBody.cfg_scale = cfgScale; } // Handle file inputs try { // Start image (for img2video) const startImageInput = document.getElementById("start-image"); if (startImageInput.files.length > 0) { const base64 = await fileToBase64(startImageInput.files[0]); requestBody.start_image = base64; } // End image const endImageInput = document.getElementById("end-image"); if (endImageInput.files.length > 0) { const base64 = await fileToBase64(endImageInput.files[0]); requestBody.end_image = base64; } } catch (error) { console.error("Error processing image files:", error); resultDiv.innerHTML = '
Error processing image files: ' + error.message + '
'; loader.classList.add("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } input.disabled = false; generateBtn.disabled = false; return; } // Make API request try { const response = await fetch("v1/videos/generations", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(requestBody), }); const json = await response.json(); if (json.error) { // Display error resultDiv.innerHTML = 'Error: ' + json.error.message + '
'; loader.classList.add("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } input.disabled = false; generateBtn.disabled = false; return; } // Clear result div and hide placeholder resultDiv.innerHTML = ''; if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } // Display generated video if (json.data && json.data.length > 0) { json.data.forEach((item, index) => { const videoContainer = document.createElement("div"); videoContainer.className = "flex flex-col"; // Create video element const video = document.createElement("video"); video.controls = true; video.className = "w-full h-auto rounded-lg"; video.preload = "metadata"; if (item.url) { video.src = item.url; } else if (item.b64_json) { video.src = "data:video/mp4;base64," + item.b64_json; } else { return; // Skip invalid items } videoContainer.appendChild(video); // Create caption container const captionDiv = document.createElement("div"); captionDiv.className = "mt-2 p-2 bg-[var(--color-bg-secondary)] rounded-lg text-xs"; // Prompt caption const promptCaption = document.createElement("p"); promptCaption.className = "text-[var(--color-text-primary)] mb-1.5 break-words"; promptCaption.innerHTML = 'Prompt: ' + escapeHtml(prompt); captionDiv.appendChild(promptCaption); // Negative prompt if provided if (negativePrompt) { const negativeCaption = document.createElement("p"); negativeCaption.className = "text-[var(--color-text-secondary)] mb-1.5 break-words"; negativeCaption.innerHTML = 'Negative Prompt: ' + escapeHtml(negativePrompt); captionDiv.appendChild(negativeCaption); } // Generation details const detailsDiv = document.createElement("div"); detailsDiv.className = "flex flex-wrap gap-3 text-[10px] text-[var(--color-text-secondary)] mt-1.5"; detailsDiv.innerHTML = ` Size: ${width}x${height} ${fps ? `FPS: ${fps}` : ''} ${numFrames !== undefined ? `Frames: ${numFrames}` : ''} ${seconds !== undefined ? `Duration: ${seconds}s` : ''} ${step !== undefined ? `Steps: ${step}` : ''} ${seed !== undefined ? `Seed: ${seed}` : ''} ${cfgScale !== undefined ? `CFG Scale: ${cfgScale}` : ''} `; captionDiv.appendChild(detailsDiv); // Copy prompt button const copyBtn = document.createElement("button"); copyBtn.className = "mt-1.5 px-2 py-0.5 text-[10px] bg-[var(--color-primary)] text-white rounded hover:opacity-80"; copyBtn.innerHTML = 'Copy Prompt'; copyBtn.onclick = () => { navigator.clipboard.writeText(prompt).then(() => { copyBtn.innerHTML = 'Copied!'; setTimeout(() => { copyBtn.innerHTML = 'Copy Prompt'; }, 2000); }); }; captionDiv.appendChild(copyBtn); videoContainer.appendChild(captionDiv); resultDiv.appendChild(videoContainer); }); // Hide placeholder when videos are displayed if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } } else { resultDiv.innerHTML = 'No videos were generated.
'; if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } } } catch (error) { console.error("Error generating video:", error); resultDiv.innerHTML = 'Error: ' + error.message + '
'; if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } } finally { // Hide loader and re-enable form loader.classList.add("hidden"); input.disabled = false; generateBtn.disabled = false; input.focus(); } } // Helper function to escape HTML function escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } // Initialize document.addEventListener("DOMContentLoaded", function() { const input = document.getElementById("input"); const form = document.getElementById("genvideo"); if (input) { input.focus(); } if (form) { form.addEventListener("submit", genVideo); } // Handle Enter key press in the prompt input (but allow Shift+Enter for new lines) if (input) { input.addEventListener("keydown", function(event) { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); genVideo(event); } }); } // Hide loader initially const loader = document.getElementById("loader"); if (loader) { loader.classList.add("hidden"); } });