import "dotenv/config"; import express, { Request, Response } from "express"; import * as path from "path"; import downloadRoutes from "./downloadRoutes"; import webhookRoutes from "./webhookRoutes"; import jellyfinRoutes from "./jellyfinRoutes"; import { jobEvents, listJobs } from "./jobStore"; const app = express(); const PORT = parseInt(process.env.PORT || "7860", 10); // 7860 = HF Spaces default app.use(express.json()); app.use(express.static(path.join(__dirname, "..", "public"))); app.use("/api", downloadRoutes); app.use("/api", webhookRoutes); app.use("/api", jellyfinRoutes); // GET /api/events — Server-Sent Events stream of job status changes, so the // frontend can show "Downloading… Ready. Choose your resolution." live // without polling. app.get("/api/events", (req: Request, res: Response) => { res.set({ "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }); res.flushHeaders(); // Send current state immediately so a newly-opened tab isn't blank. res.write(`event: snapshot\ndata: ${JSON.stringify(listJobs())}\n\n`); const onUpdate = (job: unknown) => { res.write(`event: update\ndata: ${JSON.stringify(job)}\n\n`); }; jobEvents.on("update", onUpdate); const keepAlive = setInterval(() => res.write(": ping\n\n"), 25000); req.on("close", () => { clearInterval(keepAlive); jobEvents.off("update", onUpdate); }); }); app.get("/healthz", (_req: Request, res: Response) => res.json({ ok: true })); app.listen(PORT, () => { console.log(`Domain of Happenings listening on port ${PORT}`); });