lukawskikacper's picture
Vibe coded implementation (with some manual fixes)
74cf6bd
raw
history blame
12.9 kB
// Index page functionality
document.addEventListener('DOMContentLoaded', () => {
const youtubeUrlInput = document.getElementById('youtube-url');
const processButton = document.getElementById('process-button');
const processStatus = document.getElementById('process-status');
const processingIndicator = document.getElementById('processing');
const recentlyProcessedCard = document.getElementById('recently-processed');
const videoListContainer = document.getElementById('video-list');
// Example video buttons
const exampleButtons = document.querySelectorAll('.example-video');
// Process button click handler
processButton.addEventListener('click', () => processVideo());
// Enter key in input field
youtubeUrlInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') processVideo();
});
// Example video buttons
exampleButtons.forEach(button => {
button.addEventListener('click', () => {
youtubeUrlInput.value = button.dataset.url;
processVideo();
});
});
// Process video function
function processVideo() {
const youtubeUrl = youtubeUrlInput.value.trim();
if (!youtubeUrl) {
processStatus.innerHTML = '<div class="alert alert-warning">Please enter a YouTube URL</div>';
return;
}
// Extract video ID
const videoId = extractVideoId(youtubeUrl);
if (!videoId) {
processStatus.innerHTML = '<div class="alert alert-error">Invalid YouTube URL</div>';
return;
}
// Show loading indicator with spinner and text
processStatus.innerHTML = `
<div class="flex items-center justify-center my-4">
<span class="loading loading-spinner loading-md text-primary"></span>
<span class="ml-2">Processing video... This may take a few moments</span>
</div>
`;
// Set a timeout to handle overly long processing
const timeoutId = setTimeout(() => {
processStatus.innerHTML = `
<div class="alert alert-warning">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span>Processing is taking longer than expected. Please wait...</span>
</div>
`;
}, 20000); // 20 seconds
// Send request to process the video
fetch('/api/video/process', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: youtubeUrl })
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to process video');
}
return response.json();
})
.then(data => {
// Clear timeout for long-running process
clearTimeout(timeoutId);
// Extract video ID from response (handles both old and new API formats)
const videoId = data.video ? data.video.video_id : data.video_id;
const isNewlyProcessed = data.newly_processed !== undefined ? data.newly_processed : true;
if (!videoId) {
throw new Error('Invalid response: Missing video ID');
}
// Get video title (for display)
const videoTitle = data.video ? data.video.title : (data.title || `Video ${videoId}`);
// Log for debugging
console.log('Process response:', {videoId, isNewlyProcessed, data});
// Show success message
processStatus.innerHTML = `
<div role="alert" class="alert alert-success">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>${isNewlyProcessed ? 'Video processed successfully!' : 'Video was already processed!'}</span>
<div>
<a href="/video/${videoId}" class="btn btn-sm btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Open Video
</a>
</div>
</div>
`;
// Update recent videos lists
displayRecentVideos();
loadFooterRecentVideos(); // Update footer videos as well
})
.catch(error => {
// Clear timeout for long-running process
clearTimeout(timeoutId);
// Show error message
console.error('Process error:', error);
processStatus.innerHTML = handleError(error);
});
}
// Display recently processed videos
function displayRecentVideos() {
// Show loading state
recentlyProcessedCard.classList.remove('hidden');
videoListContainer.innerHTML = `
<div class="flex justify-center items-center p-4">
<span class="loading loading-spinner loading-md"></span>
<span class="ml-2">Loading recent videos...</span>
</div>
`;
const carouselPrev = document.getElementById('carousel-prev');
const carouselNext = document.getElementById('carousel-next');
// Fetch recent videos from server
fetch('/api/video/recent?limit=5')
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch recent videos');
}
return response.json();
})
.then(videos => {
if (videos && videos.length > 0) {
// Limit to 5 videos
const limitedVideos = videos.slice(0, 5);
// Generate carousel items
const carouselItems = limitedVideos.map((video, index) => {
// Format date if available
let formattedDate = '';
if (video.created_at) {
const date = new Date(video.created_at * 1000); // Convert Unix timestamp to milliseconds
formattedDate = date.toLocaleDateString();
}
// Use title or default
const videoTitle = video.title || `Video ${video.video_id}`;
return `
<div id="video-${index}" class="carousel-item">
<a href="/video/${video.video_id}" class="card bg-base-100 shadow-sm hover:shadow-md transition-all w-64 md:w-72 flex flex-col">
<figure class="w-full h-36 overflow-hidden">
<img src="https://img.youtube.com/vi/${video.video_id}/mqdefault.jpg" alt="Thumbnail" class="w-full h-full object-cover">
</figure>
<div class="card-body p-3">
<h3 class="card-title text-sm line-clamp-2">${videoTitle}</h3>
<div class="text-xs opacity-70">${formattedDate}</div>
</div>
</a>
</div>
`;
}).join('');
// Add carousel items to container
videoListContainer.innerHTML = carouselItems;
// Setup navigation arrows
if (limitedVideos.length > 1) {
// Show arrows for multiple videos
let currentIndex = 0;
const maxIndex = limitedVideos.length - 1;
// Show navigation arrows
carouselPrev.classList.remove('hidden');
carouselNext.classList.remove('hidden');
// Left button is disabled by default (we're at the start)
const prevButton = carouselPrev.querySelector('button');
const nextButton = carouselNext.querySelector('button');
prevButton.classList.add('btn-disabled');
// Functions to update button states
const updateButtonStates = () => {
if (currentIndex === 0) {
prevButton.classList.add('btn-disabled');
} else {
prevButton.classList.remove('btn-disabled');
}
if (currentIndex === maxIndex) {
nextButton.classList.add('btn-disabled');
} else {
nextButton.classList.remove('btn-disabled');
}
};
// Setup navigation buttons
prevButton.addEventListener('click', () => {
if (currentIndex > 0) {
currentIndex--;
document.getElementById(`video-${currentIndex}`).scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
});
updateButtonStates();
}
});
nextButton.addEventListener('click', () => {
if (currentIndex < maxIndex) {
currentIndex++;
document.getElementById(`video-${currentIndex}`).scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
});
updateButtonStates();
}
});
} else {
// Hide arrows for single video
carouselPrev.classList.add('hidden');
carouselNext.classList.add('hidden');
}
} else {
recentlyProcessedCard.classList.add('hidden');
carouselPrev.classList.add('hidden');
carouselNext.classList.add('hidden');
}
})
.catch(error => {
console.error('Error fetching recent videos:', error);
videoListContainer.innerHTML = `
<div class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Failed to load recent videos</span>
</div>
`;
carouselPrev.classList.add('hidden');
carouselNext.classList.add('hidden');
});
}
// Display recent videos on page load
displayRecentVideos();
});