// 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');
// Check for search parameter in URL
const urlParams = new URLSearchParams(window.location.search);
const searchQuery = urlParams.get('search');
if (searchQuery) {
// Display search results
displaySearchResults(searchQuery);
}
// 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 = '
Please enter a YouTube URL
';
return;
}
// Extract video ID
const videoId = extractVideoId(youtubeUrl);
if (!videoId) {
processStatus.innerHTML = '
Invalid YouTube URL
';
return;
}
// Show loading indicator with spinner and text
processStatus.innerHTML = `
Processing video... This may take a few moments
`;
// Set a timeout to handle overly long processing
const timeoutId = setTimeout(() => {
processStatus.innerHTML = `
Processing is taking longer than expected. Please wait...
`;
}, 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 = `
${isNewlyProcessed ? 'Video processed successfully!' : 'Video was already processed!'}
`;
}).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 = `
Failed to load recent videos
`;
carouselPrev.classList.add('hidden');
carouselNext.classList.add('hidden');
});
}
// Display recent videos on page load
displayRecentVideos();
// Function to display search results
function displaySearchResults(query) {
// Try to use the modal if available
const searchModal = document.getElementById('search-results-modal');
const searchResultsContainer = document.getElementById('search-results-container');
if (searchModal && searchResultsContainer) {
// Update modal title with search term
const modalTitle = searchModal.querySelector('h3');
if (modalTitle) {
modalTitle.textContent = `Search Results for "${query}"`;
}
// Show the modal
searchModal.showModal();
// Clear previous results and show loading state
searchResultsContainer.innerHTML = `
Searching...
`;
// Fetch and display results in the modal
fetchAndDisplayResults(query, searchResultsContainer);
} else {
// Fallback to the old implementation if modal is not available
// Show a search results card with loading indicator
processStatus.innerHTML = `
Search Results for "${query}"
Searching...
`;
// Fetch and display results in the processStatus element
fetchAndDisplayResults(query, null, processStatus);
}
}
// Helper function to fetch and display search results
function fetchAndDisplayResults(query, container, fallbackContainer) {
// Fetch search results from API
fetch(`/api/video/search?query=${encodeURIComponent(query)}&limit=10`)
.then(response => {
if (!response.ok) {
throw new Error('Failed to perform search');
}
return response.json();
})
.then(results => {
if (results && results.length > 0) {
// Group results by video
const videoGroups = {};
// First pass: collect all video IDs that need titles
const videoIds = [];
results.forEach(result => {
const videoId = result.segment.video_id;
if (!videoGroups[videoId]) {
videoGroups[videoId] = {
videoId: videoId,
title: `Video ${videoId}`, // Default title, will be updated
segments: []
};
// Add to list of IDs to fetch titles for
videoIds.push(videoId);
}
videoGroups[videoId].segments.push(result);
});
// If we have video IDs, fetch their proper titles
const videoPromises = videoIds.map(videoId => {
return fetch(`/api/video/info/${videoId}`)
.then(response => response.ok ? response.json() : null)
.then(videoInfo => {
if (videoInfo && videoInfo.title && videoGroups[videoId]) {
videoGroups[videoId].title = videoInfo.title;
}
})
.catch(error => console.error(`Error fetching video info for ${videoId}:`, error));
});
// After all video titles are fetched, continue with rendering
Promise.all(videoPromises).then(() => {
// Generate results HTML
const resultsHTML = Object.values(videoGroups).map(group => {
const segmentsHTML = group.segments.map(result => {
return `