add video to opengraph
deploy / build-and-deploy (push) Successful in 42s

This commit is contained in:
bytedream committed 2026-10-31 01:49:53 +01:00
1 parent 73d4f6af42
commit 30c76a10ab
4 files changed
+35 -13

No files matched your search

+5 -3
View File
@@ -26,12 +26,14 @@ const { title, description, keywords, openGraph, viewTransition } = Astro.props;
<title>{title}</title>
{openGraph && (
<>
<meta name="og:title" content={title} />
<meta property="og:url" content={Astro.url.pathname} />
<meta property="og:title" content={title} />
{description && <meta property="og:description" content={description} />}
<meta property="og:url" content={Astro.url.href} />
<meta property="og:type" content="website" />
<meta property="og:image" content={logo512.src} />
<meta property="og:image" content={new URL(logo512.src, Astro.url).href} />
</>
)}
<slot name="head" />
<meta name="description" content={description} />
{keywords && <meta name="keywords" content={keywords.join(', ')} />}
<base href=`${BASE_PATH}/` />
+1
View File
@@ -14,6 +14,7 @@ const { title, description, footer = true } = Astro.props;
---
<BaseLayout title={title} description={description} openGraph={true} viewTransition>
{Astro.slots.has('head') && <slot name="head" slot="head" />}
<main class="min-h-[calc(100vh-3.5rem)] h-full w-full relative overflow-y-hidden">
<slot />
</main>
+15 -4
View File
@@ -11,19 +11,30 @@ if (!video) return new Response(null, { status: 404 });
const metadata = await getVideoMetadata(video);
if (!metadata) return new Response(null, { status: 404 });
const { title, description } = metadata;
const { title, description, width, height, duration } = metadata;
const basePath = import.meta.env.BASE_URL.replace(/\/$/, '');
const streamUrl = `${basePath}/video/${encodeURIComponent(video.name)}/stream`;
const streamPath = `${basePath}/video/${encodeURIComponent(video.name)}/stream`;
const streamUrl = new URL(streamPath, Astro.url);
---
<WebsiteLayout title={`${title} – Video`}>
<WebsiteLayout title={`${title} – Video`} description={description ?? undefined}>
<Fragment slot="head">
<meta property="og:type" content="video" />
<meta property="og:video" content={streamUrl.href} />
{streamUrl.href.startsWith('https://') && <meta property="og:video:secure_url" content={streamUrl.href} />}
<meta property="og:video:type" content={video.contentType} />
{width && <meta property="og:video:width" content={width.toString()} />}
{height && <meta property="og:video:height" content={height.toString()} />}
{duration && <meta property="og:video:duration" content={duration.toString()} />}
</Fragment>
<section class="mx-auto flex w-full max-w-6xl flex-col gap-5 px-4 py-8 lg:py-12">
<div>
<p class="text-sm tracking-widest text-base-content/60 uppercase">Video</p>
<h1 class="mt-1 text-2xl font-semibold break-words lg:text-4xl">{title}</h1>
</div>
<VideoPlayer client:load src={streamUrl} type={video.contentType} title={title} />
<VideoPlayer client:load src={streamPath} type={video.contentType} title={title} />
{description && (
<p class="max-w-3xl text-base leading-relaxed whitespace-pre-line text-base-content/70">{description}</p>
+14 -6
View File
@@ -26,13 +26,15 @@ export interface VideoRange {
export interface VideoMetadata {
title: string;
description: string | null;
width: number | null;
height: number | null;
duration: number | null;
}
interface VideoMetadataCacheEntry {
modifiedAtMs: number;
size: number;
title: string;
description: string | null;
metadata: VideoMetadata;
}
const videoMetadataCache = new Map<string, VideoMetadataCacheEntry>();
@@ -83,22 +85,28 @@ export async function getVideoMetadata(video: ResolvedVideo): Promise<VideoMetad
const cached = videoMetadataCache.get(video.path);
if (cached && cached.modifiedAtMs === modifiedAtMs && cached.size === video.size) {
return cached;
return cached.metadata;
}
let metadata;
let metadata: VideoMetadata;
try {
const parsed = await parseFile(video.path, {
duration: false,
skipCovers: true
});
const videoTrack = parsed.format.trackInfo?.find((track) => track.video);
const duration = parsed.format.containerDuration ?? parsed.format.duration;
metadata = {
title: parsed.common.title?.trim() || video.name,
description:
parsed.common.longDescription?.trim() ||
parsed.common.description?.map((value) => value.trim()).find(Boolean) ||
null
null,
width: videoTrack?.video?.displayWidth ?? videoTrack?.video?.pixelWidth ?? null,
height: videoTrack?.video?.displayHeight ?? videoTrack?.video?.pixelHeight ?? null,
duration: duration ? Math.round(duration) : null
};
} catch {
// couldn't read metadata
@@ -108,7 +116,7 @@ export async function getVideoMetadata(video: ResolvedVideo): Promise<VideoMetad
videoMetadataCache.set(video.path, {
modifiedAtMs,
size: video.size,
...metadata
metadata
});
return metadata;