This commit is contained in:
1 parent
80fde5a94b
commit
e5e26222ce
2 files changed
+7
-32
No files matched your search
@@ -12,26 +12,17 @@ if (!video) return new Response(null, { status: 404 });
|
|||||||
const metadata = await getVideoMetadata(video);
|
const metadata = await getVideoMetadata(video);
|
||||||
if (!metadata) return new Response(null, { status: 404 });
|
if (!metadata) return new Response(null, { status: 404 });
|
||||||
|
|
||||||
const { title, description, width, height, duration } = metadata;
|
const { title, description } = metadata;
|
||||||
const streamUrl = siteUrl(`video/${encodeURIComponent(video.name)}/stream`);
|
const streamUrl = siteUrl(`video/${encodeURIComponent(video.name)}/stream`);
|
||||||
const thumbnailUrl = siteUrl(`video/${encodeURIComponent(video.name)}/thumbnail.jpg`);
|
const thumbnailUrl = siteUrl(`video/${encodeURIComponent(video.name)}/thumbnail.jpg`);
|
||||||
const thumbnailWidth = 1280;
|
|
||||||
const thumbnailHeight = width && Math.round((height / width) * thumbnailWidth);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<WebsiteLayout title={`${title} - Video`} description={description ?? undefined} openGraph="minimal">
|
<WebsiteLayout title={`${title} - Video`} description={description ?? undefined} openGraph="minimal">
|
||||||
<Fragment slot="head">
|
<Fragment slot="head">
|
||||||
<meta property="og:type" content="video.other" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:image" content={thumbnailUrl} />
|
<meta property="og:image" content={thumbnailUrl} />
|
||||||
<meta property="og:image:type" content="image/jpeg" />
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
<meta property="og:image:width" content={thumbnailWidth.toString()} />
|
<meta property="og:image:alt" content={title} />
|
||||||
<meta property="og:image:height" content={thumbnailHeight.toString()} />
|
|
||||||
<meta property="og:video" content={streamUrl} />
|
|
||||||
{streamUrl.startsWith('https') && <meta property="og:video:secure_url" content={streamUrl} />}
|
|
||||||
<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>
|
</Fragment>
|
||||||
|
|
||||||
<section class="mx-auto flex w-full max-w-6xl flex-col gap-5 px-4 py-8 lg:py-12">
|
<section class="mx-auto flex w-full max-w-6xl flex-col gap-5 px-4 py-8 lg:py-12">
|
||||||
|
|||||||
+4
-20
@@ -35,22 +35,16 @@ export interface VideoThumbnail {
|
|||||||
export interface VideoMetadata {
|
export interface VideoMetadata {
|
||||||
title: string;
|
title: string;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
duration: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FFprobeStream {
|
interface FFprobeStream {
|
||||||
codec_type?: string;
|
codec_type?: string;
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
tags?: Record<string, string>;
|
tags?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FFprobeResult {
|
interface FFprobeResult {
|
||||||
streams?: FFprobeStream[];
|
streams?: FFprobeStream[];
|
||||||
format?: {
|
format?: {
|
||||||
duration?: string;
|
|
||||||
tags?: Record<string, string>;
|
tags?: Record<string, string>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -113,9 +107,8 @@ export async function getVideoMetadata(video: ResolvedVideo): Promise<VideoMetad
|
|||||||
'ffprobe',
|
'ffprobe',
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
[
|
[
|
||||||
'-v',
|
'-v', 'error',
|
||||||
'error', '-show_entries',
|
'-show_entries', 'format_tags=title,description,synopsis:stream=codec_type:stream_tags=title,description,synopsis',
|
||||||
'format=duration:format_tags=title,description,synopsis:stream=codec_type,width,height:stream_tags=title,description,synopsis',
|
|
||||||
'-of', 'json',
|
'-of', 'json',
|
||||||
video.path
|
video.path
|
||||||
],
|
],
|
||||||
@@ -123,21 +116,12 @@ export async function getVideoMetadata(video: ResolvedVideo): Promise<VideoMetad
|
|||||||
);
|
);
|
||||||
|
|
||||||
const parsed = JSON.parse(stdout) as FFprobeResult;
|
const parsed = JSON.parse(stdout) as FFprobeResult;
|
||||||
if (!parsed.format) throw new Error('invalid video stream');
|
|
||||||
|
|
||||||
const videoStream = parsed.streams?.find((stream) => stream.codec_type === 'video');
|
const videoStream = parsed.streams?.find((stream) => stream.codec_type === 'video');
|
||||||
if (!videoStream) throw new Error('no video stream');
|
const tags = { ...videoStream?.tags, ...parsed.format?.tags };
|
||||||
else if (!videoStream.width || !videoStream.height) throw new Error('invalid video stream height');
|
|
||||||
|
|
||||||
const tags = { ...videoStream.tags, ...parsed.format.tags };
|
|
||||||
const duration = Number(parsed.format.duration);
|
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
title: tags['title'] || video.name,
|
title: tags['title'] || video.name,
|
||||||
description: (tags['synopsis'] || tags['longdescription'] || tags['description']) ?? null,
|
description: (tags['synopsis'] || tags['longdescription'] || tags['description']) ?? null
|
||||||
width: videoStream.width,
|
|
||||||
height: videoStream.height,
|
|
||||||
duration: Math.round(duration)
|
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn({ error: e instanceof Error ? e.message : String(e) }, 'error while reading video metadata');
|
logger.warn({ error: e instanceof Error ? e.message : String(e) }, 'error while reading video metadata');
|
||||||
|
|||||||
Reference in new issue
Block a user