Files

37 lines
1.5 KiB
TypeScript
Raw Permalink Normal View History

2026-09-25 23:20:57 -05:00
import { createReadStream, statSync } from "node:fs";
import { Readable } from "node:stream";
import { finishDownloadJob, getDownloadJob, jsonError, requireDownloadAccess } from "../helpers/downloadJobs";
function contentDisposition(fileName: string) {
const ascii = fileName.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_");
return `attachment; filename="${ascii}"; filename*=UTF-8''${encodeURIComponent(fileName)}`;
}
export async function handle(request: Request) {
try {
const access = await requireDownloadAccess(request);
if (access.error) return access.error;
const jobId = new URL(request.url).searchParams.get("jobId") || "";
const job = getDownloadJob(jobId, access.userId);
if (!job || job.status !== "ready" || !job.filePath || !job.fileName) {
return jsonError("This download is not ready or has already been collected.", 404);
}
const size = statSync(job.filePath).size;
const stream = createReadStream(job.filePath);
stream.on("close", () => finishDownloadJob(job));
return new Response(Readable.toWeb(stream) as ReadableStream, {
headers: {
"Content-Type": job.kind === "playlist" ? "application/zip" : "audio/mpeg",
"Content-Length": String(size),
"Content-Disposition": contentDisposition(job.fileName),
"Cache-Control": "no-store",
},
});
} catch (error) {
return jsonError(error instanceof Error ? error.message : "Unable to send the file.", 500);
}
}