ai-video-generator / index.html
zdwalter's picture
Add 2 files
20a11bf verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Video Creator | Transform Images to Video Scenes</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.scene-card:hover .scene-actions {
opacity: 1;
}
.scene-actions {
opacity: 0;
transition: opacity 0.3s ease;
}
.dropzone {
border: 2px dashed #cbd5e0;
transition: all 0.3s ease;
}
.dropzone.active {
border-color: #667eea;
background-color: #ebf4ff;
}
.progress-bar {
transition: width 0.5s ease;
}
.video-preview {
aspect-ratio: 16/9;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
</style>
</head>
<body class="bg-gray-50 text-gray-800">
<div class="min-h-screen flex flex-col">
<!-- Header -->
<header class="bg-indigo-600 text-white shadow-lg">
<div class="container mx-auto px-4 py-6">
<div class="flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fas fa-film text-2xl"></i>
<h1 class="text-2xl font-bold">AI Video Creator</h1>
</div>
<nav>
<ul class="flex space-x-6">
<li><a href="#" class="hover:text-indigo-200 transition">Home</a></li>
<li><a href="#" class="hover:text-indigo-200 transition">Templates</a></li>
<li><a href="#" class="hover:text-indigo-200 transition">Pricing</a></li>
<li><a href="#" class="hover:text-indigo-200 transition">Help</a></li>
</ul>
</nav>
<div>
<button class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium hover:bg-indigo-50 transition">
Sign In
</button>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-grow container mx-auto px-4 py-8">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Left Panel - Scene Management -->
<div class="lg:col-span-1 bg-white rounded-xl shadow-md p-6 h-fit">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-semibold">Video Scenes</h2>
<button id="add-scene-btn" class="bg-indigo-600 text-white p-2 rounded-full hover:bg-indigo-700 transition">
<i class="fas fa-plus"></i>
</button>
</div>
<div id="scenes-container" class="space-y-4 max-h-[500px] overflow-y-auto pr-2">
<!-- Scene cards will be added here dynamically -->
<div class="text-center py-8 text-gray-500" id="empty-state">
<i class="fas fa-images text-4xl mb-3"></i>
<p>No scenes added yet</p>
<p class="text-sm">Click the + button to add your first scene</p>
</div>
</div>
<div class="mt-6 pt-6 border-t border-gray-200">
<h3 class="font-medium mb-3">Scene Settings</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Transition Effect</label>
<select class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-indigo-500 focus:border-indigo-500">
<option>Fade</option>
<option>Slide Left</option>
<option>Slide Right</option>
<option>Zoom</option>
<option>None</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Scene Duration (seconds)</label>
<input type="number" min="1" max="10" value="3" class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-indigo-500 focus:border-indigo-500">
</div>
</div>
</div>
</div>
<!-- Center Panel - Scene Editor -->
<div class="lg:col-span-2 space-y-6">
<!-- Current Scene Editor -->
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold">Scene Editor</h2>
<div class="text-sm text-gray-500" id="current-scene-indicator">No scene selected</div>
</div>
<div id="scene-editor" class="text-center py-12 bg-gray-100 rounded-lg">
<div id="empty-editor" class="space-y-3">
<i class="fas fa-image text-4xl text-gray-400"></i>
<p class="text-gray-500">Select or add a scene to edit</p>
</div>
<div id="active-editor" class="hidden">
<div class="mb-4">
<div class="relative mx-auto max-w-2xl">
<div id="scene-preview" class="bg-gray-200 rounded-lg overflow-hidden video-preview flex items-center justify-center">
<img id="scene-image" src="" alt="Scene preview" class="hidden max-h-full max-w-full">
<div id="no-image" class="text-gray-500 p-4">
<i class="fas fa-image text-4xl mb-3"></i>
<p>No image selected</p>
</div>
</div>
<div id="processing-overlay" class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
<div class="text-white text-center">
<i class="fas fa-cog fa-spin text-3xl mb-2"></i>
<p>AI Processing...</p>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">AI Enhancements</label>
<select id="ai-enhancement" class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="none">None</option>
<option value="upscale">Upscale Resolution</option>
<option value="color">Color Correction</option>
<option value="animate">Animate Elements</option>
<option value="style">Artistic Style</option>
<option value="background">Background Replacement</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">AI Style</label>
<select id="ai-style" class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="realistic">Realistic</option>
<option value="cartoon">Cartoon</option>
<option value="watercolor">Watercolor</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="fantasy">Fantasy</option>
</select>
</div>
</div>
<div class="mt-4">
<div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer">
<input type="file" id="scene-upload" accept="image/*" class="hidden">
<div class="space-y-2">
<i class="fas fa-cloud-upload-alt text-3xl text-indigo-500"></i>
<p class="font-medium">Drag & drop your image here</p>
<p class="text-sm text-gray-500">or click to browse files</p>
<p class="text-xs text-gray-400">Supports JPG, PNG (Max 10MB)</p>
</div>
</div>
</div>
<div class="mt-6">
<button id="process-scene-btn" class="w-full bg-indigo-600 text-white py-3 px-4 rounded-lg font-medium hover:bg-indigo-700 transition flex items-center justify-center space-x-2">
<i class="fas fa-magic"></i>
<span>Apply AI Enhancements</span>
</button>
</div>
</div>
</div>
</div>
<!-- Video Preview -->
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold mb-4">Video Preview</h2>
<div class="bg-gray-900 rounded-lg overflow-hidden video-preview flex items-center justify-center">
<div id="video-empty" class="text-gray-400 p-6 text-center">
<i class="fas fa-video text-4xl mb-3"></i>
<p>Your video will appear here</p>
<p class="text-sm mt-2">Add and process scenes to generate preview</p>
</div>
<video id="video-preview" class="hidden w-full h-full object-contain" controls></video>
</div>
<div class="mt-6">
<div id="progress-container" class="hidden">
<div class="flex justify-between text-sm text-gray-600 mb-1">
<span>Generating video...</span>
<span id="progress-percent">0%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div id="progress-bar" class="progress-bar bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
</div>
<button id="generate-video-btn" class="w-full bg-green-600 text-white py-3 px-4 rounded-lg font-medium hover:bg-green-700 transition flex items-center justify-center space-x-2 mt-4">
<i class="fas fa-play-circle"></i>
<span>Generate Final Video</span>
</button>
</div>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="bg-gray-800 text-white py-8">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
<div>
<h3 class="text-lg font-semibold mb-4">AI Video Creator</h3>
<p class="text-gray-400">Transform your images into stunning videos with AI-powered enhancements and animations.</p>
</div>
<div>
<h4 class="font-medium mb-4">Features</h4>
<ul class="space-y-2 text-gray-400">
<li><a href="#" class="hover:text-white transition">AI Enhancements</a></li>
<li><a href="#" class="hover:text-white transition">Scene Transitions</a></li>
<li><a href="#" class="hover:text-white transition">Video Templates</a></li>
<li><a href="#" class="hover:text-white transition">Cloud Processing</a></li>
</ul>
</div>
<div>
<h4 class="font-medium mb-4">Resources</h4>
<ul class="space-y-2 text-gray-400">
<li><a href="#" class="hover:text-white transition">Documentation</a></li>
<li><a href="#" class="hover:text-white transition">Tutorials</a></li>
<li><a href="#" class="hover:text-white transition">API Reference</a></li>
<li><a href="#" class="hover:text-white transition">Community</a></li>
</ul>
</div>
<div>
<h4 class="font-medium mb-4">Company</h4>
<ul class="space-y-2 text-gray-400">
<li><a href="#" class="hover:text-white transition">About Us</a></li>
<li><a href="#" class="hover:text-white transition">Careers</a></li>
<li><a href="#" class="hover:text-white transition">Privacy Policy</a></li>
<li><a href="#" class="hover:text-white transition">Terms of Service</a></li>
</ul>
</div>
</div>
<div class="border-t border-gray-700 mt-8 pt-8 text-center text-gray-400">
<p>&copy; 2023 AI Video Creator. All rights reserved.</p>
</div>
</div>
</footer>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const addSceneBtn = document.getElementById('add-scene-btn');
const scenesContainer = document.getElementById('scenes-container');
const emptyState = document.getElementById('empty-state');
const sceneEditor = document.getElementById('scene-editor');
const emptyEditor = document.getElementById('empty-editor');
const activeEditor = document.getElementById('active-editor');
const currentSceneIndicator = document.getElementById('current-scene-indicator');
const dropzone = document.getElementById('dropzone');
const sceneUpload = document.getElementById('scene-upload');
const sceneImage = document.getElementById('scene-image');
const noImage = document.getElementById('no-image');
const processSceneBtn = document.getElementById('process-scene-btn');
const processingOverlay = document.getElementById('processing-overlay');
const generateVideoBtn = document.getElementById('generate-video-btn');
const videoPreview = document.getElementById('video-preview');
const videoEmpty = document.getElementById('video-empty');
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const progressPercent = document.getElementById('progress-percent');
// State
let scenes = [];
let currentSceneId = null;
// Add new scene
addSceneBtn.addEventListener('click', function() {
const sceneId = Date.now().toString();
const scene = {
id: sceneId,
title: `Scene ${scenes.length + 1}`,
image: null,
processed: false,
processing: false
};
scenes.push(scene);
renderScenes();
// Select the new scene
selectScene(sceneId);
// Hide empty state if it's the first scene
if (scenes.length === 1) {
emptyState.classList.add('hidden');
}
});
// Render all scenes
function renderScenes() {
scenesContainer.innerHTML = '';
scenes.forEach(scene => {
const sceneCard = document.createElement('div');
sceneCard.className = `scene-card bg-white border rounded-lg p-4 relative ${currentSceneId === scene.id ? 'border-indigo-500 ring-1 ring-indigo-500' : 'border-gray-200'}`;
sceneCard.dataset.id = scene.id;
sceneCard.innerHTML = `
<div class="flex items-start space-x-3">
<div class="flex-shrink-0 w-16 h-16 bg-gray-100 rounded-md overflow-hidden flex items-center justify-center">
${scene.image ?
`<img src="${scene.image}" alt="Scene thumbnail" class="w-full h-full object-cover">` :
`<i class="fas fa-image text-gray-400"></i>`}
</div>
<div class="flex-grow">
<h3 class="font-medium">${scene.title}</h3>
<p class="text-sm text-gray-500">
${scene.processing ?
'<span class="text-yellow-600"><i class="fas fa-spinner fa-spin mr-1"></i> Processing</span>' :
(scene.processed ? '<span class="text-green-600"><i class="fas fa-check-circle mr-1"></i> Processed</span>' : 'Not processed')}
</p>
</div>
</div>
<div class="scene-actions absolute top-2 right-2 flex space-x-1">
<button class="edit-scene p-1 text-gray-500 hover:text-indigo-600 transition">
<i class="fas fa-pencil-alt text-sm"></i>
</button>
<button class="delete-scene p-1 text-gray-500 hover:text-red-600 transition">
<i class="fas fa-trash-alt text-sm"></i>
</button>
</div>
`;
scenesContainer.appendChild(sceneCard);
// Add event listeners to the buttons we just created
sceneCard.querySelector('.edit-scene').addEventListener('click', (e) => {
e.stopPropagation();
// In a real app, we'd implement scene title editing
alert('Edit scene title functionality would go here');
});
sceneCard.querySelector('.delete-scene').addEventListener('click', (e) => {
e.stopPropagation();
deleteScene(scene.id);
});
// Select scene on click
sceneCard.addEventListener('click', () => {
selectScene(scene.id);
});
});
}
// Select a scene
function selectScene(sceneId) {
currentSceneId = sceneId;
const scene = scenes.find(s => s.id === sceneId);
if (scene) {
// Update UI
currentSceneIndicator.textContent = `Editing: ${scene.title}`;
emptyEditor.classList.add('hidden');
activeEditor.classList.remove('hidden');
// Show scene image if it exists
if (scene.image) {
sceneImage.src = scene.image;
sceneImage.classList.remove('hidden');
noImage.classList.add('hidden');
} else {
sceneImage.classList.add('hidden');
noImage.classList.remove('hidden');
}
// Re-render scenes to update selected state
renderScenes();
}
}
// Delete a scene
function deleteScene(sceneId) {
if (confirm('Are you sure you want to delete this scene?')) {
scenes = scenes.filter(scene => scene.id !== sceneId);
// If we deleted the currently selected scene, clear the editor
if (currentSceneId === sceneId) {
currentSceneId = null;
emptyEditor.classList.remove('hidden');
activeEditor.classList.add('hidden');
currentSceneIndicator.textContent = 'No scene selected';
}
// Show empty state if no scenes left
if (scenes.length === 0) {
emptyState.classList.remove('hidden');
}
renderScenes();
}
}
// Drag and drop for image upload
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
dropzone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropzone.classList.add('active');
}
function unhighlight() {
dropzone.classList.remove('active');
}
dropzone.addEventListener('drop', handleDrop, false);
dropzone.addEventListener('click', () => sceneUpload.click());
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
sceneUpload.addEventListener('change', function() {
handleFiles(this.files);
});
function handleFiles(files) {
if (!currentSceneId) return;
const file = files[0];
if (!file.type.match('image.*')) {
alert('Please select an image file');
return;
}
if (file.size > 10 * 1024 * 1024) {
alert('File size should be less than 10MB');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const scene = scenes.find(s => s.id === currentSceneId);
if (scene) {
scene.image = e.target.result;
scene.processed = false;
renderScenes();
// Update the editor view
sceneImage.src = e.target.result;
sceneImage.classList.remove('hidden');
noImage.classList.add('hidden');
}
};
reader.readAsDataURL(file);
}
// Process scene with AI
processSceneBtn.addEventListener('click', function() {
if (!currentSceneId) return;
const scene = scenes.find(s => s.id === currentSceneId);
if (!scene || !scene.image) {
alert('Please add an image to the scene first');
return;
}
const enhancement = document.getElementById('ai-enhancement').value;
const style = document.getElementById('ai-style').value;
// Simulate AI processing
scene.processing = true;
renderScenes();
processingOverlay.classList.remove('hidden');
processSceneBtn.disabled = true;
// This would be an API call in a real app
setTimeout(() => {
// Simulate successful processing
scene.processing = false;
scene.processed = true;
// In a real app, we'd get the processed image from the API
// For demo, we'll just add a filter to simulate processing
sceneImage.style.filter = getFilterForStyle(style);
processingOverlay.classList.add('hidden');
processSceneBtn.disabled = false;
renderScenes();
// Show success message
alert(`Scene processed with ${enhancement} enhancement and ${style} style!`);
}, 3000);
});
function getFilterForStyle(style) {
switch(style) {
case 'cartoon': return 'contrast(1.5) saturate(1.5)';
case 'watercolor': return 'contrast(1.2) brightness(1.1) sepia(0.3) saturate(1.5)';
case 'cyberpunk': return 'contrast(1.8) brightness(0.8) hue-rotate(-20deg) saturate(2)';
case 'fantasy': return 'contrast(1.3) brightness(1.1) hue-rotate(10deg) saturate(1.8)';
default: return 'none';
}
}
// Generate final video
generateVideoBtn.addEventListener('click', function() {
if (scenes.length === 0) {
alert('Please add at least one scene');
return;
}
const unprocessedScenes = scenes.filter(s => !s.processed);
if (unprocessedScenes.length > 0) {
if (!confirm(`${unprocessedScenes.length} scenes are not processed. Do you want to continue without processing them?`)) {
return;
}
}
// Simulate video generation
progressContainer.classList.remove('hidden');
generateVideoBtn.disabled = true;
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress > 100) progress = 100;
progressBar.style.width = `${progress}%`;
progressPercent.textContent = `${Math.floor(progress)}%`;
if (progress === 100) {
clearInterval(interval);
// Simulate video being ready
setTimeout(() => {
// In a real app, we'd have an actual video URL from the API
videoEmpty.classList.add('hidden');
videoPreview.classList.remove('hidden');
// For demo purposes, we'll just show a message
videoPreview.innerHTML = `
<div class="h-full flex items-center justify-center text-white text-center p-6">
<div>
<i class="fas fa-check-circle text-4xl text-green-400 mb-3"></i>
<h3 class="text-xl font-medium mb-2">Video Generated Successfully!</h3>
<p class="mb-4">Your AI-enhanced video is ready.</p>
<button class="bg-indigo-600 hover:bg-indigo-700 px-4 py-2 rounded-lg transition">
Download Video
</button>
</div>
</div>
`;
progressContainer.classList.add('hidden');
generateVideoBtn.disabled = false;
}, 500);
}
}, 300);
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=zdwalter/ai-video-generator" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>