import React from 'react'; import {AbsoluteFill, Composition, Series, getInputProps, useCurrentFrame} from 'remotion'; import {Musik, type MusikEintrag} from './musik'; import {SCHRIFT} from './untertitel'; import type {Geplant} from './zeit'; // Eine Folge = Liste von Szenen. Daraus entstehen die Komposition der ganzen // Folge und je Szene eine eigene Komposition (zum einzelnen Rendern): // F02 ganze Folge // F02-03-... nur Szene 3 // Mit --props='{"entwurf": true}' blendet jede Szene oben links Szene, Zeit // und Zeilen-ID ein, damit Rückmeldungen genau sagen können, wo etwas ist. export type SzenenEintrag = { name: string; datei: string; // Quelldatei der Szene (für werkzeuge/render.mjs) plan: {dauer: number; liste: Geplant[]}; K: React.FC; musik?: MusikEintrag; // Hintergrundmusik der Szene (siehe baukasten/musik.tsx) }; const BILD = {fps: 30, width: 1920, height: 1080} as const; const zeit = (f: number) => { const s = Math.floor(f / 30); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`; }; export const kennung = (kuerzel: string, i: number, name: string) => `${kuerzel}-${String(i + 1).padStart(2, '0')}-${name .replace(/ä/g, 'ae') .replace(/ö/g, 'oe') .replace(/ü/g, 'ue') .replace(/ß/g, 'ss') .replace(/[^A-Za-z0-9]+/g, '-') .replace(/^-|-$/g, '')}`; const Entwurf: React.FC<{szenen: SzenenEintrag[]; index: number; versatz: number}> = ({szenen, index, versatz}) => { const f = useCurrentFrame(); const sz = szenen[index]; const zeile = sz.plan.liste.find((l) => f >= l.start && f < l.frei + 10); return (
{zeit(versatz + f)} · Szene {index + 1} „{sz.name}“ · {zeit(f)} {zeile ? ` · ${zeile.id}` : ''}
); }; const istEntwurf = () => getInputProps().entwurf === true; const versatzVon = (szenen: SzenenEintrag[], index: number) => szenen.slice(0, index).reduce((s, x) => s + x.plan.dauer, 0); export const folgenDauer = (szenen: SzenenEintrag[]) => versatzVon(szenen, szenen.length); const EinzelSzene: React.FC<{szenen: SzenenEintrag[]; index: number}> = ({szenen, index}) => { const {K, musik, plan} = szenen[index]; return ( {musik && } {istEntwurf() && } ); }; // Komponenten einmal je Folge bauen (Kompositions-Props müssen ohne // Funktionen auskommen, darum stecken die Szenen in den Komponenten selbst). export const baueFolge = (kuerzel: string, szenen: SzenenEintrag[]) => { const Ganz: React.FC = () => ( {szenen.map((s, i) => ( ))} ); const einzeln = szenen.map((s, i) => { const E: React.FC = () => ; return {id: kennung(kuerzel, i, s.name), K: E, dauer: s.plan.dauer, datei: s.datei, name: s.name}; }); return {kuerzel, dauer: folgenDauer(szenen), Ganz, einzeln}; }; export type Folge = ReturnType; // Alle Kompositionen einer Folge (für Root.tsx) export const FolgenKompositionen: React.FC<{folge: Folge}> = ({folge}) => ( <> {folge.einzeln.map((e) => ( ))} );