thors1's picture
Initial DeepSite commit
40bc364 verified
Raw
History Blame Contribute Delete
23.5 kB
// State management
let currentMode = 'template';
let isRecording = false;
let isPlaying = false;
let uploadedImage = null;
let recordingInterval = null;
let recordingTime = 0;
let generationQueue = [];
let currentGeneration = null;
let selectedTemplate = null;
let selectedMusic = null;
let audioContext = null;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
initializeDragDrop();
initializeSliders();
initializeCheckboxes();
initializeGenerations();
});
// Drag and Drop Setup
function initializeDragDrop() {
const uploadZone = document.getElementById('uploadZone');
if (!uploadZone) return;
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
uploadZone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
uploadZone.addEventListener('dragenter', () => {
uploadZone.classList.add('dragover');
});
uploadZone.addEventListener('dragleave', () => {
uploadZone.classList.remove('dragover');
});
uploadZone.addEventListener('drop', (e) => {
uploadZone.classList.remove('dragover');
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
handleFile(files[0]);
}
});
}
// Initialize range sliders with value display
function initializeSliders() {
const sliders = document.querySelectorAll('.slider-thumb');
sliders.forEach(slider => {
const updateValue = () => {
const valueDisplay = slider.parentElement.querySelector('span:last-child');
if (valueDisplay) {
let suffix = '%';
if (slider.parentElement.textContent.includes('Blink')) suffix = '';
valueDisplay.textContent = slider.value + suffix;
}
};
slider.addEventListener('input', updateValue);
});
}
// Initialize checkboxes with visual feedback
function initializeCheckboxes() {
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', (e) => {
const label = e.target.closest('label');
if (label) {
if (e.target.checked) {
label.classList.add('bg-violet-50');
} else {
label.classList.remove('bg-violet-50');
}
}
});
});
}
// Initialize generations with demo data
function initializeGenerations() {
// Simulate real-time progress updates
setInterval(() => {
updateProcessingItems();
}, 3000);
}
// Mode selection
function selectMode(mode, element) {
currentMode = mode;
// Update UI
document.querySelectorAll('.mode-card').forEach(card => {
card.classList.remove('active');
});
element.classList.add('active');
// Show/hide sections with animation
const sections = {
template: document.getElementById('templateSection'),
music: document.getElementById('musicSection'),
voice: document.getElementById('voiceSection')
};
Object.values(sections).forEach(section => {
if (section) {
section.classList.add('hidden');
section.style.opacity = '0';
}
});
if (sections[mode]) {
sections[mode].classList.remove('hidden');
setTimeout(() => {
sections[mode].style.transition = 'opacity 0.2s ease';
sections[mode].style.opacity = '1';
}, 10);
}
lucide.createIcons();
}
// Image upload handling
function handleImageUpload(event) {
const file = event.target.files[0];
if (file) {
handleFile(file);
}
}
function handleFile(file) {
if (!file.type.startsWith('image/')) {
showNotification('Please upload an image file (JPG, PNG, WEBP)', 'error');
return;
}
if (file.size > 10 * 1024 * 1024) {
showNotification('File size must be less than 10MB', 'error');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
uploadedImage = e.target.result;
updateUploadUI(file.name);
showNotification('Image uploaded successfully!', 'success');
};
reader.readAsDataURL(file);
}
function updateUploadUI(filename) {
const placeholder = document.getElementById('uploadPlaceholder');
const preview = document.getElementById('imagePreview');
const previewImg = document.getElementById('previewImage');
const activePreview = document.getElementById('activePreview');
const previewPlaceholder = document.getElementById('previewPlaceholder');
const summaryInput = document.getElementById('summaryInput');
if (placeholder) placeholder.classList.add('hidden');
if (preview) {
preview.classList.remove('hidden');
const img = preview.querySelector('img');
if (img) img.src = uploadedImage;
}
if (previewPlaceholder) previewPlaceholder.classList.add('hidden');
if (activePreview) {
activePreview.classList.remove('hidden');
if (previewImg) previewImg.src = uploadedImage;
}
if (summaryInput) summaryInput.textContent = filename;
}
function removeImage() {
uploadedImage = null;
document.getElementById('uploadPlaceholder').classList.remove('hidden');
document.getElementById('imagePreview').classList.add('hidden');
document.getElementById('fileInput').value = '';
document.getElementById('previewPlaceholder').classList.remove('hidden');
document.getElementById('activePreview').classList.add('hidden');
document.getElementById('summaryInput').textContent = 'No image';
showNotification('Image removed', 'info');
}
// Template selection in sidebar
function selectTemplate(element, templateName) {
selectedTemplate = templateName;
document.querySelectorAll('.template-option').forEach(opt => {
opt.classList.remove('bg-violet-50', 'border-violet-300');
opt.classList.add('border-gray-200');
});
element.classList.remove('border-gray-200');
element.classList.add('bg-violet-50', 'border-violet-300');
// Update preview if image is uploaded
if (uploadedImage) {
showNotification(`Template "${templateName}" selected`, 'success');
}
}
// Template selection in grid
function selectTemplateCard(element) {
document.querySelectorAll('.template-card').forEach(card => {
card.classList.remove('selected');
});
element.classList.add('selected');
// Extract template name
const nameEl = element.querySelector('h4');
if (nameEl) selectedTemplate = nameEl.textContent;
// Switch to preview tab with animation
setTimeout(() => {
const previewBtn = document.querySelector('button[onclick*="preview"]');
if (previewBtn) switchTab('preview', previewBtn);
}, 200);
showNotification('Template selected', 'success');
}
// Tab switching
function switchTab(tabName, element) {
const tabs = ['previewTab', 'templatesTab', 'audioTab', 'captionsTab'];
tabs.forEach(tab => {
const el = document.getElementById(tab);
if (el) {
el.classList.add('hidden');
el.style.opacity = '0';
}
});
const selectedTab = document.getElementById(tabName + 'Tab');
if (selectedTab) {
selectedTab.classList.remove('hidden');
setTimeout(() => {
selectedTab.style.transition = 'opacity 0.3s ease';
selectedTab.style.opacity = '1';
}, 10);
}
document.querySelectorAll('.tab-active').forEach(btn => {
btn.classList.remove('tab-active');
btn.classList.add('text-gray-500');
});
if (element) {
element.classList.remove('text-gray-500');
element.classList.add('tab-active');
}
lucide.createIcons();
}
// Aspect ratio selection
function setAspect(btn) {
document.querySelectorAll('.aspect-btn').forEach(b => {
b.classList.remove('border-violet-500', 'bg-violet-50', 'text-violet-700');
b.classList.add('border-gray-200', 'text-gray-600');
});
btn.classList.remove('border-gray-200', 'text-gray-600');
btn.classList.add('border-violet-500', 'bg-violet-50', 'text-violet-700');
// Update preview aspect ratio
const activePreview = document.getElementById('activePreview');
if (activePreview) {
const ratio = btn.textContent.trim();
if (ratio === '1:1') activePreview.style.aspectRatio = '1/1';
else if (ratio === '4:5') activePreview.style.aspectRatio = '4/5';
else if (ratio === '9:16') activePreview.style.aspectRatio = '9/16';
else if (ratio === '16:9') activePreview.style.aspectRatio = '16/9';
}
}
// Recording functionality
function toggleRecording() {
const btn = document.getElementById('recordBtn');
const icon = document.getElementById('recordIcon');
const text = document.getElementById('recordText');
if (!isRecording) {
// Start recording
isRecording = true;
recordingTime = 0;
if (icon) {
icon.classList.add('recording-pulse');
icon.classList.remove('rounded-full');
icon.classList.add('rounded-sm');
}
if (text) text.textContent = 'Stop Recording (0:00)';
if (btn) {
btn.classList.remove('bg-red-50', 'border-red-200');
btn.classList.add('bg-red-100', 'border-red-300');
}
// Simulate recording timer
recordingInterval = setInterval(() => {
recordingTime++;
const mins = Math.floor(recordingTime / 60);
const secs = recordingTime % 60;
if (text) {
text.textContent = `Stop Recording (${mins}:${secs.toString().padStart(2, '0')})`;
}
}, 1000);
showNotification('Recording started...', 'info');
// Simulate recording limit
setTimeout(() => {
if (isRecording) toggleRecording();
}, 60000); // Auto stop after 60 seconds
} else {
// Stop recording
isRecording = false;
clearInterval(recordingInterval);
if (icon) {
icon.classList.remove('recording-pulse');
icon.classList.add('rounded-full');
icon.classList.remove('rounded-sm');
}
if (text) text.textContent = 'Record Voice';
if (btn) {
btn.classList.add('bg-red-50', 'border-red-200');
btn.classList.remove('bg-red-100', 'border-red-300');
}
showNotification(`Recording saved (${recordingTime}s)`, 'success');
// Add to generations
addGenerationToQueue({
name: `Voice Recording (${recordingTime}s)`,
type: 'Voice',
status: 'completed',
duration: `${recordingTime}s`,
resolution: '1080p'
});
}
}
// Play/Pause toggle for preview
function togglePlay() {
const playIcon = document.getElementById('playIcon');
const progressBar = document.getElementById('progressBar');
isPlaying = !isPlaying;
if (isPlaying) {
if (playIcon) {
playIcon.setAttribute('data-lucide', 'pause');
lucide.createIcons();
}
// Simulate progress
if (progressBar) {
progressBar.style.width = '0%';
setTimeout(() => {
progressBar.style.transition = 'width 15s linear';
progressBar.style.width = '100%';
}, 100);
}
showNotification('Playing preview...', 'info');
setTimeout(() => {
isPlaying = false;
if (playIcon) {
playIcon.setAttribute('data-lucide', 'play');
lucide.createIcons();
}
if (progressBar) {
progressBar.style.transition = 'none';
progressBar.style.width = '0%';
}
}, 15000);
} else {
if (playIcon) {
playIcon.setAttribute('data-lucide', 'play');
lucide.createIcons();
}
if (progressBar) {
progressBar.style.transition = 'none';
}
}
}
// Generate Lip Sync
function generateLipSync() {
if (!uploadedImage) {
showNotification('Please upload a cat photo first', 'error');
document.getElementById('uploadZone').scrollIntoView({ behavior: 'smooth' });
return;
}
if (!selectedTemplate && currentMode === 'template') {
showNotification('Please select a template', 'error');
return;
}
// Deduct credits
const creditsBadge = document.querySelector('.text-violet-900');
if (creditsBadge) {
let credits = parseInt(creditsBadge.textContent.replace(/,/g, ''));
credits -= 45;
creditsBadge.textContent = credits.toLocaleString() + ' credits';
}
// Add to generations
const generationId = Date.now();
const newGeneration = {
id: generationId,
name: selectedTemplate || 'Custom Generation',
type: currentMode === 'template' ? 'Template' : currentMode === 'music' ? 'Music' : 'Voice',
status: 'processing',
progress: 0,
timestamp: new Date(),
duration: '0:15',
resolution: '1080p'
};
addGenerationToQueue(newGeneration);
showNotification('Generation started! Check the Generations panel.', 'success');
// Simulate generation progress
simulateGenerationProgress(generationId);
}
// Add generation to queue and UI
function addGenerationToQueue(generation) {
generationQueue.push(generation);
renderGenerationCard(generation);
}
// Render generation card
function renderGenerationCard(generation) {
const container = document.querySelector('.overflow-y-auto.custom-scrollbar');
if (!container) return;
const card = document.createElement('div');
card.className = 'generation-card bg-white border border-gray-200 rounded-xl p-3 shadow-sm mb-3';
card.id = `gen-${generation.id}`;
const statusColor = generation.status === 'completed' ? 'green' : generation.status === 'failed' ? 'red' : 'amber';
card.innerHTML = `
<div class="flex items-start space-x-3">
<div class="w-16 h-16 bg-gray-100 rounded-lg flex-shrink-0 overflow-hidden relative">
${uploadedImage ? `<img src="${uploadedImage}" class="w-full h-full object-cover opacity-50">` : ''}
${generation.status === 'processing' ? `
<div class="absolute inset-0 flex items-center justify-center">
<div class="w-6 h-6 border-2 border-violet-600 border-t-transparent rounded-full animate-spin"></div>
</div>
` : ''}
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between mb-1">
<span class="text-[10px] font-medium text-${statusColor}-600 bg-${statusColor}-50 px-1.5 py-0.5 rounded capitalize">${generation.status}</span>
<button class="text-gray-400 hover:text-gray-600" onclick="deleteGeneration(${generation.id})">
<i data-lucide="x" class="w-3 h-3"></i>
</button>
</div>
<p class="text-xs font-medium text-gray-900 truncate">${generation.name}</p>
<p class="text-[10px] text-gray-500">${generation.type} • Just now</p>
${generation.status === 'processing' ? `
<div class="mt-2">
<div class="flex justify-between text-[10px] text-gray-500 mb-1">
<span>Rendering lip sync...</span>
<span class="progress-text">0%</span>
</div>
<div class="h-1 bg-gray-100 rounded-full overflow-hidden">
<div class="h-full progress-bar w-0 rounded-full transition-all duration-300"></div>
</div>
</div>
` : ''}
</div>
</div>
`;
container.insertBefore(card, container.firstChild);
lucide.createIcons();
}
// Simulate generation progress
function simulateGenerationProgress(generationId) {
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 15;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
completeGeneration(generationId);
}
updateGenerationProgress(generationId, progress);
}, 1000);
}
// Update generation progress UI
function updateGenerationProgress(generationId, progress) {
const card = document.getElementById(`gen-${generationId}`);
if (!card) return;
const bar = card.querySelector('.progress-bar');
const text = card.querySelector('.progress-text');
if (bar) bar.style.width = progress + '%';
if (text) text.textContent = Math.round(progress) + '%';
}
// Complete generation
function completeGeneration(generationId) {
const card = document.getElementById(`gen-${generationId}`);
if (!card) return;
card.innerHTML = `
<div class="flex items-start space-x-3">
<div class="w-16 h-16 bg-gray-100 rounded-lg flex-shrink-0 overflow-hidden relative group cursor-pointer">
<img src="${uploadedImage}" class="w-full h-full object-cover">
<div class="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
<i data-lucide="play" class="w-6 h-6 text-white fill-current"></i>
</div>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between mb-1">
<span class="text-[10px] font-medium text-green-600 bg-green-50 px-1.5 py-0.5 rounded">Completed</span>
<button class="text-gray-400 hover:text-gray-600" onclick="deleteGeneration(${generationId})">
<i data-lucide="x" class="w-3 h-3"></i>
</button>
</div>
<p class="text-xs font-medium text-gray-900 truncate">Ready to download</p>
<p class="text-[10px] text-gray-500">0:15 • 1080p</p>
<div class="flex items-center space-x-2 mt-2 opacity-0 group-hover:opacity-100 transition-opacity">
<button class="text-[10px] text-violet-600 hover:text-violet-700 font-medium" onclick="downloadGeneration(${generationId})">
Download
</button>
<button class="text-[10px] text-gray-500 hover:text-gray-700 font-medium">Duplicate</button>
</div>
</div>
</div>
`;
card.classList.add('group', 'cursor-pointer');
lucide.createIcons();
showNotification('Generation complete!', 'success');
}
// Delete generation
function deleteGeneration(generationId) {
const card = document.getElementById(`gen-${generationId}`);
if (card) {
card.style.opacity = '0';
card.style.transform = 'translateX(20px)';
setTimeout(() => card.remove(), 300);
generationQueue = generationQueue.filter(g => g.id !== generationId);
}
}
// Download generation
function downloadGeneration(generationId) {
showNotification('Downloading video...', 'success');
// Simulate download
setTimeout(() => {
showNotification('Download complete!', 'success');
}, 2000);
}
// Update processing items animation
function updateProcessingItems() {
const processingBars = document.querySelectorAll('.progress-bar');
processingBars.forEach(bar => {
if (bar.style.width && parseInt(bar.style.width) < 100) {
const currentWidth = parseInt(bar.style.width) || 0;
const newWidth = Math.min(currentWidth + Math.random() * 10, 95);
bar.style.width = newWidth + '%';
const text = bar.parentElement.previousElementSibling?.querySelector('.progress-text');
if (text) text.textContent = Math.round(newWidth) + '%';
}
});
}
// Reset form
function resetForm() {
if (confirm('Reset all settings? This will clear your image and selections.')) {
removeImage();
document.querySelectorAll('.template-option').forEach(opt => {
opt.classList.remove('bg-violet-50', 'border-violet-300');
opt.classList.add('border-gray-200');
});
document.querySelectorAll('.template-card').forEach(card => {
card.classList.remove('selected');
});
document.querySelectorAll('.slider-thumb').forEach(slider => {
slider.value = 50;
const display = slider.parentElement.querySelector('span:last-child');
if (display) display.textContent = '50%';
});
document.querySelectorAll('input[type="checkbox"]').forEach(cb => {
cb.checked = false;
});
selectMode('template', document.querySelector('.mode-card.active'));
document.querySelectorAll('.aspect-btn').forEach((btn, index) => {
if (index === 0) setAspect(btn);
});
showNotification('Form reset', 'info');
}
}
// Notification system
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
const colors = {
success: 'bg-green-500',
error: 'bg-red-500',
info: 'bg-violet-500'
};
notification.className = `fixed bottom-4 right-4 ${colors[type]} text-white px-4 py-2 rounded-lg shadow-lg z-50 text-sm font-medium transform translate-y-0 transition-all duration-300`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateY(10px)';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// Music selection
function selectMusic(element, trackName) {
selectedMusic = trackName;
document.querySelectorAll('.music-track').forEach(track => {
track.classList.remove('bg-pink-50', 'border-pink-300');
});
element.classList.add('bg-pink-50', 'border-pink-300');
showNotification(`Music track "${trackName}" selected`, 'success');
}
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Space to play/pause when preview is visible
if (e.code === 'Space' && uploadedImage && document.activePreview?.classList.contains('hidden')) {
e.preventDefault();
togglePlay();
}
// ESC to close/cancel
if (e.code === 'Escape' && isRecording) {
toggleRecording();
}
// G to generate
if (e.code === 'KeyG' && e.ctrlKey) {
e.preventDefault();
generateLipSync();
}
});