Veu3 / index.html
Hamed744's picture
Update index.html
d545782 verified
raw
history blame
56 kB
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ساخت ویدیو هوشمند آلفا | تبدیل تصویر و متن به انیمیشن</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/Vazirmatn-font-face.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: rgb(84, 110, 122);
--primary-light: rgba(84, 110, 122, 0.1);
--primary-dark: rgb(69, 90, 100);
--accent-color: #4fc3f7;
--accent-dark: #29b6f6;
}
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-8px); }
100% { transform: translateY(0px); }
}
@keyframes gradientFlow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulseGlow {
0% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.4); }
70% { box-shadow: 0 0 0 12px rgba(76, 175, 80, 0); }
100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); }
}
.animate-float {
animation: float 6s ease-in-out infinite;
}
.gradient-animate {
background-size: 300% 300%;
animation: gradientFlow 12s ease infinite;
}
.animate-fade-in {
animation: fadeIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.pulse-glow {
animation: pulseGlow 2s infinite;
}
.glass-morphism {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
}
.option-card {
transition: all 0.4s cubic-bezier(0.68, -0.6, 0.32, 1.6);
transform-style: preserve-3d;
}
.option-card:hover {
transform: translateY(-6px) scale(1.02);
}
.option-card.selected {
transform: translateY(-4px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 10px 10px -5px rgba(0, 0, 0, 0.1);
}
.file-drop-area {
transition: all 0.3s ease;
}
.file-drop-area.drag-over {
border-color: var(--accent-color);
background: rgba(79, 195, 247, 0.08);
}
.progress-track {
background: linear-gradient(90deg, var(--primary-color) 0%, var(--accent-color) 100%);
background-size: 200% 100%;
animation: gradientFlow 4s ease infinite;
}
.text-gradient {
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
.status-message {
animation: fadeIn 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.video-container {
perspective: 1000px;
}
.video-wrapper {
transform-style: preserve-3d;
transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.video-wrapper:hover {
transform: rotateX(2deg) rotateY(2deg) scale(1.02);
}
/* Button styles */
.primary-btn {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
color: white;
transition: all 0.3s ease;
}
.primary-btn:hover {
background: linear-gradient(135deg, var(--primary-dark) 0%, rgb(59, 77, 86) 100%);
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.secondary-btn {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #e2e8f0;
transition: all 0.3s ease;
}
.secondary-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
}
.accent-btn {
background: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-dark) 100%);
color: white;
transition: all 0.3s ease;
}
.accent-btn:hover {
background: linear-gradient(135deg, var(--accent-dark) 0%, #039be5 100%);
}
.tab-btn {
position: relative;
overflow: hidden;
}
.tab-btn::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, var(--primary-color) 0%, var(--accent-color) 100%);
transform: scaleX(0);
transition: transform 0.3s ease;
transform-origin: left;
}
.tab-btn.active::after {
transform: scaleX(1);
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: var(--primary-light);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary-color);
}
/* Elegant background */
.bg-pattern {
background-color: #0f172a;
background-image:
radial-gradient(at 47% 33%, rgba(84, 110, 122, 0.2) 0, transparent 59%),
radial-gradient(at 82% 65%, rgba(79, 195, 247, 0.1) 0, transparent 55%);
}
/* Floating elements */
.floating-element {
position: absolute;
border-radius: 50%;
filter: blur(60px);
opacity: 0.15;
z-index: -1;
}
/* Input focus styles */
.custom-input:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.2);
}
/* Card hover effect */
.hover-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.hover-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 10px 10px -5px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body class="min-h-screen bg-pattern text-gray-100 font-sans overflow-x-hidden">
<!-- Floating background elements -->
<div class="floating-element bg-primary-color w-64 h-64 top-20 left-20"></div>
<div class="floating-element bg-accent-color w-96 h-96 bottom-20 right-20"></div>
<div class="container mx-auto px-4 py-12 max-w-4xl relative">
<!-- Header Section -->
<div class="text-center mb-16 animate-fade-in" style="animation-delay: 0.1s;">
<div class="inline-flex items-center justify-center p-5 rounded-2xl glass-morphism mb-6 shadow-lg hover-card">
<i class="fas fa-film text-4xl text-transparent bg-clip-text bg-gradient-to-r from-primary-color to-accent-color"></i>
</div>
<h1 class="text-4xl font-bold mb-4 flex items-center justify-center">
<span class="text-gradient bg-gradient-to-r from-primary-color to-accent-color">ساخت ویدیو هوشمند</span>
<span class="text-gradient bg-gradient-to-r from-accent-color to-cyan-400 mr-3">آلفا</span>
</h1>
<p class="text-lg text-gray-300 max-w-2xl mx-auto leading-relaxed">
با فناوری هوش مصنوعی آلفا، تصاویر و متون خود را به انیمیشن‌های خیره‌کننده تبدیل کنید
</p>
</div>
<!-- Main Card -->
<div class="glass-morphism rounded-3xl overflow-hidden shadow-2xl mb-10 animate-fade-in hover-card" style="animation-delay: 0.2s;">
<!-- Mode Selector -->
<div class="flex border-b border-gray-700/50">
<button class="mode-button tab-btn flex-1 py-5 font-medium text-sm uppercase tracking-wider transition-all duration-300 relative overflow-hidden group active" data-mode="image-to-video">
<span class="relative z-10 flex items-center justify-center">
<i class="fas fa-image ml-2 text-accent-color"></i>
تصویر به ویدیو
</span>
</button>
<button class="mode-button tab-btn flex-1 py-5 font-medium text-sm uppercase tracking-wider transition-all duration-300 relative overflow-hidden group" data-mode="text-to-video">
<span class="relative z-10 flex items-center justify-center">
<i class="fas fa-font ml-2 text-gray-400 group-hover:text-gray-300 transition-colors"></i>
متن به ویدیو
</span>
</button>
</div>
<!-- Form Content -->
<div class="p-8">
<!-- Image to Video Section -->
<div id="imageToVideoSection" class="form-mode-section">
<div class="mb-8">
<label for="imageFile" class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-image mr-2 text-accent-color"></i>
انتخاب تصویر
</label>
<div class="file-drop-area relative border-2 border-dashed border-gray-700 rounded-2xl p-10 text-center cursor-pointer transition-all duration-300 hover:border-accent-color bg-gray-800/30" id="fileUploadArea">
<input type="file" id="imageFile" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" accept="image/jpeg, image/png, image/webp" />
<div class="flex flex-col items-center justify-center">
<div class="w-16 h-16 rounded-full bg-gradient-to-br from-primary-light to-accent-color/20 flex items-center justify-center mb-4">
<i class="fas fa-cloud-upload-alt text-2xl text-accent-color"></i>
</div>
<p class="text-sm text-gray-300 mb-1">فایل را اینجا رها کنید یا برای انتخاب کلیک کنید</p>
<p class="text-xs text-gray-500">فرمت‌های پشتیبانی شده: JPG, PNG, WebP</p>
</div>
</div>
<img id="imagePreview" src="#" alt="پیش‌نمایش" class="mt-5 rounded-xl shadow-lg w-full max-h-72 object-contain hidden mx-auto border border-gray-700/50" />
</div>
</div>
<!-- Text to Video Section -->
<div id="textToVideoSection" class="form-mode-section hidden">
<div class="mb-8 text-center py-5 px-6 rounded-xl bg-gradient-to-r from-primary-light to-accent-color/20 border border-accent-color/20 shadow-inner">
<div class="flex items-center justify-center">
<i class="fas fa-info-circle text-accent-color mr-3"></i>
<span class="text-sm text-gray-200">در این حالت، ویدیو فقط بر اساس متن راهنما ساخته می‌شود.</span>
</div>
</div>
</div>
<!-- Prompt Section -->
<div class="mb-8">
<label for="prompt" class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-magic mr-2 text-accent-color"></i>
شرح انیمیشن (Prompt)
</label>
<div class="relative">
<textarea id="prompt" rows="3" class="custom-input w-full px-5 py-4 bg-gray-800/40 border border-gray-700 rounded-xl focus:ring-2 focus:ring-accent-color focus:border-accent-color text-gray-200 placeholder-gray-500 transition-all duration-300 shadow-inner" placeholder="مثال: گربه‌ای که به آرامی پلک می‌زند">یک موجود از داخل تصویر شروع به حرکت می‌کند</textarea>
<div class="absolute bottom-4 left-4 text-xs text-gray-500 flex items-center">
<i class="fas fa-lightbulb mr-1 text-yellow-300/70"></i>
<span>توضیحات دقیق‌تر = نتیجه بهتر</span>
</div>
</div>
</div>
<!-- Duration Options -->
<div class="mb-8">
<label class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-clock mr-2 text-accent-color"></i>
مدت زمان
</label>
<div class="grid grid-cols-2 gap-4">
<button class="option-card duration-button p-4 rounded-xl bg-gray-800/50 border border-gray-700 text-gray-200 hover:border-accent-color flex flex-col items-center relative overflow-hidden" data-api-duration="5">
<div class="w-10 h-10 rounded-full bg-accent-color/10 flex items-center justify-center mb-2">
<i class="fas fa-hourglass-start text-accent-color"></i>
</div>
<span class="font-medium">کوتاه</span>
<span class="text-xs text-gray-400 mt-1">۵ ثانیه</span>
<div class="absolute inset-0 bg-gradient-to-br from-primary-light to-accent-color/5 opacity-0 hover:opacity-100 transition-opacity duration-300"></div>
</button>
<button class="option-card duration-button p-4 rounded-xl bg-gradient-to-br from-primary-light to-accent-color/20 border border-accent-color/30 text-gray-100 hover:border-accent-color flex flex-col items-center relative selected" data-api-duration="7.8">
<div class="w-10 h-10 rounded-full bg-accent-color/20 flex items-center justify-center mb-2">
<i class="fas fa-hourglass-half text-gray-200"></i>
</div>
<span class="font-medium">استاندارد</span>
<span class="text-xs text-accent-color mt-1">۸ ثانیه</span>
<div class="absolute inset-0 bg-gradient-to-br from-primary-light to-accent-color/10 opacity-0 hover:opacity-100 transition-opacity duration-300"></div>
</button>
</div>
</div>
<!-- Advanced Settings Toggle -->
<button id="advancedSettingsToggle" class="w-full py-4 px-5 mb-6 rounded-xl bg-gray-800/50 border border-gray-700 text-gray-300 hover:bg-gray-700/50 hover:text-white transition-all duration-300 flex items-center justify-between group">
<span class="flex items-center">
<i class="fas fa-cog mr-3 text-accent-color group-hover:animate-spin"></i>
تنظیمات پیشرفته
</span>
<i class="fas fa-chevron-down text-gray-400 transition-transform duration-300 group-hover:text-accent-color"></i>
</button>
<!-- Advanced Settings -->
<div id="advancedSettings" class="hidden bg-gradient-to-br from-gray-800/30 to-gray-900/30 rounded-2xl p-6 mb-8 border border-gray-700/50 shadow-inner">
<div class="mb-8">
<label class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-crop-alt mr-2 text-accent-color"></i>
نسبت تصویر
</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<button class="option-card aspect-ratio-button p-3 rounded-xl bg-gray-800/50 border border-gray-700 text-gray-200 hover:border-accent-color flex flex-col items-center" data-height="768" data-width="768">
<i class="fas fa-square text-xl mb-1"></i>
<span class="text-sm">مربع</span>
<span class="text-xs text-gray-400 mt-1">۱:۱</span>
</button>
<button class="option-card aspect-ratio-button p-3 rounded-xl bg-gray-800/50 border border-gray-700 text-gray-200 hover:border-accent-color flex flex-col items-center" data-height="768" data-width="432">
<i class="fas fa-mobile-alt text-xl mb-1"></i>
<span class="text-sm">پرتره</span>
<span class="text-xs text-gray-400 mt-1">۹:۱۶</span>
</button>
<button class="option-card aspect-ratio-button p-3 rounded-xl bg-gray-800/50 border border-gray-700 text-gray-200 hover:border-accent-color flex flex-col items-center" data-height="432" data-width="768">
<i class="fas fa-desktop text-xl mb-1"></i>
<span class="text-sm">لنداسکیپ</span>
<span class="text-xs text-gray-400 mt-1">۱۶:۹</span>
</button>
<button class="option-card aspect-ratio-button p-3 rounded-xl bg-gradient-to-br from-primary-light to-accent-color/20 border border-accent-color/30 text-gray-100 hover:border-accent-color flex flex-col items-center selected" data-height="576" data-width="1024">
<i class="fas fa-expand text-xl mb-1"></i>
<span class="text-sm">پیش‌فرض</span>
<span class="text-xs text-accent-color mt-1">آلفا</span>
</button>
</div>
</div>
<div class="mb-8">
<label for="negativePrompt" class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-ban mr-2 text-red-300"></i>
متن راهنمای منفی
</label>
<textarea id="negativePrompt" rows="2" class="custom-input w-full px-5 py-4 bg-gray-800/40 border border-gray-700 rounded-xl focus:ring-2 focus:ring-accent-color focus:border-accent-color text-gray-200 placeholder-gray-500 transition-all duration-300 shadow-inner">کیفیت پایین، تار، لرزان</textarea>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div>
<label for="cfgScale" class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-sliders-h mr-2 text-accent-color"></i>
مقیاس CFG
</label>
<div class="relative">
<input type="number" id="cfgScale" value="1.0" min="1.0" max="10.0" step="0.1" class="custom-input w-full px-5 py-3 bg-gray-800/40 border border-gray-700 rounded-xl focus:ring-2 focus:ring-accent-color focus:border-accent-color text-gray-200 transition-all duration-300 shadow-inner" />
<div class="absolute left-4 top-3 text-gray-500">
<i class="fas fa-adjust"></i>
</div>
</div>
</div>
<div>
<label for="seed" class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-dice mr-2 text-yellow-300"></i>
سید تصادفی
</label>
<div class="relative">
<input type="number" id="seed" value="0" min="0" class="custom-input w-full px-5 py-3 bg-gray-800/40 border border-gray-700 rounded-xl focus:ring-2 focus:ring-accent-color focus:border-accent-color text-gray-200 transition-all duration-300 shadow-inner" />
<div class="absolute left-4 top-3 text-gray-500">
<i class="fas fa-random"></i>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="outputHeight" class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-arrows-alt-v mr-2 text-accent-color"></i>
ارتفاع (پیکسل)
</label>
<div class="relative">
<input type="number" id="outputHeight" value="576" step="32" min="256" max="1280" class="custom-input w-full px-5 py-3 bg-gray-800/40 border border-gray-700 rounded-xl focus:ring-2 focus:ring-accent-color focus:border-accent-color text-gray-200 transition-all duration-300 shadow-inner" />
<div class="absolute left-4 top-3 text-gray-500">
<i class="fas fa-ruler-vertical"></i>
</div>
</div>
</div>
<div>
<label for="outputWidth" class="block text-sm font-medium mb-3 text-gray-300 flex items-center">
<i class="fas fa-arrows-alt-h mr-2 text-accent-color"></i>
عرض (پیکسل)
</label>
<div class="relative">
<input type="number" id="outputWidth" value="1024" step="32" min="256" max="1280" class="custom-input w-full px-5 py-3 bg-gray-800/40 border border-gray-700 rounded-xl focus:ring-2 focus:ring-accent-color focus:border-accent-color text-gray-200 transition-all duration-300 shadow-inner" />
<div class="absolute left-4 top-3 text-gray-500">
<i class="fas fa-ruler-horizontal"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Generate Button -->
<div class="text-center mt-10">
<button id="generateButton" class="primary-btn w-full py-5 px-8 rounded-2xl text-lg font-bold shadow-lg hover:shadow-xl group relative overflow-hidden">
<span class="relative z-10 flex items-center justify-center">
<i class="fas fa-magic mr-3 transform group-hover:rotate-12 transition-transform"></i>
ساخت ویدیو هوشمند
</span>
<span class="absolute inset-0 bg-gradient-to-r from-primary-color/30 to-accent-color/30 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
</button>
</div>
</div>
</div>
<!-- Status Section -->
<div id="statusSection" class="glass-morphism rounded-3xl overflow-hidden shadow-2xl mb-10 hidden animate-fade-in hover-card">
<div class="p-8">
<h2 class="text-xl font-bold mb-6 flex items-center">
<div class="w-10 h-10 rounded-full bg-gradient-to-r from-primary-light to-accent-color/20 flex items-center justify-center mr-3">
<i class="fas fa-spinner fa-spin text-accent-color"></i>
</div>
<span class="text-gradient bg-gradient-to-r from-primary-color to-accent-color">در حال پردازش درخواست</span>
</h2>
<div class="mb-6">
<div class="flex justify-between text-sm mb-2">
<span class="text-gray-300">پیشرفت عملیات</span>
<span id="progressPercent" class="text-accent-color font-medium">0%</span>
</div>
<div class="w-full h-2.5 bg-gray-800 rounded-full overflow-hidden">
<div id="progressBar" class="h-full rounded-full progress-track" style="width: 0%"></div>
</div>
</div>
<div id="statusMessages" class="space-y-3 max-h-60 overflow-y-auto pr-2 custom-scroll">
<!-- Status messages will appear here -->
</div>
</div>
</div>
<!-- Output Section -->
<div id="outputSection" class="glass-morphism rounded-3xl overflow-hidden shadow-2xl hidden animate-fade-in hover-card">
<div class="p-8">
<h2 class="text-xl font-bold mb-6 flex items-center">
<div class="w-10 h-10 rounded-full bg-gradient-to-r from-green-500/20 to-teal-500/20 flex items-center justify-center mr-3">
<i class="fas fa-check text-green-300"></i>
</div>
<span class="text-gradient bg-gradient-to-r from-green-200 to-teal-200">ویدیوی شما آماده شد!</span>
</h2>
<div class="video-container mb-6">
<div class="video-wrapper bg-gray-900 rounded-xl overflow-hidden shadow-lg">
<video id="outputVideo" controls preload="metadata" playsinline class="w-full"></video>
</div>
</div>
<div class="flex flex-col md:flex-row justify-between items-center">
<p id="finalSeed" class="text-sm text-gray-400 mb-4 md:mb-0">
<!-- Final seed will appear here -->
</p>
<div class="flex space-x-3 rtl:space-x-reverse">
<button id="downloadButton" class="accent-btn py-2.5 px-5 rounded-xl flex items-center shadow hover:shadow-md">
<i class="fas fa-download mr-2"></i>
دانلود ویدیو
</button>
<button id="newGenerationButton" class="secondary-btn py-2.5 px-5 rounded-xl flex items-center">
<i class="fas fa-redo mr-2"></i>
ساخت جدید
</button>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="text-center text-gray-500 text-sm mt-16">
<p class="flex items-center justify-center">
ساخته شده با
<i class="fas fa-heart text-accent-color mx-2 animate-pulse"></i>
توسط تیم آلفا
</p>
<p class="mt-2 text-gray-600">نسخه ۲.۰ | فناوری هوش مصنوعی پیشرفته</p>
</div>
</div>
<script>
// DOM Elements
const imageFileInput = document.getElementById('imageFile');
const imagePreview = document.getElementById('imagePreview');
const promptInput = document.getElementById('prompt');
const durationButtons = document.querySelectorAll('.duration-button');
const aspectRatioButtons = document.querySelectorAll('.aspect-ratio-button');
const generateButton = document.getElementById('generateButton');
const outputVideo = document.getElementById('outputVideo');
const finalSeedElement = document.getElementById('finalSeed');
const statusMessagesDiv = document.getElementById('statusMessages');
const statusSection = document.getElementById('statusSection');
const outputSection = document.getElementById('outputSection');
const progressBar = document.getElementById('progressBar');
const progressPercent = document.getElementById('progressPercent');
const downloadButton = document.getElementById('downloadButton');
const newGenerationButton = document.getElementById('newGenerationButton');
const advancedSettingsToggle = document.getElementById('advancedSettingsToggle');
const advancedSettingsDiv = document.getElementById('advancedSettings');
const negativePromptInput = document.getElementById('negativePrompt');
const cfgScaleInput = document.getElementById('cfgScale');
const seedInput = document.getElementById('seed');
const outputHeightInput = document.getElementById('outputHeight');
const outputWidthInput = document.getElementById('outputWidth');
const modeButtons = document.querySelectorAll('.mode-button');
const imageToVideoSection = document.getElementById('imageToVideoSection');
const textToVideoSection = document.getElementById('textToVideoSection');
const fileUploadArea = document.getElementById('fileUploadArea');
// Constants
const SPACE_URL_BASE = "https://lightricks-ltx-video-distilled.hf.space";
const FN_INDEX_GENERATE = 5;
let selectedApiDuration = 7.8;
// Variables
let currentSessionHash = '';
let uploadedImageInfo = null;
let currentGenerationMode = "image-to-video";
let currentProgressPhase = 0;
let totalUnitsInPhase = 0;
let completedUnitsInPhase = 0;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Set default selections
document.querySelector('.duration-button.selected')?.dispatchEvent(new Event('click'));
document.querySelector('.aspect-ratio-button.selected')?.dispatchEvent(new Event('click'));
updateFormForMode(currentGenerationMode);
// Set up drag and drop for file upload
setupDragAndDrop();
});
// Drag and Drop Setup
function setupDragAndDrop() {
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
fileUploadArea.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
fileUploadArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
fileUploadArea.addEventListener(eventName, unhighlight, false);
});
function highlight() {
fileUploadArea.classList.add('drag-over');
playHoverSound();
}
function unhighlight() {
fileUploadArea.classList.remove('drag-over');
}
fileUploadArea.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
imageFileInput.files = files;
imageFileInput.dispatchEvent(new Event('change'));
playSuccessSound();
}
}
}
// Event listeners for mode buttons
modeButtons.forEach(button => {
button.addEventListener('click', () => {
// Update active state
modeButtons.forEach(btn => {
btn.classList.remove('active');
btn.querySelector('i').classList.remove('text-accent-color');
btn.querySelector('i').classList.add('text-gray-400');
});
button.classList.add('active');
button.querySelector('i').classList.remove('text-gray-400');
button.querySelector('i').classList.add('text-accent-color');
// Update mode
currentGenerationMode = button.dataset.mode;
updateFormForMode(currentGenerationMode);
// Play click sound
playClickSound();
});
});
// Advanced settings toggle
advancedSettingsToggle.addEventListener('click', () => {
const isOpen = advancedSettingsDiv.classList.contains('hidden');
if (isOpen) {
advancedSettingsDiv.classList.remove('hidden');
advancedSettingsToggle.querySelector('i').classList.remove('fa-chevron-down');
advancedSettingsToggle.querySelector('i').classList.add('fa-chevron-up');
} else {
advancedSettingsDiv.classList.add('hidden');
advancedSettingsToggle.querySelector('i').classList.remove('fa-chevron-up');
advancedSettingsToggle.querySelector('i').classList.add('fa-chevron-down');
}
// Play click sound
playClickSound();
});
// Image file input change
imageFileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
// Validate file type
if (!file.type.startsWith('image/')) {
addStatusMessage('لطفاً یک فایل تصویری (JPG, PNG, WebP) انتخاب کنید.', 'error');
imageFileInput.value = '';
imagePreview.style.display = 'none';
playErrorSound();
return;
}
// Show preview
const reader = new FileReader();
reader.onload = function(e) {
imagePreview.src = e.target.result;
imagePreview.style.display = 'block';
imagePreview.classList.add('animate-fade-in');
// Add success message
addStatusMessage('تصویر با موفقیت آپلود شد!', 'success');
playSuccessSound();
}
reader.readAsDataURL(file);
uploadedImageInfo = null;
} else {
imagePreview.style.display = 'none';
}
});
// Duration buttons
durationButtons.forEach(button => {
button.addEventListener('click', () => {
// Update selection
durationButtons.forEach(btn => {
btn.classList.remove('selected', 'bg-gradient-to-br', 'from-primary-light', 'to-accent-color/20', 'border-accent-color/30', 'text-gray-100');
btn.classList.add('bg-gray-800/50', 'border-gray-700', 'text-gray-200');
});
button.classList.add('selected', 'bg-gradient-to-br', 'from-primary-light', 'to-accent-color/20', 'border-accent-color/30', 'text-gray-100');
button.classList.remove('bg-gray-800/50', 'border-gray-700', 'text-gray-200');
selectedApiDuration = parseFloat(button.dataset.apiDuration);
// Play click sound
playClickSound();
});
});
// Aspect ratio buttons
aspectRatioButtons.forEach(button => {
button.addEventListener('click', () => {
// Update selection
aspectRatioButtons.forEach(btn => {
btn.classList.remove('selected', 'bg-gradient-to-br', 'from-primary-light', 'to-accent-color/20', 'border-accent-color/30', 'text-gray-100');
btn.classList.add('bg-gray-800/50', 'border-gray-700', 'text-gray-200');
});
button.classList.add('selected', 'bg-gradient-to-br', 'from-primary-light', 'to-accent-color/20', 'border-accent-color/30', 'text-gray-100');
button.classList.remove('bg-gray-800/50', 'border-gray-700', 'text-gray-200');
outputHeightInput.value = button.dataset.height;
outputWidthInput.value = button.dataset.width;
// Play click sound
playClickSound();
});
});
// New generation button
newGenerationButton.addEventListener('click', () => {
outputSection.classList.add('hidden');
window.scrollTo({
top: 0,
behavior: 'smooth'
});
playClickSound();
});
// Update form for selected mode
function updateFormForMode(mode) {
if (mode === "image-to-video") {
imageToVideoSection.classList.remove('hidden');
textToVideoSection.classList.add('hidden');
promptInput.placeholder = "مثال: گربه‌ای که به آرامی پلک می‌زند";
} else if (mode === "text-to-video") {
imageToVideoSection.classList.add('hidden');
textToVideoSection.classList.remove('hidden');
imageFileInput.value = '';
imagePreview.style.display = 'none';
uploadedImageInfo = null;
promptInput.placeholder = "مثال: یک اژدهای آتشی که بر فراز قلعه پرواز می‌کند";
}
}
// Add status message
function addStatusMessage(message, type = 'info') {
statusSection.classList.remove('hidden');
const messageDiv = document.createElement('div');
messageDiv.className = `status-message p-4 rounded-lg text-sm flex items-start ${type === 'error' ?
'bg-gradient-to-r from-red-900/30 to-pink-900/20 text-red-100 border-r-2 border-red-400' :
type === 'success' ?
'bg-gradient-to-r from-green-900/30 to-teal-900/20 text-green-100 border-r-2 border-green-400' :
'bg-gradient-to-r from-blue-900/30 to-indigo-900/20 text-blue-100 border-r-2 border-blue-400'}`;
const icon = document.createElement('i');
icon.className = type === 'error' ? 'fas fa-exclamation-circle mt-0.5 mr-3 text-red-300' :
type === 'success' ? 'fas fa-check-circle mt-0.5 mr-3 text-green-300' :
'fas fa-info-circle mt-0.5 mr-3 text-blue-300';
const text = document.createElement('span');
text.textContent = message;
messageDiv.appendChild(icon);
messageDiv.appendChild(text);
if (statusMessagesDiv.firstChild) {
statusMessagesDiv.insertBefore(messageDiv, statusMessagesDiv.firstChild);
} else {
statusMessagesDiv.appendChild(messageDiv);
}
// Auto-scroll to top
statusMessagesDiv.scrollTop = 0;
}
// Clear status and output
function clearStatusAndOutput() {
statusMessagesDiv.innerHTML = '';
statusSection.classList.add('hidden');
outputSection.classList.add('hidden');
if (outputVideo.src) {
URL.revokeObjectURL(outputVideo.src);
}
outputVideo.src = '';
finalSeedElement.textContent = '';
progressBar.style.width = '0%';
progressPercent.textContent = '0%';
}
// Generate random hash
function generateRandomHash(length = 11) {
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
// Upload image
async function uploadImage(file) {
addStatusMessage('۱. آماده‌سازی تصویر...');
const uploadId = generateRandomHash(12);
const formData = new FormData();
formData.append('files', file);
try {
const response = await fetch(`${SPACE_URL_BASE}/gradio_api/upload?upload_id=${uploadId}`, {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`خطا در آپلود (${response.status})`);
}
const result = await response.json();
if (result && result.length > 0 && typeof result[0] === 'string') {
return {
path: result[0],
url: `${SPACE_URL_BASE}/gradio_api/file=${result[0]}`,
orig_name: file.name,
size: file.size,
mime_type: file.type || 'application/octet-stream',
meta: { "_type": "gradio.FileData" }
};
} else {
throw new Error('پاسخ آپلود نامعتبر.');
}
} catch (error) {
addStatusMessage(`خطا در آپلود: ${error.message}`, 'error');
console.error('Upload error:', error);
return null;
}
}
// Submit to queue
async function submitToQueue(payload) {
addStatusMessage('۲. ارسال درخواست به سرور...');
try {
const response = await fetch(`${SPACE_URL_BASE}/gradio_api/queue/join`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`خطا در ارسال به صف (${response.status}) - ${errorText.substring(0,100)}`);
}
return await response.json();
} catch (error) {
addStatusMessage(`خطا در ارتباط با سرور: ${error.message}`, 'error');
console.error('Queue join error:', error);
return null;
}
}
// Update progress bar
function updateVisualProgress(progressDataArray) {
if (!progressDataArray || progressDataArray.length === 0) return;
const pInfo = progressDataArray[0];
if (pInfo.desc === "Saving video") {
if (currentProgressPhase !== 1) {
currentProgressPhase = 1;
addStatusMessage("مرحله نهایی: ذخیره ویدیو...");
}
const progress = Math.round((pInfo.progress || 0) * 100);
progressBar.style.width = `${progress}%`;
progressPercent.textContent = `${progress}%`;
} else if (pInfo.length && pInfo.index !== null) {
if (currentProgressPhase !== 0 || pInfo.index === 0) {
if (pInfo.index === 0) {
currentProgressPhase = 0;
totalUnitsInPhase = pInfo.length;
completedUnitsInPhase = 0;
addStatusMessage(`شروع پردازش (${totalUnitsInPhase} گام)...`);
}
}
completedUnitsInPhase = pInfo.index + 1;
const progress = Math.round((completedUnitsInPhase / totalUnitsInPhase) * 100);
progressBar.style.width = `${progress}%`;
progressPercent.textContent = `${progress}%`;
}
}
// Listen for results
function listenForResults(eventId) {
addStatusMessage('۳. در حال تولید ویدیو...');
const eventSource = new EventSource(`${SPACE_URL_BASE}/gradio_api/queue/data?session_hash=${currentSessionHash}`);
currentProgressPhase = -1;
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.event_id !== eventId && data.msg !== "queue_full") return;
switch (data.msg) {
case "process_starts":
addStatusMessage('عملیات در سرور آغاز شد.');
progressBar.style.width = '0%';
progressPercent.textContent = '0%';
break;
case "progress":
updateVisualProgress(data.progress_data);
break;
case "process_completed":
eventSource.close();
generateButton.disabled = false;
generateButton.innerHTML = '<i class="fas fa-magic mr-2"></i> ساخت ویدیو هوشمند';
progressBar.style.width = '100%';
progressPercent.textContent = '100%';
if (data.success && data.output?.data?.[0]?.video?.url) {
addStatusMessage('ویدیو با موفقیت ساخته شد! 🎉', 'success');
outputSection.classList.remove('hidden');
// Scroll to output section
setTimeout(() => {
outputSection.scrollIntoView({ behavior: 'smooth' });
}, 500);
outputVideo.src = data.output.data[0].video.url;
outputVideo.load();
// Set up download button
downloadButton.onclick = function() {
const a = document.createElement('a');
a.href = data.output.data[0].video.url;
a.download = `alpha-video-${new Date().toISOString().slice(0,10)}.mp4`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
playSuccessSound();
};
outputVideo.play().catch(e => {
addStatusMessage("برای مشاهده، دکمه پخش ویدیو را بزنید.", "info");
});
finalSeedElement.textContent = data.output.data[1] ? `سید نهایی: ${data.output.data[1]}` : '';
// Play completion sound
playCompletionSound();
} else {
const errorMsg = data.output?.error || data.error || "خطای نامشخص در تکمیل فرآیند.";
addStatusMessage(`تولید ویدیو ناموفق بود: ${errorMsg}`, 'error');
console.error('Process completed with error:', data);
playErrorSound();
}
break;
case "queue_full":
addStatusMessage('سرور مشغول است، لطفا کمی بعد تلاش کنید.', 'error');
eventSource.close();
generateButton.disabled = false;
generateButton.innerHTML = '<i class="fas fa-magic mr-2"></i> ساخت ویدیو هوشمند';
playErrorSound();
break;
}
};
eventSource.onerror = function(error) {
addStatusMessage('خطا در ارتباط با سرور. اتصال خود را بررسی کنید.', 'error');
console.error('EventSource error:', error);
eventSource.close();
generateButton.disabled = false;
generateButton.innerHTML = '<i class="fas fa-magic mr-2"></i> ساخت ویدیو هوشمند';
playErrorSound();
};
}
// Generate button click
generateButton.addEventListener('click', async () => {
clearStatusAndOutput();
const imageFile = imageFileInput.files[0];
// Validate inputs
if (currentGenerationMode === "image-to-video" && !imageFile) {
addStatusMessage('لطفاً ابتدا یک تصویر انتخاب کنید.', 'error');
playErrorSound();
return;
}
if (!promptInput.value.trim()) {
addStatusMessage('لطفاً متن راهنما (Prompt) را وارد کنید.', 'error');
playErrorSound();
return;
}
// Disable button and show loading state
generateButton.disabled = true;
generateButton.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> در حال پردازش...';
statusSection.classList.remove('hidden');
// Generate session hash
currentSessionHash = generateRandomHash();
// Upload image if in image-to-video mode
if (currentGenerationMode === "image-to-video") {
uploadedImageInfo = await uploadImage(imageFile);
if (!uploadedImageInfo) {
generateButton.disabled = false;
generateButton.innerHTML = '<i class="fas fa-magic mr-2"></i> ساخت ویدیو هوشمند';
return;
}
} else {
uploadedImageInfo = null;
}
// Prepare payload
const userSeed = parseInt(seedInput.value);
const generationPayload = {
fn_index: FN_INDEX_GENERATE,
data: [
promptInput.value,
negativePromptInput.value,
uploadedImageInfo,
(currentGenerationMode === "image-to-video" ? null : ""),
parseInt(outputHeightInput.value),
parseInt(outputWidthInput.value),
currentGenerationMode,
selectedApiDuration,
9,
(userSeed > 0) ? userSeed : Math.floor(Math.random() * (2**32 -1)),
(userSeed <= 0),
parseFloat(cfgScaleInput.value),
true
],
event_data: null,
session_hash: currentSessionHash,
};
// Submit to queue
const joinResponse = await submitToQueue(generationPayload);
if (joinResponse && joinResponse.event_id) {
listenForResults(joinResponse.event_id);
} else {
addStatusMessage('ارسال درخواست به سرور ناموفق بود.', 'error');
generateButton.disabled = false;
generateButton.innerHTML = '<i class="fas fa-magic mr-2"></i> ساخت ویدیو هوشمند';
playErrorSound();
}
});
// Sound effects
function playClickSound() {
const audio = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-select-click-1109.mp3');
audio.volume = 0.2;
audio.play().catch(e => console.log('Audio play failed:', e));
}
function playHoverSound() {
const audio = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-arcade-game-jump-coin-216.mp3');
audio.volume = 0.1;
audio.play().catch(e => console.log('Audio play failed:', e));
}
function playSuccessSound() {
const audio = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-correct-answer-tone-2870.mp3');
audio.volume = 0.2;
audio.play().catch(e => console.log('Audio play failed:', e));
}
function playErrorSound() {
const audio = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-warning-alarm-688.mp3');
audio.volume = 0.1;
audio.play().catch(e => console.log('Audio play failed:', e));
}
function playCompletionSound() {
const audio = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3');
audio.volume = 0.2;
audio.play().catch(e => console.log('Audio play failed:', e));
}
</script>
</body>
</html>