From 6eebebc9ecb3e3e1f6a44cc35cf6e28e6ff4e841 Mon Sep 17 00:00:00 2001 From: Anil Matcha Date: Tue, 17 Feb 2026 21:52:55 +0530 Subject: [PATCH] Added Video Studio --- src/components/VideoStudio.js | 554 ++++++++++++++++++++++++++++++++++ src/lib/models.js | 405 +++++++++++++++++++++++++ src/lib/muapi.js | 54 +++- src/main.js | 4 +- 4 files changed, 1015 insertions(+), 2 deletions(-) create mode 100644 src/components/VideoStudio.js diff --git a/src/components/VideoStudio.js b/src/components/VideoStudio.js new file mode 100644 index 0000000..e49b537 --- /dev/null +++ b/src/components/VideoStudio.js @@ -0,0 +1,554 @@ +import { muapi } from '../lib/muapi.js'; +import { t2vModels, getAspectRatiosForVideoModel, getDurationsForModel, getResolutionsForVideoModel } from '../lib/models.js'; +import { AuthModal } from './AuthModal.js'; + +export function VideoStudio() { + const container = document.createElement('div'); + container.className = 'w-full h-full flex flex-col items-center justify-center bg-app-bg relative p-4 md:p-6 overflow-y-auto custom-scrollbar overflow-x-hidden'; + + // --- State --- + const defaultModel = t2vModels[0]; + let selectedModel = defaultModel.id; + let selectedModelName = defaultModel.name; + let selectedAr = defaultModel.inputs?.aspect_ratio?.default || '16:9'; + let selectedDuration = defaultModel.inputs?.duration?.default || 5; + let selectedResolution = defaultModel.inputs?.resolution?.default || ''; + let dropdownOpen = null; + + // ========================================== + // 1. HERO SECTION + // ========================================== + const hero = document.createElement('div'); + hero.className = 'flex flex-col items-center mb-10 md:mb-20 animate-fade-in-up transition-all duration-700'; + hero.innerHTML = ` +
+
+
+ + + + +
+ + + + +
+
+
+
+

Video Studio

+

Create stunning AI videos from text in seconds

+ `; + container.appendChild(hero); + + // ========================================== + // 2. PROMPT BAR + // ========================================== + const promptWrapper = document.createElement('div'); + promptWrapper.className = 'w-full max-w-4xl relative z-40 animate-fade-in-up'; + promptWrapper.style.animationDelay = '0.2s'; + + const bar = document.createElement('div'); + bar.className = 'w-full bg-[#111]/90 backdrop-blur-xl border border-white/10 rounded-[1.5rem] md:rounded-[2.5rem] p-3 md:p-5 flex flex-col gap-3 md:gap-5 shadow-3xl'; + + const topRow = document.createElement('div'); + topRow.className = 'flex items-start gap-5 px-2'; + + const textarea = document.createElement('textarea'); + textarea.placeholder = 'Describe the video you imagine'; + textarea.className = 'flex-1 bg-transparent border-none text-white text-base md:text-xl placeholder:text-muted focus:outline-none resize-none pt-2.5 leading-relaxed min-h-[40px] max-h-[150px] md:max-h-[250px] overflow-y-auto custom-scrollbar'; + textarea.rows = 1; + textarea.oninput = () => { + textarea.style.height = 'auto'; + const maxHeight = window.innerWidth < 768 ? 150 : 250; + textarea.style.height = Math.min(textarea.scrollHeight, maxHeight) + 'px'; + }; + + topRow.appendChild(textarea); + bar.appendChild(topRow); + + // Bottom Row: Controls + const bottomRow = document.createElement('div'); + bottomRow.className = 'flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-4 px-2 pt-4 border-t border-white/5'; + + const controlsLeft = document.createElement('div'); + controlsLeft.className = 'flex items-center gap-1.5 md:gap-2.5 relative overflow-x-auto no-scrollbar pb-1 md:pb-0'; + + const createControlBtn = (icon, label, id) => { + const btn = document.createElement('button'); + btn.id = id; + btn.className = 'flex items-center gap-1.5 md:gap-2.5 px-3 md:px-4 py-2 md:py-2.5 bg-white/5 hover:bg-white/10 rounded-xl md:rounded-2xl transition-all border border-white/5 group whitespace-nowrap'; + btn.innerHTML = ` + ${icon} + ${label} + + `; + return btn; + }; + + const modelBtn = createControlBtn(` +
+ V +
+ `, selectedModelName, 'v-model-btn'); + + const arBtn = createControlBtn(` + + `, selectedAr, 'v-ar-btn'); + + const durationBtn = createControlBtn(` + + `, `${selectedDuration}s`, 'v-duration-btn'); + + const resolutionBtn = createControlBtn(` + + `, selectedResolution || '720p', 'v-resolution-btn'); + + controlsLeft.appendChild(modelBtn); + controlsLeft.appendChild(arBtn); + controlsLeft.appendChild(durationBtn); + controlsLeft.appendChild(resolutionBtn); + + // Initial visibility + const initDurations = getDurationsForModel(defaultModel.id); + durationBtn.style.display = initDurations.length > 0 ? 'flex' : 'none'; + const initResolutions = getResolutionsForVideoModel(defaultModel.id); + resolutionBtn.style.display = initResolutions.length > 0 ? 'flex' : 'none'; + + const generateBtn = document.createElement('button'); + generateBtn.className = 'bg-primary text-black px-6 md:px-8 py-3 md:py-3.5 rounded-xl md:rounded-[1.5rem] font-black text-sm md:text-base hover:shadow-glow hover:scale-105 active:scale-95 transition-all flex items-center justify-center gap-2.5 w-full sm:w-auto shadow-lg'; + generateBtn.innerHTML = `Generate ✨`; + + bottomRow.appendChild(controlsLeft); + bottomRow.appendChild(generateBtn); + bar.appendChild(bottomRow); + promptWrapper.appendChild(bar); + container.appendChild(promptWrapper); + + // ========================================== + // 3. DROPDOWNS + // ========================================== + const dropdown = document.createElement('div'); + dropdown.className = 'absolute bottom-[102%] left-2 z-50 transition-all opacity-0 pointer-events-none scale-95 origin-bottom-left glass rounded-3xl p-3 translate-y-2 w-[calc(100vw-3rem)] max-w-xs shadow-4xl border border-white/10 flex flex-col'; + + const updateControlsForModel = (modelId) => { + const availableArs = getAspectRatiosForVideoModel(modelId); + selectedAr = availableArs[0]; + document.getElementById('v-ar-btn-label').textContent = selectedAr; + + const durations = getDurationsForModel(modelId); + if (durations.length > 0) { + selectedDuration = durations[0]; + document.getElementById('v-duration-btn-label').textContent = `${selectedDuration}s`; + durationBtn.style.display = 'flex'; + } else { + durationBtn.style.display = 'none'; + } + + const resolutions = getResolutionsForVideoModel(modelId); + if (resolutions.length > 0) { + selectedResolution = resolutions[0]; + document.getElementById('v-resolution-btn-label').textContent = selectedResolution; + resolutionBtn.style.display = 'flex'; + } else { + resolutionBtn.style.display = 'none'; + } + }; + + const showDropdown = (type, anchorBtn) => { + dropdown.innerHTML = ''; + dropdown.classList.remove('opacity-0', 'pointer-events-none'); + dropdown.classList.add('opacity-100', 'pointer-events-auto'); + + if (type === 'model') { + dropdown.classList.add('w-[calc(100vw-3rem)]', 'max-w-xs'); + dropdown.classList.remove('max-w-[240px]', 'max-w-[200px]'); + dropdown.innerHTML = ` +
+
+
+ + +
+
+
Video models
+
+
+ `; + const list = dropdown.querySelector('#v-model-list-container'); + + const renderModels = (filter = '') => { + list.innerHTML = ''; + const filtered = t2vModels.filter(m => m.name.toLowerCase().includes(filter.toLowerCase()) || m.id.toLowerCase().includes(filter.toLowerCase())); + filtered.forEach(m => { + const item = document.createElement('div'); + item.className = `flex items-center justify-between p-3.5 hover:bg-white/5 rounded-2xl cursor-pointer transition-all border border-transparent hover:border-white/5 ${selectedModel === m.id ? 'bg-white/5 border-white/5' : ''}`; + item.innerHTML = ` +
+
${m.name.charAt(0)}
+
+ ${m.name} +
+
+ ${selectedModel === m.id ? '' : ''} + `; + item.onclick = (e) => { + e.stopPropagation(); + selectedModel = m.id; + selectedModelName = m.name; + document.getElementById('v-model-btn-label').textContent = selectedModelName; + updateControlsForModel(selectedModel); + closeDropdown(); + }; + list.appendChild(item); + }); + }; + + renderModels(); + const searchInput = dropdown.querySelector('#v-model-search'); + searchInput.onclick = (e) => e.stopPropagation(); + searchInput.oninput = (e) => renderModels(e.target.value); + + } else if (type === 'ar') { + dropdown.classList.add('max-w-[240px]'); + dropdown.innerHTML = `
Aspect Ratio
`; + const list = document.createElement('div'); + list.className = 'flex flex-col gap-1'; + const availableArs = getAspectRatiosForVideoModel(selectedModel); + availableArs.forEach(r => { + const item = document.createElement('div'); + item.className = 'flex items-center justify-between p-3.5 hover:bg-white/5 rounded-2xl cursor-pointer transition-all group'; + item.innerHTML = ` +
+
+
+
+ ${r} +
+ ${selectedAr === r ? '' : ''} + `; + item.onclick = (e) => { + e.stopPropagation(); + selectedAr = r; + document.getElementById('v-ar-btn-label').textContent = r; + closeDropdown(); + }; + list.appendChild(item); + }); + dropdown.appendChild(list); + + } else if (type === 'duration') { + dropdown.classList.add('max-w-[200px]'); + dropdown.innerHTML = `
Duration
`; + const list = document.createElement('div'); + list.className = 'flex flex-col gap-1'; + const durations = getDurationsForModel(selectedModel); + durations.forEach(d => { + const item = document.createElement('div'); + item.className = 'flex items-center justify-between p-3.5 hover:bg-white/5 rounded-2xl cursor-pointer transition-all group'; + item.innerHTML = ` + ${d}s + ${selectedDuration === d ? '' : ''} + `; + item.onclick = (e) => { + e.stopPropagation(); + selectedDuration = d; + document.getElementById('v-duration-btn-label').textContent = `${d}s`; + closeDropdown(); + }; + list.appendChild(item); + }); + dropdown.appendChild(list); + + } else if (type === 'resolution') { + dropdown.classList.add('max-w-[200px]'); + dropdown.innerHTML = `
Resolution
`; + const list = document.createElement('div'); + list.className = 'flex flex-col gap-1'; + const resolutions = getResolutionsForVideoModel(selectedModel); + resolutions.forEach(r => { + const item = document.createElement('div'); + item.className = 'flex items-center justify-between p-3.5 hover:bg-white/5 rounded-2xl cursor-pointer transition-all group'; + item.innerHTML = ` + ${r} + ${selectedResolution === r ? '' : ''} + `; + item.onclick = (e) => { + e.stopPropagation(); + selectedResolution = r; + document.getElementById('v-resolution-btn-label').textContent = r; + closeDropdown(); + }; + list.appendChild(item); + }); + dropdown.appendChild(list); + } + + // Position dropdown + const btnRect = anchorBtn.getBoundingClientRect(); + const containerRect = container.getBoundingClientRect(); + if (window.innerWidth < 768) { + dropdown.style.left = '50%'; + dropdown.style.transform = 'translateX(-50%) translate(0, 8px)'; + } else { + dropdown.style.left = `${btnRect.left - containerRect.left}px`; + dropdown.style.transform = 'translate(0, 8px)'; + } + dropdown.style.bottom = `${containerRect.bottom - btnRect.top + 8}px`; + }; + + const closeDropdown = () => { + dropdown.classList.add('opacity-0', 'pointer-events-none'); + dropdown.classList.remove('opacity-100', 'pointer-events-auto'); + dropdownOpen = null; + }; + + const toggleDropdown = (type, btn) => (e) => { + e.stopPropagation(); + if (dropdownOpen === type) closeDropdown(); + else { dropdownOpen = type; showDropdown(type, btn); } + }; + + modelBtn.onclick = toggleDropdown('model', modelBtn); + arBtn.onclick = toggleDropdown('ar', arBtn); + durationBtn.onclick = toggleDropdown('duration', durationBtn); + resolutionBtn.onclick = toggleDropdown('resolution', resolutionBtn); + + window.addEventListener('click', closeDropdown); + container.appendChild(dropdown); + + // ========================================== + // 4. CANVAS AREA + HISTORY + // ========================================== + const generationHistory = []; + + const historySidebar = document.createElement('div'); + historySidebar.className = 'fixed right-0 top-0 h-full w-20 md:w-24 bg-black/60 backdrop-blur-xl border-l border-white/5 z-50 flex flex-col items-center py-4 gap-3 overflow-y-auto transition-all duration-500 translate-x-full opacity-0'; + historySidebar.id = 'video-history-sidebar'; + + const historyLabel = document.createElement('div'); + historyLabel.className = 'text-[9px] font-bold text-muted uppercase tracking-widest mb-2'; + historyLabel.textContent = 'History'; + historySidebar.appendChild(historyLabel); + + const historyList = document.createElement('div'); + historyList.className = 'flex flex-col gap-2 w-full px-2'; + historySidebar.appendChild(historyList); + container.appendChild(historySidebar); + + // Main canvas + const canvas = document.createElement('div'); + canvas.className = 'absolute inset-0 flex flex-col items-center justify-center p-4 min-[800px]:p-16 z-10 opacity-0 pointer-events-none transition-all duration-1000 translate-y-10 scale-95'; + + const videoContainer = document.createElement('div'); + videoContainer.className = 'relative group'; + + const resultVideo = document.createElement('video'); + resultVideo.className = 'max-h-[60vh] max-w-[80vw] rounded-3xl shadow-3xl border border-white/10 interactive-glow object-contain'; + resultVideo.controls = true; + resultVideo.loop = true; + resultVideo.autoplay = true; + resultVideo.muted = true; + resultVideo.playsInline = true; + videoContainer.appendChild(resultVideo); + + // Canvas Controls + const canvasControls = document.createElement('div'); + canvasControls.className = 'mt-6 flex gap-3 opacity-0 transition-opacity delay-500 duration-500 justify-center'; + + const regenerateBtn = document.createElement('button'); + regenerateBtn.className = 'bg-white/10 hover:bg-white/20 px-6 py-2.5 rounded-2xl text-xs font-bold transition-all border border-white/5 backdrop-blur-lg text-white'; + regenerateBtn.textContent = '↻ Regenerate'; + + const downloadBtn = document.createElement('button'); + downloadBtn.className = 'bg-primary text-black px-6 py-2.5 rounded-2xl text-xs font-bold transition-all shadow-glow active:scale-95'; + downloadBtn.textContent = '↓ Download'; + + const newPromptBtn = document.createElement('button'); + newPromptBtn.className = 'bg-white/10 hover:bg-white/20 px-6 py-2.5 rounded-2xl text-xs font-bold transition-all border border-white/5 backdrop-blur-lg text-white'; + newPromptBtn.textContent = '+ New'; + + canvasControls.appendChild(regenerateBtn); + canvasControls.appendChild(downloadBtn); + canvasControls.appendChild(newPromptBtn); + + canvas.appendChild(videoContainer); + canvas.appendChild(canvasControls); + container.appendChild(canvas); + + // --- Helper: Show video in canvas --- + const showVideoInCanvas = (videoUrl) => { + hero.classList.add('hidden'); + promptWrapper.classList.add('hidden'); + + resultVideo.src = videoUrl; + resultVideo.onloadeddata = () => { + canvas.classList.remove('opacity-0', 'pointer-events-none', 'translate-y-10', 'scale-95'); + canvas.classList.add('opacity-100', 'translate-y-0', 'scale-100'); + canvasControls.classList.remove('opacity-0'); + canvasControls.classList.add('opacity-100'); + }; + }; + + // --- Helper: Add to history --- + const addToHistory = (entry) => { + generationHistory.unshift(entry); + localStorage.setItem('video_history', JSON.stringify(generationHistory.slice(0, 30))); + historySidebar.classList.remove('translate-x-full', 'opacity-0'); + historySidebar.classList.add('translate-x-0', 'opacity-100'); + renderHistory(); + }; + + const renderHistory = () => { + historyList.innerHTML = ''; + generationHistory.forEach((entry, idx) => { + const thumb = document.createElement('div'); + thumb.className = `relative group/thumb cursor-pointer rounded-xl overflow-hidden border-2 transition-all duration-300 ${idx === 0 ? 'border-primary shadow-glow' : 'border-white/10 hover:border-white/30'}`; + + thumb.innerHTML = ` + +
+ +
+ `; + + thumb.onclick = (e) => { + if (e.target.closest('.hist-download')) { + downloadFile(entry.url, `video-${entry.id || idx}.mp4`); + return; + } + showVideoInCanvas(entry.url); + historyList.querySelectorAll('div').forEach(t => { + t.classList.remove('border-primary', 'shadow-glow'); + t.classList.add('border-white/10'); + }); + thumb.classList.remove('border-white/10'); + thumb.classList.add('border-primary', 'shadow-glow'); + }; + + historyList.appendChild(thumb); + }); + }; + + // --- Helper: Download file --- + const downloadFile = async (url, filename) => { + try { + const response = await fetch(url); + const blob = await response.blob(); + const blobUrl = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = blobUrl; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(blobUrl); + } catch (err) { + window.open(url, '_blank'); + } + }; + + // --- Load history from localStorage --- + try { + const saved = JSON.parse(localStorage.getItem('video_history') || '[]'); + if (saved.length > 0) { + saved.forEach(e => generationHistory.push(e)); + historySidebar.classList.remove('translate-x-full', 'opacity-0'); + historySidebar.classList.add('translate-x-0', 'opacity-100'); + renderHistory(); + } + } catch (e) { /* ignore */ } + + // --- Button Handlers --- + downloadBtn.onclick = () => { + const current = resultVideo.src; + if (current) { + const entry = generationHistory.find(e => e.url === current); + downloadFile(current, `video-${entry?.id || 'clip'}.mp4`); + } + }; + + regenerateBtn.onclick = () => generateBtn.click(); + + newPromptBtn.onclick = () => { + canvas.classList.add('opacity-0', 'pointer-events-none', 'translate-y-10', 'scale-95'); + canvas.classList.remove('opacity-100', 'translate-y-0', 'scale-100'); + canvasControls.classList.add('opacity-0'); + canvasControls.classList.remove('opacity-100'); + hero.classList.remove('hidden', 'opacity-0', 'scale-95', '-translate-y-10', 'pointer-events-none'); + promptWrapper.classList.remove('hidden', 'opacity-40'); + textarea.value = ''; + textarea.focus(); + }; + + // ========================================== + // 5. GENERATION LOGIC + // ========================================== + generateBtn.onclick = async () => { + const prompt = textarea.value.trim(); + if (!prompt) return; + + const apiKey = localStorage.getItem('muapi_key'); + if (!apiKey) { + AuthModal(() => generateBtn.click()); + return; + } + + hero.classList.add('opacity-0', 'scale-95', '-translate-y-10', 'pointer-events-none'); + + generateBtn.disabled = true; + generateBtn.innerHTML = ` Generating...`; + + try { + const params = { + prompt, + model: selectedModel, + aspect_ratio: selectedAr, + }; + + const durations = getDurationsForModel(selectedModel); + if (durations.length > 0) params.duration = selectedDuration; + + const resolutions = getResolutionsForVideoModel(selectedModel); + if (resolutions.length > 0) params.resolution = selectedResolution; + + const model = t2vModels.find(m => m.id === selectedModel); + if (model?.inputs?.quality) params.quality = model.inputs.quality.default; + + const res = await muapi.generateVideo(params); + + console.log('[VideoStudio] Full response:', res); + + if (res && res.url) { + addToHistory({ + id: res.id || Date.now().toString(), + url: res.url, + prompt, + model: selectedModel, + aspect_ratio: selectedAr, + duration: selectedDuration, + timestamp: new Date().toISOString() + }); + showVideoInCanvas(res.url); + } else { + console.error('[VideoStudio] No video URL in response:', res); + throw new Error('No video URL returned by API'); + } + } catch (e) { + console.error(e); + generateBtn.innerHTML = `Error: ${e.message.slice(0, 40)}`; + setTimeout(() => { + generateBtn.innerHTML = `Generate ✨`; + generateBtn.disabled = false; + }, 3000); + } finally { + generateBtn.disabled = false; + generateBtn.innerHTML = `Generate ✨`; + } + }; + + return container; +} diff --git a/src/lib/models.js b/src/lib/models.js index 5994678..2d86987 100644 --- a/src/lib/models.js +++ b/src/lib/models.js @@ -2021,3 +2021,408 @@ export const getAspectRatiosForModel = (modelId) => { return ['1:1', '16:9', '9:16', '4:3', '3:2', '21:9']; }; + +// ========================================== +// Text-to-Video Models +// ========================================== +export const t2vModels = [ + { + "id": "seedance-lite-t2v", + "name": "Seedance Lite", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "9:21"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["480p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" } + } + }, + { + "id": "seedance-pro-t2v", + "name": "Seedance Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "9:21"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["480p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" } + } + }, + { + "id": "seedance-pro-t2v-fast", + "name": "Seedance Pro Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["480p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" } + } + }, + { + "id": "seedance-v1.5-pro-t2v", + "name": "Seedance v1.5 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "3:4", "4:3", "21:9"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["480p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + }, + { + "id": "seedance-v1.5-pro-t2v-fast", + "name": "Seedance v1.5 Pro Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "3:4", "4:3", "21:9"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + }, + { + "id": "kling-v2.1-master-t2v", + "name": "Kling v2.1 Master", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 } + } + }, + { + "id": "kling-v2.5-turbo-pro-t2v", + "name": "Kling v2.5 Turbo Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "9:16" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 } + } + }, + { + "id": "kling-v2.6-pro-t2v", + "name": "Kling v2.6 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [5, 10], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds.", "default": 5 } + } + }, + { + "id": "kling-o1-text-to-video", + "name": "Kling O1 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [5, 10], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 } + } + }, + { + "id": "kling-v3.0-pro-text-to-video", + "name": "Kling v3.0 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "The aspect ratio of the generated video", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 } + } + }, + { + "id": "kling-v3.0-standard-text-to-video", + "name": "Kling v3.0 Standard", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "The aspect ratio of the generated video", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 } + } + }, + { + "id": "veo3-text-to-video", + "name": "Veo 3", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the desired video content." }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" } + } + }, + { + "id": "veo3-fast-text-to-video", + "name": "Veo 3 Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the desired video content." }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" } + } + }, + { + "id": "veo3.1-text-to-video", + "name": "Veo 3.1", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [8], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 8 }, + "resolution": { "enum": ["1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "1080p" } + } + }, + { + "id": "veo3.1-fast-text-to-video", + "name": "Veo 3.1 Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [8], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 8 }, + "resolution": { "enum": ["1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "1080p" } + } + }, + { + "id": "runway-text-to-video", + "name": "Runway Gen-3", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to be used to generate a video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "4:3", "3:4"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [5, 8], "title": "Duration", "name": "duration", "type": "int", "description": "The duration in seconds. If 8-second video is selected, 1080p resolution cannot be used.", "default": 5 }, + "resolution": { "enum": ["720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video. If 1080p is selected, 8-second video cannot be generated.", "default": "720p" } + } + }, + { + "id": "wan2.1-text-to-video", + "name": "Wan 2.1", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["480p", "720p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" }, + "quality": { "enum": ["medium", "high"], "title": "Quality", "name": "quality", "type": "string", "description": "The quality of the generated video.", "default": "medium" } + } + }, + { + "id": "wan2.2-text-to-video", + "name": "Wan 2.2", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds.", "default": 5 }, + "resolution": { "enum": ["480p", "720p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" }, + "quality": { "enum": ["medium", "high"], "title": "Quality", "name": "quality", "type": "string", "description": "The quality of the generated video.", "default": "medium" } + } + }, + { + "id": "wan2.2-5b-fast-t2v", + "name": "Wan 2.2 Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "resolution": { "enum": ["480p", "580p", "720p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" } + } + }, + { + "id": "wan2.5-text-to-video", + "name": "Wan 2.5", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["480p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" } + } + }, + { + "id": "wan2.5-text-to-video-fast", + "name": "Wan 2.5 Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + }, + { + "id": "wan2.6-text-to-video", + "name": "Wan 2.6", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [5, 10, 15], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + }, + { + "id": "hunyuan-text-to-video", + "name": "Hunyuan", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" } + } + }, + { + "id": "hunyuan-fast-text-to-video", + "name": "Hunyuan Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" } + } + }, + { + "id": "pixverse-v4.5-t2v", + "name": "Pixverse v4.5", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "4:3", "3:4"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds. 8s not supported for 1080p resolution.", "default": 5 }, + "resolution": { "enum": ["360p", "540p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + }, + { + "id": "pixverse-v5-t2v", + "name": "Pixverse v5", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "4:3", "3:4"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["360p", "540p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + }, + { + "id": "pixverse-v5.5-t2v", + "name": "Pixverse v5.5", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1", "4:3", "3:4"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [5, 8, 10], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds.", "default": 5 }, + "resolution": { "enum": ["360p", "540p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "360p" } + } + }, + { + "id": "minimax-hailuo-02-standard-t2v", + "name": "Hailuo 02 Standard", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "duration": { "enum": [6, 10], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 6 }, + "resolution": { "enum": ["768P"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "768P" } + } + }, + { + "id": "minimax-hailuo-02-pro-t2v", + "name": "Hailuo 02 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "duration": { "enum": [6], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 6 }, + "resolution": { "enum": ["1080P"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "1080P" } + } + }, + { + "id": "minimax-hailuo-2.3-pro-t2v", + "name": "Hailuo 2.3 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "resolution": { "enum": ["1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "1080p" } + } + }, + { + "id": "minimax-hailuo-2.3-standard-t2v", + "name": "Hailuo 2.3 Standard", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "duration": { "enum": [6, 10], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 6 } + } + }, + { + "id": "openai-sora", + "name": "Sora", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "resolution": { "enum": ["480p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "480p" } + } + }, + { + "id": "openai-sora-2-text-to-video", + "name": "Sora 2", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [10, 15], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 10 } + } + }, + { + "id": "openai-sora-2-pro-text-to-video", + "name": "Sora 2 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" }, + "duration": { "enum": [10, 15, 25], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds. Currently 25 seconds supports 720p only.", "default": 10 }, + "resolution": { "enum": ["720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + }, + { + "id": "vidu-v2.0-t2v", + "name": "Vidu v2.0", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "The prompt to generate the video" }, + "aspect_ratio": { "enum": ["9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "9:16" }, + "duration": { "enum": [4], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds.", "default": 4 }, + "resolution": { "enum": ["1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "1080p" } + } + }, + { + "id": "ovi-text-to-video", + "name": "OVI", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "16:9" } + } + }, + { + "id": "grok-imagine-text-to-video", + "name": "Grok Imagine", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["9:16", "16:9", "2:3", "3:2", "1:1"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "Aspect ratio of the output video.", "default": "1:1" }, + "duration": { "enum": [6, 10], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds.", "default": 6 } + } + }, + { + "id": "ltx-2-pro-text-to-video", + "name": "LTX 2 Pro", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "duration": { "enum": [6, 8, 10], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 6 } + } + }, + { + "id": "ltx-2-fast-text-to-video", + "name": "LTX 2 Fast", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "duration": { "enum": [6, 8, 10, 12, 14, 16, 18, 20], "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 6 } + } + }, + { + "id": "ltx-2-19b-text-to-video", + "name": "LTX 2 19B", + "inputs": { + "prompt": { "type": "string", "title": "Prompt", "name": "prompt", "description": "Text prompt describing the video." }, + "aspect_ratio": { "enum": ["16:9", "9:16"], "title": "Aspect Ratio", "name": "aspect_ratio", "type": "string", "description": "The aspect ratio of the generated video", "default": "16:9" }, + "duration": { "title": "Duration", "name": "duration", "type": "int", "description": "The duration of the generated video in seconds", "default": 5 }, + "resolution": { "enum": ["480p", "720p", "1080p"], "title": "Resolution", "name": "resolution", "type": "string", "description": "The resolution of the generated video.", "default": "720p" } + } + } +]; + +export const getVideoModelById = (id) => t2vModels.find(m => m.id === id); + +export const getAspectRatiosForVideoModel = (modelId) => { + const model = getVideoModelById(modelId); + if (!model) return ['16:9']; + const arInput = model.inputs?.aspect_ratio; + if (arInput && arInput.enum) return arInput.enum; + return ['16:9', '9:16', '1:1']; +}; + +export const getDurationsForModel = (modelId) => { + const model = getVideoModelById(modelId); + if (!model) return [5]; + const durInput = model.inputs?.duration; + if (durInput && durInput.enum) return durInput.enum; + if (durInput) return [durInput.default || 5]; + return []; +}; + +export const getResolutionsForVideoModel = (modelId) => { + const model = getVideoModelById(modelId); + if (!model) return []; + const resInput = model.inputs?.resolution; + if (resInput && resInput.enum) return resInput.enum; + return []; +}; diff --git a/src/lib/muapi.js b/src/lib/muapi.js index d66c28a..f709557 100644 --- a/src/lib/muapi.js +++ b/src/lib/muapi.js @@ -1,4 +1,4 @@ -import { getModelById } from './models.js'; +import { getModelById, getVideoModelById } from './models.js'; export class MuapiClient { constructor() { @@ -160,6 +160,58 @@ export class MuapiClient { throw new Error('Generation timed out after polling.'); } + async generateVideo(params) { + const key = this.getKey(); + + const modelInfo = getVideoModelById(params.model); + const endpoint = modelInfo?.endpoint || params.model; + const url = `${this.baseUrl}/api/v1/${endpoint}`; + + const finalPayload = { prompt: params.prompt }; + + if (params.aspect_ratio) finalPayload.aspect_ratio = params.aspect_ratio; + if (params.duration) finalPayload.duration = params.duration; + if (params.resolution) finalPayload.resolution = params.resolution; + if (params.quality) finalPayload.quality = params.quality; + + console.log('[Muapi] Video Request:', url); + console.log('[Muapi] Video Payload:', finalPayload); + + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': key + }, + body: JSON.stringify(finalPayload) + }); + + if (!response.ok) { + const errText = await response.text(); + console.error('[Muapi] API Error Body:', errText); + throw new Error(`API Request Failed: ${response.status} ${response.statusText} - ${errText.slice(0, 100)}`); + } + + const submitData = await response.json(); + console.log('[Muapi] Video Submit Response:', submitData); + + const requestId = submitData.request_id || submitData.id; + if (!requestId) return submitData; + + console.log('[Muapi] Polling for video results, request_id:', requestId); + const result = await this.pollForResult(requestId, key, 120, 2000); + + const videoUrl = result.outputs?.[0] || result.url || result.output?.url; + console.log('[Muapi] Video URL:', videoUrl); + return { ...result, url: videoUrl }; + + } catch (error) { + console.error("Muapi Video Client Error:", error); + throw error; + } + } + getDimensionsFromAR(ar) { // Base unit 1024 (Flux standard) switch (ar) { diff --git a/src/main.js b/src/main.js index 6ff8dde..c1e287e 100644 --- a/src/main.js +++ b/src/main.js @@ -13,7 +13,9 @@ function navigate(page) { if (page === 'image') { contentArea.appendChild(ImageStudio()); } else if (page === 'video') { - contentArea.innerHTML = '
Video Studio Coming Soon 🎬
'; + import('./components/VideoStudio.js').then(({ VideoStudio }) => { + contentArea.appendChild(VideoStudio()); + }); } else if (page === 'cinema') { import('./components/CinemaStudio.js').then(({ CinemaStudio }) => { contentArea.appendChild(CinemaStudio());