Initial commit

This commit is contained in:
twotalesanimation
2026-05-19 22:20:29 +02:00
commit 0fbe856dce
173 changed files with 38316 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
import { NextRequest, NextResponse } from "next/server";
import fs from "fs";
import path from "path";
export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ key: string[] }> }
) {
const { key } = await params;
// key is a catch-all segment array, e.g. ["videos", "uuid-filename.mp4"]
const relativePath = key.join("/");
// Sanitize: prevent path traversal
const uploadDir = path.resolve(process.env.LOCAL_UPLOAD_DIR ?? "./uploads");
const filePath = path.resolve(path.join(uploadDir, relativePath));
if (!filePath.startsWith(uploadDir)) {
return new NextResponse("Forbidden", { status: 403 });
}
if (!fs.existsSync(filePath)) {
return new NextResponse("Not found", { status: 404 });
}
const stat = fs.statSync(filePath);
const ext = path.extname(filePath).toLowerCase();
const mimeMap: Record<string, string> = {
".mp4": "video/mp4",
".mov": "video/quicktime",
".avi": "video/x-msvideo",
".mxf": "application/mxf",
".webm": "video/webm",
};
const contentType = mimeMap[ext] ?? "application/octet-stream";
// Support range requests so the HTML5 video player can seek
const rangeHeader = req.headers.get("range");
if (rangeHeader) {
const [startStr, endStr] = rangeHeader.replace("bytes=", "").split("-");
const start = parseInt(startStr, 10);
const end = endStr ? parseInt(endStr, 10) : stat.size - 1;
const chunkSize = end - start + 1;
const stream = fs.createReadStream(filePath, { start, end });
const nodeStream = stream as unknown as ReadableStream;
return new NextResponse(nodeStream, {
status: 206,
headers: {
"Content-Range": `bytes ${start}-${end}/${stat.size}`,
"Accept-Ranges": "bytes",
"Content-Length": String(chunkSize),
"Content-Type": contentType,
},
});
}
const stream = fs.createReadStream(filePath) as unknown as ReadableStream;
return new NextResponse(stream, {
headers: {
"Content-Length": String(stat.size),
"Content-Type": contentType,
"Accept-Ranges": "bytes",
},
});
}