aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/web/app/api/assets/[assetId]/route.ts75
-rw-r--r--packages/shared/assetdb.ts19
2 files changed, 71 insertions, 23 deletions
diff --git a/apps/web/app/api/assets/[assetId]/route.ts b/apps/web/app/api/assets/[assetId]/route.ts
index 3bff79ba..66ec6754 100644
--- a/apps/web/app/api/assets/[assetId]/route.ts
+++ b/apps/web/app/api/assets/[assetId]/route.ts
@@ -2,7 +2,11 @@ import { createContextFromRequest } from "@/server/api/client";
import { and, eq } from "drizzle-orm";
import { assets } from "@hoarder/db/schema";
-import { readAsset } from "@hoarder/shared/assetdb";
+import {
+ createAssetReadStream,
+ getAssetSize,
+ readAssetMetadata,
+} from "@hoarder/shared/assetdb";
export const dynamic = "force-dynamic";
export async function GET(
@@ -22,35 +26,60 @@ export async function GET(
return Response.json({ error: "Asset not found" }, { status: 404 });
}
- const { asset, metadata } = await readAsset({
- userId: ctx.user.id,
- assetId: params.assetId,
- });
+ const [metadata, size] = await Promise.all([
+ readAssetMetadata({
+ userId: ctx.user.id,
+ assetId: params.assetId,
+ }),
+
+ getAssetSize({
+ userId: ctx.user.id,
+ assetId: params.assetId,
+ }),
+ ]);
const range = request.headers.get("Range");
if (range) {
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
- const end = parts[1] ? parseInt(parts[1], 10) : asset.length - 1;
-
- // TODO: Don't read the whole asset into memory in the first place
- const chunk = asset.subarray(start, end + 1);
- return new Response(chunk, {
- status: 206, // Partial Content
- headers: {
- "Content-Range": `bytes ${start}-${end}/${asset.length}`,
- "Accept-Ranges": "bytes",
- "Content-Length": chunk.length.toString(),
- "Content-type": metadata.contentType,
- },
+ const end = parts[1] ? parseInt(parts[1], 10) : size - 1;
+
+ const stream = createAssetReadStream({
+ userId: ctx.user.id,
+ assetId: params.assetId,
+ start,
+ end,
});
- } else {
- return new Response(asset, {
- status: 200,
- headers: {
- "Content-Length": asset.length.toString(),
- "Content-type": metadata.contentType,
+
+ return new Response(
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
+ stream as any,
+ {
+ status: 206, // Partial Content
+ headers: {
+ "Content-Range": `bytes ${start}-${end}/${size}`,
+ "Accept-Ranges": "bytes",
+ "Content-Length": (end - start + 1).toString(),
+ "Content-type": metadata.contentType,
+ },
},
+ );
+ } else {
+ const stream = createAssetReadStream({
+ userId: ctx.user.id,
+ assetId: params.assetId,
});
+
+ return new Response(
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
+ stream as any,
+ {
+ status: 200,
+ headers: {
+ "Content-Length": size.toString(),
+ "Content-type": metadata.contentType,
+ },
+ },
+ );
}
}
diff --git a/packages/shared/assetdb.ts b/packages/shared/assetdb.ts
index fb7d2461..2ef69279 100644
--- a/packages/shared/assetdb.ts
+++ b/packages/shared/assetdb.ts
@@ -123,6 +123,25 @@ export async function readAsset({
return { asset, metadata };
}
+export function createAssetReadStream({
+ userId,
+ assetId,
+ start,
+ end,
+}: {
+ userId: string;
+ assetId: string;
+ start?: number;
+ end?: number;
+}) {
+ const assetDir = getAssetDir(userId, assetId);
+
+ return fs.createReadStream(path.join(assetDir, "asset.bin"), {
+ start,
+ end,
+ });
+}
+
export async function readAssetMetadata({
userId,
assetId,