feat: Video unter dem Headerbild aus dem Medienverzeichnis (intro.mp4)
Startet nur nach Klick; mit Vorschaubild (intro.jpg) lädt vorab nichts vom Video. Vorschaubilder von Intro und Reels laufen über die Bildoptimierung statt im Original, Videos bevorzugen eine Web-Fassung <name>.web.mp4. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
51b6d79bbf
commit
33f5096bf1
6 files changed
+99
-10
No files matched your search
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* URL eines Bildes ueber die Next.js-Bildoptimierung (AVIF/WebP, passende
|
||||
* Breite) fuer Stellen, an denen kein <Image> moeglich ist, etwa das
|
||||
* poster-Attribut von <video>. Ein 15-MB-Foto aus dem Medienverzeichnis wird
|
||||
* so als wenige Dutzend Kilobyte ausgeliefert statt im Original.
|
||||
*/
|
||||
import { getImageProps } from "next/image";
|
||||
|
||||
/** Liefert eine Variante mit mindestens `width` Pixeln Breite. */
|
||||
export function optimizedImageUrl(src: string, width: number): string {
|
||||
// getImageProps gibt als src die 2x-Variante der angegebenen Breite
|
||||
// zurueck; mit halber Breite entspricht sie der gewuenschten.
|
||||
const half = Math.ceil(width / 2);
|
||||
return getImageProps({ src, alt: "", width: half, height: half, quality: 75 }).props.src;
|
||||
}
|
||||
+18
-4
@@ -52,12 +52,21 @@ async function find(base: string, exts: readonly string[]): Promise<Found | null
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Video mit Vorrang fuer die Web-Fassung "<name>.web.mp4" (1080p,
|
||||
* faststart, siehe scripts/optimize-media.ts). Fehlt sie, das Original.
|
||||
*/
|
||||
async function video(base: string): Promise<Found | null> {
|
||||
return (await find(`${base}.web`, VIDEO_EXTS)) ?? find(base, VIDEO_EXTS);
|
||||
}
|
||||
|
||||
async function image(base: string, alt: string): Promise<Media | null> {
|
||||
const found = await find(base, IMAGE_EXTS);
|
||||
return found ? { kind: "image", src: found.src, alt, width: 1200, height: 1500 } : null;
|
||||
}
|
||||
|
||||
export type Reel = { src: string | null; poster: Media | null };
|
||||
export type IntroVideo = { src: string | null; poster: Media | null };
|
||||
|
||||
export type SiteMedia = {
|
||||
/** Logo fuer helle Flaechen (dunkles Logo). */
|
||||
@@ -66,22 +75,26 @@ export type SiteMedia = {
|
||||
logoLight: string | null;
|
||||
header: Media;
|
||||
headerVideo: string | null;
|
||||
/** Video direkt unter dem Headerbild (intro.mp4, Vorschaubild intro.jpg). */
|
||||
intro: IntroVideo;
|
||||
portraits: Record<string, Media>;
|
||||
reels: Reel[];
|
||||
};
|
||||
|
||||
export async function loadSiteMedia(): Promise<SiteMedia> {
|
||||
const people = [site.team.head, ...site.team.people];
|
||||
const [logoDark, logoLight, header, headerVideo, portraitList, reels] = await Promise.all([
|
||||
const [logoDark, logoLight, header, headerVideo, introVideo, introPoster, portraitList, reels] = await Promise.all([
|
||||
find("logo", LOGO_EXTS),
|
||||
find("logo-hell", LOGO_EXTS),
|
||||
image("header", "ARTISTA Modelcamp in der Toskana"),
|
||||
find("header", VIDEO_EXTS),
|
||||
video("header"),
|
||||
video("intro"),
|
||||
image("intro", "Vorschaubild Video ARTISTA Modelcamp"),
|
||||
Promise.all(people.map((p) => image(`team/${p.slug}`, `Porträt ${p.name}`))),
|
||||
Promise.all(
|
||||
[1, 2, 3].map(async (n): Promise<Reel> => {
|
||||
const [video, poster] = await Promise.all([find(`reels/reel-${n}`, VIDEO_EXTS), image(`reels/reel-${n}`, `Vorschaubild Reel ${n}`)]);
|
||||
return { src: video?.src ?? null, poster };
|
||||
const [clip, poster] = await Promise.all([video(`reels/reel-${n}`), image(`reels/reel-${n}`, `Vorschaubild Reel ${n}`)]);
|
||||
return { src: clip?.src ?? null, poster };
|
||||
}),
|
||||
),
|
||||
]);
|
||||
@@ -96,6 +109,7 @@ export async function loadSiteMedia(): Promise<SiteMedia> {
|
||||
logoLight: logoLight?.src ?? logoDark?.src ?? null,
|
||||
header: header ?? site.hero.media,
|
||||
headerVideo: headerVideo?.src ?? null,
|
||||
intro: { src: introVideo?.src ?? null, poster: introPoster },
|
||||
portraits,
|
||||
reels,
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user