Spaces:
No application file
No application file
| 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>): 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]; | |
| } | |