| | |
| | function fileToBase64(file) { |
| | return new Promise((resolve, reject) => { |
| | const reader = new FileReader(); |
| | reader.onload = () => { |
| | |
| | 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"); |
| |
|
| | |
| | loader.classList.remove("hidden"); |
| | if (resultPlaceholder) { |
| | resultPlaceholder.style.display = "none"; |
| | } |
| | input.disabled = true; |
| | generateBtn.disabled = true; |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | const model = document.getElementById("video-model").value; |
| | const size = document.getElementById("video-size").value; |
| | const negativePrompt = document.getElementById("negative-prompt").value.trim(); |
| | |
| | |
| | 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; |
| | } |
| |
|
| | |
| | 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; |
| |
|
| | |
| | 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; |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | try { |
| | |
| | const startImageInput = document.getElementById("start-image"); |
| | if (startImageInput.files.length > 0) { |
| | const base64 = await fileToBase64(startImageInput.files[0]); |
| | requestBody.start_image = base64; |
| | } |
| |
|
| | |
| | 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 = '<p class="text-xs text-red-500 p-2">Error processing image files: ' + error.message + '</p>'; |
| | loader.classList.add("hidden"); |
| | if (resultPlaceholder) { |
| | resultPlaceholder.style.display = "none"; |
| | } |
| | input.disabled = false; |
| | generateBtn.disabled = false; |
| | return; |
| | } |
| |
|
| | |
| | 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) { |
| | |
| | resultDiv.innerHTML = '<p class="text-xs text-red-500 p-2">Error: ' + json.error.message + '</p>'; |
| | loader.classList.add("hidden"); |
| | if (resultPlaceholder) { |
| | resultPlaceholder.style.display = "none"; |
| | } |
| | input.disabled = false; |
| | generateBtn.disabled = false; |
| | return; |
| | } |
| |
|
| | |
| | resultDiv.innerHTML = ''; |
| | if (resultPlaceholder) { |
| | resultPlaceholder.style.display = "none"; |
| | } |
| |
|
| | |
| | if (json.data && json.data.length > 0) { |
| | json.data.forEach((item, index) => { |
| | const videoContainer = document.createElement("div"); |
| | videoContainer.className = "flex flex-col"; |
| |
|
| | |
| | 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; |
| | } |
| | |
| | videoContainer.appendChild(video); |
| |
|
| | |
| | const captionDiv = document.createElement("div"); |
| | captionDiv.className = "mt-2 p-2 bg-[var(--color-bg-secondary)] rounded-lg text-xs"; |
| |
|
| | |
| | const promptCaption = document.createElement("p"); |
| | promptCaption.className = "text-[var(--color-text-primary)] mb-1.5 break-words"; |
| | promptCaption.innerHTML = '<strong>Prompt:</strong> ' + escapeHtml(prompt); |
| | captionDiv.appendChild(promptCaption); |
| |
|
| | |
| | if (negativePrompt) { |
| | const negativeCaption = document.createElement("p"); |
| | negativeCaption.className = "text-[var(--color-text-secondary)] mb-1.5 break-words"; |
| | negativeCaption.innerHTML = '<strong>Negative Prompt:</strong> ' + escapeHtml(negativePrompt); |
| | captionDiv.appendChild(negativeCaption); |
| | } |
| |
|
| | |
| | const detailsDiv = document.createElement("div"); |
| | detailsDiv.className = "flex flex-wrap gap-3 text-[10px] text-[var(--color-text-secondary)] mt-1.5"; |
| | detailsDiv.innerHTML = ` |
| | <span><strong>Size:</strong> ${width}x${height}</span> |
| | ${fps ? `<span><strong>FPS:</strong> ${fps}</span>` : ''} |
| | ${numFrames !== undefined ? `<span><strong>Frames:</strong> ${numFrames}</span>` : ''} |
| | ${seconds !== undefined ? `<span><strong>Duration:</strong> ${seconds}s</span>` : ''} |
| | ${step !== undefined ? `<span><strong>Steps:</strong> ${step}</span>` : ''} |
| | ${seed !== undefined ? `<span><strong>Seed:</strong> ${seed}</span>` : ''} |
| | ${cfgScale !== undefined ? `<span><strong>CFG Scale:</strong> ${cfgScale}</span>` : ''} |
| | `; |
| | captionDiv.appendChild(detailsDiv); |
| |
|
| | |
| | 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 = '<i class="fas fa-copy mr-1"></i>Copy Prompt'; |
| | copyBtn.onclick = () => { |
| | navigator.clipboard.writeText(prompt).then(() => { |
| | copyBtn.innerHTML = '<i class="fas fa-check mr-1"></i>Copied!'; |
| | setTimeout(() => { |
| | copyBtn.innerHTML = '<i class="fas fa-copy mr-1"></i>Copy Prompt'; |
| | }, 2000); |
| | }); |
| | }; |
| | captionDiv.appendChild(copyBtn); |
| |
|
| | videoContainer.appendChild(captionDiv); |
| | resultDiv.appendChild(videoContainer); |
| | }); |
| | |
| | if (resultPlaceholder) { |
| | resultPlaceholder.style.display = "none"; |
| | } |
| | } else { |
| | resultDiv.innerHTML = '<p class="text-xs text-[var(--color-text-secondary)] p-2">No videos were generated.</p>'; |
| | if (resultPlaceholder) { |
| | resultPlaceholder.style.display = "none"; |
| | } |
| | } |
| |
|
| | } catch (error) { |
| | console.error("Error generating video:", error); |
| | resultDiv.innerHTML = '<p class="text-xs text-red-500 p-2">Error: ' + error.message + '</p>'; |
| | if (resultPlaceholder) { |
| | resultPlaceholder.style.display = "none"; |
| | } |
| | } finally { |
| | |
| | loader.classList.add("hidden"); |
| | input.disabled = false; |
| | generateBtn.disabled = false; |
| | input.focus(); |
| | } |
| | } |
| |
|
| | |
| | function escapeHtml(text) { |
| | const div = document.createElement("div"); |
| | div.textContent = text; |
| | return div.innerHTML; |
| | } |
| |
|
| | |
| | document.addEventListener("DOMContentLoaded", function() { |
| | const input = document.getElementById("input"); |
| | const form = document.getElementById("genvideo"); |
| |
|
| | if (input) { |
| | input.focus(); |
| | } |
| |
|
| | if (form) { |
| | form.addEventListener("submit", genVideo); |
| | } |
| |
|
| | |
| | if (input) { |
| | input.addEventListener("keydown", function(event) { |
| | if (event.key === "Enter" && !event.shiftKey) { |
| | event.preventDefault(); |
| | genVideo(event); |
| | } |
| | }); |
| | } |
| |
|
| | |
| | const loader = document.getElementById("loader"); |
| | if (loader) { |
| | loader.classList.add("hidden"); |
| | } |
| | }); |
| |
|