const jobs = {}; const JOB_CLEANUP_MS = 60 * 60 * 1000; // Clean up old jobs setInterval(() => { const now = Date.now(); Object.keys(jobs).forEach(id => { if (now - jobs[id].timestamp > JOB_CLEANUP_MS) delete jobs[id]; }); }, 60000); function createJob(batchId = null) { const id = Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 8); jobs[id] = { id, batchId, timestamp: Date.now(), // Updated State Machine: status: 'queued', // queued, downloading, processing, ready, error progress: 0, // 0-100 (approx) message: 'Waiting in queue...', fileUrl: null, error: null, videoTitle: "Pending..." }; return id; } function getJob(id) { return jobs[id]; } function getBatchJobs(batchId) { return Object.values(jobs).filter(j => j.batchId === batchId); } // --- GRANULAR UPDATES --- function updateJobStatus(jobId, status, message, progress = 0) { if (jobs[jobId]) { jobs[jobId].status = status; jobs[jobId].message = message; if (progress > 0) jobs[jobId].progress = progress; } } function setJobFile(jobId, fileUrl) { if (jobs[jobId]) { jobs[jobId].fileUrl = fileUrl; jobs[jobId].status = 'ready'; jobs[jobId].message = 'Done'; jobs[jobId].progress = 100; } } function setJobTitle(jobId, title) { if (jobs[jobId]) jobs[jobId].videoTitle = title; } function setJobError(jobId, message) { if (jobs[jobId]) { jobs[jobId].status = 'error'; jobs[jobId].error = message; jobs[jobId].message = message; // Show error as main message } } // --- RETRY LOGIC --- function resetJob(jobId) { if (jobs[jobId]) { jobs[jobId].status = 'queued'; jobs[jobId].error = null; jobs[jobId].message = 'Retrying...'; jobs[jobId].progress = 0; jobs[jobId].fileUrl = null; // Timestamp update to prevent cleanup jobs[jobId].timestamp = Date.now(); return true; } return false; } module.exports = { createJob, getJob, getBatchJobs, updateJobStatus, setJobFile, setJobTitle, setJobError, resetJob };