mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-09 20:12:10 +10:00
* fix: small ui fixes * feat: #171 * fix: improvements to library scanning on admin UI * feat: v2 download API * fix: add download context cleanup * fix: lint
74 lines
1.9 KiB
TypeScript
74 lines
1.9 KiB
TypeScript
import { type } from "arktype";
|
|
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
|
import contextManager from "~/server/internal/downloads/coordinator";
|
|
import libraryManager from "~/server/internal/library";
|
|
|
|
const GetChunk = type({
|
|
context: "string",
|
|
files: type({
|
|
filename: "string",
|
|
chunkIndex: "number",
|
|
}).array(),
|
|
}).configure(throwingArktype);
|
|
|
|
export default defineEventHandler(async (h3) => {
|
|
const body = await readDropValidatedBody(h3, GetChunk);
|
|
|
|
const context = await contextManager.fetchContext(body.context);
|
|
if (!context)
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: "Invalid download context.",
|
|
});
|
|
|
|
const streamFiles = [];
|
|
|
|
for (const file of body.files) {
|
|
const manifestFile = context.manifest[file.filename];
|
|
if (!manifestFile)
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: `Unknown file: ${file.filename}`,
|
|
});
|
|
|
|
const start = manifestFile.lengths
|
|
.slice(0, file.chunkIndex)
|
|
.reduce((a, b) => a + b, 0);
|
|
const end = start + manifestFile.lengths[file.chunkIndex];
|
|
|
|
streamFiles.push({ filename: file.filename, start, end });
|
|
}
|
|
|
|
setHeader(
|
|
h3,
|
|
"Content-Lengths",
|
|
streamFiles.map((e) => e.end - e.start).join(","),
|
|
); // Non-standard header, but we're cool like that 😎
|
|
|
|
for (const file of streamFiles) {
|
|
const gameReadStream = await libraryManager.readFile(
|
|
context.libraryId,
|
|
context.libraryPath,
|
|
context.versionName,
|
|
file.filename,
|
|
{ start: file.start, end: file.end },
|
|
);
|
|
if (!gameReadStream)
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "Failed to create read stream",
|
|
});
|
|
await gameReadStream.pipeTo(
|
|
new WritableStream({
|
|
write(chunk) {
|
|
h3.node.res.write(chunk);
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
await h3.node.res.end();
|
|
|
|
return;
|
|
});
|