import cacheHandler from "~~/server/internal/cache"; import { defineClientEventHandler } from "~~/server/internal/clients/event-handler"; import prisma from "~~/server/internal/db/database"; import libraryManager from "~~/server/internal/library"; const chunkSize = 1024 * 1024 * 64; const gameLookupCache = cacheHandler.createCache<{ libraryId: string | null; libraryPath: string; }>("downloadGameLookupCache"); export default defineClientEventHandler(async (h3) => { const query = getQuery(h3); const gameId = query.id?.toString(); const versionName = query.version?.toString(); const filename = query.name?.toString(); const chunkIndex = parseInt(query.chunk?.toString() ?? "?"); if (!gameId || !versionName || !filename || Number.isNaN(chunkIndex)) throw createError({ statusCode: 400, message: "Invalid chunk arguments", }); let game = await gameLookupCache.getItem(gameId); if (!game) { game = await prisma.game.findUnique({ where: { id: gameId, }, select: { libraryId: true, libraryPath: true, }, }); if (!game || !game.libraryId) throw createError({ statusCode: 400, message: "Invalid game ID" }); await gameLookupCache.setItem(gameId, game); } if (!game.libraryId) throw createError({ statusCode: 500, message: "Somehow, we got here.", }); const peek = await libraryManager.peekFile( game.libraryId, game.libraryPath, versionName, filename, ); if (!peek) throw createError({ status: 400, message: "Failed to peek file" }); const start = chunkIndex * chunkSize; const end = Math.min((chunkIndex + 1) * chunkSize, peek.size); const currentChunkSize = end - start; setHeader(h3, "Content-Length", currentChunkSize); if (start >= end) throw createError({ statusCode: 400, message: "Invalid chunk index", }); const gameReadStream = await libraryManager.readFile( game.libraryId, game.libraryPath, versionName, filename, { start, end }, ); if (!gameReadStream) throw createError({ statusCode: 400, message: "Failed to create stream", }); return sendStream(h3, gameReadStream); });