aboutsummaryrefslogtreecommitdiffstats
path: root/packages/api/utils/assets.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/api/utils/assets.ts')
-rw-r--r--packages/api/utils/assets.ts57
1 files changed, 57 insertions, 0 deletions
diff --git a/packages/api/utils/assets.ts b/packages/api/utils/assets.ts
new file mode 100644
index 00000000..d8a726a6
--- /dev/null
+++ b/packages/api/utils/assets.ts
@@ -0,0 +1,57 @@
+import { Context } from "hono";
+import { stream } from "hono/streaming";
+
+import {
+ createAssetReadStream,
+ getAssetSize,
+ readAssetMetadata,
+} from "@karakeep/shared/assetdb";
+
+import { toWebReadableStream } from "./upload";
+
+export async function serveAsset(c: Context, assetId: string, userId: string) {
+ const [metadata, size] = await Promise.all([
+ readAssetMetadata({
+ userId,
+ assetId,
+ }),
+
+ getAssetSize({
+ userId,
+ assetId,
+ }),
+ ]);
+
+ const range = c.req.header("Range");
+ if (range) {
+ const parts = range.replace(/bytes=/, "").split("-");
+ const start = parseInt(parts[0], 10);
+ const end = parts[1] ? parseInt(parts[1], 10) : size - 1;
+
+ const fStream = createAssetReadStream({
+ userId,
+ assetId,
+ start,
+ end,
+ });
+ c.status(206); // Partial Content
+ c.header("Content-Range", `bytes ${start}-${end}/${size}`);
+ c.header("Accept-Ranges", "bytes");
+ c.header("Content-Length", (end - start + 1).toString());
+ c.header("Content-type", metadata.contentType);
+ return stream(c, async (stream) => {
+ await stream.pipe(toWebReadableStream(fStream));
+ });
+ } else {
+ const fStream = createAssetReadStream({
+ userId,
+ assetId,
+ });
+ c.status(200);
+ c.header("Content-Length", size.toString());
+ c.header("Content-type", metadata.contentType);
+ return stream(c, async (stream) => {
+ await stream.pipe(toWebReadableStream(fStream));
+ });
+ }
+}