document.addEventListener('DOMContentLoaded', function() {
const socket = io();
let currentJobId = null;
let activeJobs = new Map(); // Track all jobs by ID
// Elements
const youtubeUrlInput = document.getElementById('youtube-url');
const transcribeBtn = document.getElementById('transcribe-btn');
const queueContainer = document.getElementById('queue-container');
const queueList = document.getElementById('queue-list');
const statusContainer = document.getElementById('status-container');
const statusText = document.getElementById('status-text');
const jobIdElement = document.getElementById('job-id');
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
const currentMessage = document.getElementById('current-message');
const resultContainer = document.getElementById('result-container');
const transcriptPreview = document.getElementById('transcript-preview');
const downloadTxtBtn = document.getElementById('download-txt');
const downloadSrtBtn = document.getElementById('download-srt');
const errorContainer = document.getElementById('error-container');
const errorText = document.getElementById('error-text');
// Handle form submission
transcribeBtn.addEventListener('click', function() {
const youtubeUrl = youtubeUrlInput.value.trim();
if (!youtubeUrl) {
showError('Please enter a valid YouTube URL');
return;
}
if (!isValidYouTubeUrl(youtubeUrl)) {
showError('The URL does not appear to be a valid YouTube URL');
return;
}
startTranscription(youtubeUrl);
});
// Set up socket listeners once
socket.on('status_update', handleStatusUpdate);
socket.on('queue_update', handleQueueUpdate);
// Start the transcription process
function startTranscription(youtubeUrl) {
// Reset result UI
resetResultUI();
// Disable the button during processing
transcribeBtn.disabled = true;
transcribeBtn.innerText = 'Processing...';
// Send the transcription request
fetch('/api/transcribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ youtube_url: youtubeUrl })
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to start transcription');
}
return response.json();
})
.then(data => {
// Handle multiple jobs (playlist) or single job
if (data.job_ids) {
// Playlist case
showMessage(`Added ${data.job_ids.length} videos to the queue`);
// Join socket rooms for all jobs
data.job_ids.forEach(jobId => {
socket.emit('join', { job_id: jobId });
});
// Show the queue immediately
fetchAndRenderQueue();
} else {
// Single video case
currentJobId = data.job_id;
// Join the socket.io room for this job
socket.emit('join', { job_id: currentJobId });
// Show the queue
fetchAndRenderQueue();
}
// Reset the input field and button after submission
resetButton();
})
.catch(error => {
showError(error.message);
resetButton();
});
}
// Fetch and display the current queue
function fetchAndRenderQueue() {
fetch('/api/queue')
.then(response => response.json())
.then(data => {
handleQueueUpdate(data);
})
.catch(error => {
console.error("Error fetching queue:", error);
});
}
// Handle queue updates from WebSocket
function handleQueueUpdate(data) {
const queue = data.queue || [];
// Show or hide queue container based on if there are items
if (queue.length > 0) {
queueContainer.classList.remove('hidden');
renderQueue(queue);
} else {
queueContainer.classList.add('hidden');
}
}
// Render the queue items
function renderQueue(queue) {
// Clear the existing queue
queueList.innerHTML = '';
// Add each queue item
queue.forEach(job => {
// Store in our local tracking
activeJobs.set(job.job_id, job);
// Create the queue item element
const queueItem = document.createElement('div');
queueItem.className = 'queue-item';
queueItem.dataset.jobId = job.job_id;
// Create the thumbnail
let thumbnailUrl = '/api/thumbnail/' + job.job_id;
if (!job.thumbnail || job.thumbnail === '') {
thumbnailUrl = 'https://via.placeholder.com/120x68?text=No+Thumbnail';
}
// Format duration
let durationText = 'Unknown';
if (job.duration && !isNaN(job.duration)) {
const minutes = Math.floor(job.duration / 60);
const seconds = job.duration % 60;
durationText = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
// Set the HTML content for the queue item
queueItem.innerHTML = `