import * as fs from "fs"; import * as path from "path"; import { EventEmitter } from "events"; import { DownloadJob } from "./types"; const DATA_DIR = path.join(__dirname, "..", "data"); const JOBS_FILE = path.join(DATA_DIR, "jobs.json"); if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } if (!fs.existsSync(JOBS_FILE)) { fs.writeFileSync(JOBS_FILE, "[]", "utf-8"); } // Fired every time a job is created or updated, so the SSE route can push // live status to the frontend without polling the file. export const jobEvents = new EventEmitter(); function readAll(): DownloadJob[] { const raw = fs.readFileSync(JOBS_FILE, "utf-8"); try { return JSON.parse(raw) as DownloadJob[]; } catch { return []; } } function writeAll(jobs: DownloadJob[]): void { fs.writeFileSync(JOBS_FILE, JSON.stringify(jobs, null, 2), "utf-8"); } export function listJobs(): DownloadJob[] { return readAll().sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1)); } export function getJob(id: string): DownloadJob | undefined { return readAll().find((j) => j.id === id); } export function createJob(title: string, sourceUrl: string): DownloadJob { const now = new Date().toISOString(); const job: DownloadJob = { id: `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, title, sourceUrl, status: "queued", progress: 0, bytesReceived: 0, bytesTotal: null, filePath: null, error: null, createdAt: now, updatedAt: now, }; const jobs = readAll(); jobs.push(job); writeAll(jobs); jobEvents.emit("update", job); return job; } export function updateJob(id: string, patch: Partial): DownloadJob | undefined { const jobs = readAll(); const idx = jobs.findIndex((j) => j.id === id); if (idx === -1) return undefined; jobs[idx] = { ...jobs[idx], ...patch, updatedAt: new Date().toISOString() }; writeAll(jobs); jobEvents.emit("update", jobs[idx]); return jobs[idx]; }