aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/app/api/assets/[assetId]/route.ts32
-rw-r--r--apps/web/components/dashboard/preview/AttachmentBox.tsx2
-rw-r--r--apps/web/components/dashboard/preview/LinkContentSection.tsx19
3 files changed, 47 insertions, 6 deletions
diff --git a/apps/web/app/api/assets/[assetId]/route.ts b/apps/web/app/api/assets/[assetId]/route.ts
index 73237d8d..3bff79ba 100644
--- a/apps/web/app/api/assets/[assetId]/route.ts
+++ b/apps/web/app/api/assets/[assetId]/route.ts
@@ -27,10 +27,30 @@ export async function GET(
assetId: params.assetId,
});
- return new Response(asset, {
- status: 200,
- headers: {
- "Content-type": metadata.contentType,
- },
- });
+ 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,
+ },
+ });
+ } else {
+ return new Response(asset, {
+ status: 200,
+ headers: {
+ "Content-Length": asset.length.toString(),
+ "Content-type": metadata.contentType,
+ },
+ });
+ }
}
diff --git a/apps/web/components/dashboard/preview/AttachmentBox.tsx b/apps/web/components/dashboard/preview/AttachmentBox.tsx
index 436f1026..d631f4d9 100644
--- a/apps/web/components/dashboard/preview/AttachmentBox.tsx
+++ b/apps/web/components/dashboard/preview/AttachmentBox.tsx
@@ -20,6 +20,7 @@ import {
Pencil,
Plus,
Trash2,
+ Video,
} from "lucide-react";
import {
@@ -44,6 +45,7 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) {
screenshot: <Camera className="size-4" />,
fullPageArchive: <Archive className="size-4" />,
bannerImage: <Image className="size-4" />,
+ video: <Video className="size-4" />,
bookmarkAsset: <Paperclip className="size-4" />,
unknown: <Paperclip className="size-4" />,
};
diff --git a/apps/web/components/dashboard/preview/LinkContentSection.tsx b/apps/web/components/dashboard/preview/LinkContentSection.tsx
index f2069821..bf0d8f90 100644
--- a/apps/web/components/dashboard/preview/LinkContentSection.tsx
+++ b/apps/web/components/dashboard/preview/LinkContentSection.tsx
@@ -60,6 +60,20 @@ function CachedContentSection({ link }: { link: ZBookmarkedLink }) {
return <ScrollArea className="h-full">{content}</ScrollArea>;
}
+function VideoSection({ link }: { link: ZBookmarkedLink }) {
+ return (
+ <div className="relative h-full w-full overflow-hidden">
+ <div className="absolute inset-0 h-full w-full">
+ {/* eslint-disable-next-line jsx-a11y/media-has-caption -- captions not (yet) available */}
+ <video className="m-auto max-h-full max-w-full" controls>
+ <source src={`/api/assets/${link.videoAssetId}`} />
+ Not supported by your browser
+ </video>
+ </div>
+ </div>
+ );
+}
+
export default function LinkContentSection({
bookmark,
}: {
@@ -76,6 +90,8 @@ export default function LinkContentSection({
content = <CachedContentSection link={bookmark.content} />;
} else if (section === "archive") {
content = <FullPageArchiveSection link={bookmark.content} />;
+ } else if (section === "video") {
+ content = <VideoSection link={bookmark.content} />;
} else {
content = <ScreenshotSection link={bookmark.content} />;
}
@@ -101,6 +117,9 @@ export default function LinkContentSection({
>
Archive
</SelectItem>
+ <SelectItem value="video" disabled={!bookmark.content.videoAssetId}>
+ Video
+ </SelectItem>
</SelectGroup>
</SelectContent>
</Select>