Shorts: Schattenspiele raus, Lied-Shorts rein (Laternenlied, Serienlied); Musik und Mitlesetext im Hochformat

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lara 2026-11-08 13:02:10 +00:00
parent 8ae3efcd0a
commit 062b819b5c
6 changed files with 59 additions and 12 deletions

View file

@ -18,7 +18,9 @@ import {Banner, BannerRahmen, Profilbild} from './kanal/KanalBilder';
import {VorschauF01, VorschauF09} from './kanal/Vorschaubild'; import {VorschauF01, VorschauF09} from './kanal/Vorschaubild';
import {ShortKompositionen} from './shorts/Short'; import {ShortKompositionen} from './shorts/Short';
import {SHORTS_F01} from './shorts/folge01'; import {SHORTS_F01} from './shorts/folge01';
import {SHORTS_F08} from './shorts/folge08';
import {SZENEN as SZENEN_F01} from './folge01/Folge01'; import {SZENEN as SZENEN_F01} from './folge01/Folge01';
import {SZENEN as SZENEN_F08} from './folge08/Folge08';
import {LIED01_DAUER, Lied01} from './lieder/lied01/Lied01'; import {LIED01_DAUER, Lied01} from './lieder/lied01/Lied01';
import {LIED03_DAUER, Lied03} from './lieder/lied03/Lied03'; import {LIED03_DAUER, Lied03} from './lieder/lied03/Lied03';
@ -42,6 +44,7 @@ export const Root: React.FC = () => (
<FolgenKompositionen folge={F09} /> <FolgenKompositionen folge={F09} />
<LernShorts /> <LernShorts />
<ShortKompositionen szenen={SZENEN_F01} shorts={SHORTS_F01} /> <ShortKompositionen szenen={SZENEN_F01} shorts={SHORTS_F01} />
<ShortKompositionen szenen={SZENEN_F08} shorts={SHORTS_F08} />
<Composition id="Kanal-Profilbild" component={Profilbild} durationInFrames={1} fps={30} width={800} height={800} /> <Composition id="Kanal-Profilbild" component={Profilbild} durationInFrames={1} fps={30} width={800} height={800} />
<Composition id="Vorschau-F01" component={VorschauF01} durationInFrames={1} {...BILD} /> <Composition id="Vorschau-F01" component={VorschauF01} durationInFrames={1} {...BILD} />
<Composition id="Vorschau-F09" component={VorschauF09} durationInFrames={1} {...BILD} /> <Composition id="Vorschau-F09" component={VorschauF09} durationInFrames={1} {...BILD} />

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import {interpolate, useCurrentFrame} from 'remotion'; import {getInputProps, interpolate, useCurrentFrame} from 'remotion';
import {SCHRIFT} from '../baukasten/untertitel'; import {SCHRIFT} from '../baukasten/untertitel';
// Mitlese-Text für die Liedvideos („Pinos Liederbuch“). // Mitlese-Text für die Liedvideos („Pinos Liederbuch“).
@ -21,8 +21,11 @@ export const zeilenStart = (zeile: LiedZeile) => zerlege(zeile)[0].t;
const VOR = 0.35; // Zeile erscheint kurz vor dem ersten Wort const VOR = 0.35; // Zeile erscheint kurz vor dem ersten Wort
const NACH = 1.4; // und bleibt nach dem letzten Wort so lange stehen (höchstens bis zur nächsten Zeile) const NACH = 1.4; // und bleibt nach dem letzten Wort so lange stehen (höchstens bis zur nächsten Zeile)
export const Karaoke: React.FC<{zeilen: LiedZeile[]; fps?: number}> = ({zeilen, fps = 30}) => { // `hoch`: Fassung für Shorts im Hochformat (umbrechend, über den Shorts-Knöpfen). Wird ein Short aus einer
// Folge geschnitten (Eingabe shortKaraoke), blendet sich der Querformat-Text der Szene aus.
export const Karaoke: React.FC<{zeilen: LiedZeile[]; fps?: number; hoch?: boolean}> = ({zeilen, fps = 30, hoch = false}) => {
const f = useCurrentFrame(); const f = useCurrentFrame();
if (!hoch && getInputProps().shortKaraoke === true) return null;
const jetzt = f / fps; const jetzt = f / fps;
const woerter = zeilen.map(zerlege); const woerter = zeilen.map(zerlege);
const i = woerter.findIndex((w, k) => { const i = woerter.findIndex((w, k) => {
@ -40,7 +43,7 @@ export const Karaoke: React.FC<{zeilen: LiedZeile[]; fps?: number}> = ({zeilen,
interpolate(jetzt, [bis - 0.2, bis], [1, 0], {extrapolateLeft: 'clamp'}), interpolate(jetzt, [bis - 0.2, bis], [1, 0], {extrapolateLeft: 'clamp'}),
); );
return ( return (
<div style={{position: 'absolute', left: 0, right: 0, bottom: 56, display: 'flex', justifyContent: 'center', opacity: deck}}> <div style={{position: 'absolute', left: hoch ? 40 : 0, right: hoch ? 40 : 0, bottom: hoch ? 380 : 56, display: 'flex', justifyContent: 'center', opacity: deck}}>
<div <div
style={{ style={{
background: 'rgba(251,244,228,0.88)', background: 'rgba(251,244,228,0.88)',
@ -49,9 +52,10 @@ export const Karaoke: React.FC<{zeilen: LiedZeile[]; fps?: number}> = ({zeilen,
boxShadow: '0 6px 24px rgba(60,40,20,0.18)', boxShadow: '0 6px 24px rgba(60,40,20,0.18)',
fontFamily: SCHRIFT, fontFamily: SCHRIFT,
fontWeight: 600, fontWeight: 600,
fontSize: 60, fontSize: hoch ? 64 : 60,
lineHeight: 1.1, lineHeight: 1.15,
whiteSpace: 'nowrap', whiteSpace: hoch ? 'normal' : 'nowrap',
textAlign: 'center',
}} }}
> >
{w.map((wort, k) => { {w.map((wort, k) => {

View file

@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import {AbsoluteFill, Composition, Easing, Sequence, interpolate, useCurrentFrame} from 'remotion'; import {AbsoluteFill, Composition, Easing, Sequence, interpolate, useCurrentFrame} from 'remotion';
import type {SzenenEintrag} from '../baukasten/folge'; import type {SzenenEintrag} from '../baukasten/folge';
import {Musik} from '../baukasten/musik';
import {Karaoke, type LiedZeile} from '../lieder/karaoke';
// Shorts im Hochformat (1080×1920) direkt aus den Szenen einer Folge. // Shorts im Hochformat (1080×1920) direkt aus den Szenen einer Folge.
// Die Szene läuft unverändert im 1920×1080-Rahmen, eine Kamera schneidet // Die Szene läuft unverändert im 1920×1080-Rahmen, eine Kamera schneidet
@ -27,6 +29,8 @@ export type ShortDef = {
kamera: KameraPunkt[]; kamera: KameraPunkt[];
titel: Record<'de' | 'es' | 'fr', string>; titel: Record<'de' | 'es' | 'fr', string>;
notiz?: string; notiz?: string;
hintergrund?: string; // füllt oben/unten, wenn die Szene das Hochformat nicht ganz füllt (sonst Papier)
lied?: {zeile: string; zeilen: LiedZeile[]}; // Mitlesetext neu fürs Hochformat (Szene ohne ihren Querformat-Text rendern: shortKaraoke)
}; };
const W = 1080; const W = 1080;
@ -69,7 +73,7 @@ const Inhalt: React.FC<{szenen: SzenenEintrag[]; d: ShortDef}> = ({szenen, d}) =
const ein = interpolate(f, [0, 8], [0, 1], {extrapolateRight: 'clamp'}); const ein = interpolate(f, [0, 8], [0, 1], {extrapolateRight: 'clamp'});
const aus = interpolate(f, [dauer - 18, dauer], [1, 0], {extrapolateLeft: 'clamp'}); const aus = interpolate(f, [dauer - 18, dauer], [1, 0], {extrapolateLeft: 'clamp'});
return ( return (
<AbsoluteFill style={{background: '#f4ead8', overflow: 'hidden'}}> <AbsoluteFill style={{background: d.hintergrund ?? '#f4ead8', overflow: 'hidden'}}>
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
@ -86,8 +90,15 @@ const Inhalt: React.FC<{szenen: SzenenEintrag[]; d: ShortDef}> = ({szenen, d}) =
<AbsoluteFill style={{background: '#f4ead8'}}> <AbsoluteFill style={{background: '#f4ead8'}}>
<K /> <K />
</AbsoluteFill> </AbsoluteFill>
{/* Hintergrundmusik bzw. gesungenes Lied der Szene (liegt in der Folge, nicht in K) */}
{sz.musik && <Musik {...sz.musik} dauer={sz.plan.dauer} zeilen={sz.plan.liste} />}
</Sequence> </Sequence>
</div> </div>
{d.lied && (
<Sequence from={bild(sz, {zeile: d.lied.zeile}) - von}>
<Karaoke zeilen={d.lied.zeilen} hoch />
</Sequence>
)}
<AbsoluteFill style={{background: '#f4ead8', opacity: 1 - Math.min(ein, aus)}} /> <AbsoluteFill style={{background: '#f4ead8', opacity: 1 - Math.min(ein, aus)}} />
</AbsoluteFill> </AbsoluteFill>
); );

View file

@ -98,5 +98,6 @@ export const SHORTS_F01: ShortDef[] = [
fr: 'Un petit et un grand 🎵', fr: 'Un petit et un grand 🎵',
}, },
notiz: 'Das Serienlied komplett (Refrain, Strophe, Refrain).', notiz: 'Das Serienlied komplett (Refrain, Strophe, Refrain).',
hintergrund: 'linear-gradient(#e9e3d2 0 50%, #93ad5c 50% 100%)',
}, },
]; ];

View file

@ -0,0 +1,27 @@
import {LATERNENLIED} from '../folge08/laternenlied';
import {VOLL, type ShortDef} from './Short';
// Shorts aus Folge 8 „Noas Laterne“ (Entscheidung 08.11.: neben den Lern-Shorts die Lieder als Shorts).
// Szene 3: Die Laterne geht an („Klick“), Pino staunt, dann das gesungene Laternenlied.
export const SHORTS_F08: ShortDef[] = [
{
id: 'F08-S01-laternenlied',
szene: 3,
von: {zeile: 'klick', plus: -0.6},
bis: {zeile: 'bild1', wo: 'ende'},
kamera: [
{zeile: 'klick', x: 840, y: 560, z: VOLL},
{zeile: 'p1', x: 840, y: 560, z: VOLL},
{zeile: 'lied', plus: 3, x: 820, y: 560, z: VOLL},
{zeile: 'bild1', wo: 'ende', x: 820, y: 560, z: VOLL},
],
titel: {
de: 'Mein kleines Licht – Laternenlied 🏮',
es: 'Mi farolito – canción del farol 🏮',
fr: 'Ma lanterne – chanson de la lanterne 🏮',
},
notiz: 'Laterne geht an, dann das gesungene Laternenlied mit Mitlesetext.',
lied: {zeile: 'lied', zeilen: LATERNENLIED.zeilen},
},
];

View file

@ -8,15 +8,16 @@ Code `remotion/src/folge90/`, Dateien `/shared/youtube/shorts/Lnn-<name>-<sprach
Rhythmus (Entscheidung Nutzerin 08.11.): **jeden Tag ein Short, außer an Tagen mit einer langen Folge** Rhythmus (Entscheidung Nutzerin 08.11.): **jeden Tag ein Short, außer an Tagen mit einer langen Folge**
(11., 15., 27., 29.11.). Uhrzeit wie im Drehplan: DE und FR 12:00, ES 13:30. (11., 15., 27., 29.11.). Uhrzeit wie im Drehplan: DE und FR 12:00, ES 13:30.
Alles muss bis Di 10.11. fertig und eingeplant sein (Urlaub 11.–27.11.). Alles muss bis Di 10.11. fertig und eingeplant sein (Urlaub 11.–27.11.).
L = Lern-Short (eigene Szene), V = Vorlagen-Short (Wer bin ich? / Zähl mit! / Welche Farbe?). L = Lern-Short (eigene Szene), V = Vorlagen-Short (Wer bin ich? / Zähl mit! / Welche Farbe?),
F = Lied-Short aus einer Folge. Die Schattenspiel-Shorts (L03, L04) sind gestrichen (Nutzerin 08.11.).
| Tag | Short | Frage | Kernaussagen (geprüft) | | Tag | Short | Frage | Kernaussagen (geprüft) |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Mo 09.11. | L01-igel | Was frisst ein Igel? | Käfer, Würmer, Schnecken; Milch und Obst sind nichts für Igel. | | Mo 09.11. | L01-igel | Was frisst ein Igel? | Käfer, Würmer, Schnecken; Milch und Obst sind nichts für Igel. |
| Di 10.11. | L02-giraffe | Wer ist am größten? | klein – mittel – groß; kein Tier an Land ist so hoch wie die Giraffe. | | Di 10.11. | L02-giraffe | Wer ist am größten? | klein – mittel – groß; kein Tier an Land ist so hoch wie die Giraffe. |
| Mi 11.11. | – | (Folge 8 „Noas Laterne“) | | | Mi 11.11. | – | (Folge 8 „Noas Laterne“) | |
| Do 12.11. | L03-schatten | Warum wird der Schatten groß? | Nah am Licht groß, weit weg klein. | | Do 12.11. | F08-S01-laternenlied | Laternenlied „Mein kleines Licht“ | Laterne geht an, das Lied aus Folge 8 mit Mitlesetext. |
| Fr 13.11. | L04-hase | Kannst du einen Hasen machen? | Hand-Schatten Hase und Igel, mitmachen. | | Fr 13.11. | F01-S04-lied | Serienlied „Einer klein und einer groß“ | Das Lied aus Folge 1 komplett, mit Mitlesetext. |
| Sa 14.11. | V01-wer-pino | Wer bin ich? | klein, Stacheln, blaue Mütze → Pino. | | Sa 14.11. | V01-wer-pino | Wer bin ich? | klein, Stacheln, blaue Mütze → Pino. |
| So 15.11. | – | (Folge 9 „Lulu und der Drachen“) | | | So 15.11. | – | (Folge 9 „Lulu und der Drachen“) | |
| Mo 16.11. | L05-wind | Woran sieht man den Wind? | Blätter, Schal, Drachen; Pusten. | | Mo 16.11. | L05-wind | Woran sieht man den Wind? | Blätter, Schal, Drachen; Pusten. |
@ -45,10 +46,10 @@ Zielgruppe „Ja, speziell für Kinder“. Datei: `/shared/youtube/shorts/<Short
| Short | DE | ES | FR | | Short | DE | ES | FR |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| F08-S01-laternenlied | Mein kleines Licht – Laternenlied 🏮 \| Pinos kleine Welt #shorts | Mi farolito – canción del farol 🏮 \| El pequeño mundo de Pino #shorts | Ma lanterne – chanson de la lanterne 🏮 \| Le petit monde de Pino #shorts |
| F01-S04-lied | Einer klein und einer groß 🎵 \| Pinos kleine Welt #shorts | Uno pequeño y uno grande 🎵 \| El pequeño mundo de Pino #shorts | Un petit et un grand 🎵 \| Le petit monde de Pino #shorts |
| L01-igel | Was frisst ein Igel? \| Pinos kleine Welt #shorts | ¿Qué come un erizo? \| El pequeño mundo de Pino #shorts | Que mange un hérisson ? \| Le petit monde de Pino #shorts | | L01-igel | Was frisst ein Igel? \| Pinos kleine Welt #shorts | ¿Qué come un erizo? \| El pequeño mundo de Pino #shorts | Que mange un hérisson ? \| Le petit monde de Pino #shorts |
| L02-giraffe | Wer ist am größten? \| Pinos kleine Welt #shorts | ¿Quién es el más alto? \| El pequeño mundo de Pino #shorts | Qui est le plus grand ? \| Le petit monde de Pino #shorts | | L02-giraffe | Wer ist am größten? \| Pinos kleine Welt #shorts | ¿Quién es el más alto? \| El pequeño mundo de Pino #shorts | Qui est le plus grand ? \| Le petit monde de Pino #shorts |
| L03-schatten | Warum wird der Schatten groß? \| Pinos kleine Welt #shorts | ¿Por qué crece la sombra? \| El pequeño mundo de Pino #shorts | Pourquoi l'ombre grandit ? \| Le petit monde de Pino #shorts |
| L04-hase | Kannst du einen Hasen machen? \| Pinos kleine Welt #shorts | ¿Sabes hacer un conejo? \| El pequeño mundo de Pino #shorts | Tu sais faire un lapin ? \| Le petit monde de Pino #shorts |
| V01-wer-pino | Wer bin ich? \| Pinos kleine Welt #shorts | ¿Quién soy? \| El pequeño mundo de Pino #shorts | Qui suis-je ? \| Le petit monde de Pino #shorts | | V01-wer-pino | Wer bin ich? \| Pinos kleine Welt #shorts | ¿Quién soy? \| El pequeño mundo de Pino #shorts | Qui suis-je ? \| Le petit monde de Pino #shorts |
| L05-wind | Woran sieht man den Wind? \| Pinos kleine Welt #shorts | ¿Cómo se ve el viento? \| El pequeño mundo de Pino #shorts | Comment voit-on le vent ? \| Le petit monde de Pino #shorts | | L05-wind | Woran sieht man den Wind? \| Pinos kleine Welt #shorts | ¿Cómo se ve el viento? \| El pequeño mundo de Pino #shorts | Comment voit-on le vent ? \| Le petit monde de Pino #shorts |
| L06-schleifen | Welche Farben siehst du? \| Pinos kleine Welt #shorts | ¿Qué colores ves? \| El pequeño mundo de Pino #shorts | Quelles couleurs vois-tu ? \| Le petit monde de Pino #shorts | | L06-schleifen | Welche Farben siehst du? \| Pinos kleine Welt #shorts | ¿Qué colores ves? \| El pequeño mundo de Pino #shorts | Quelles couleurs vois-tu ? \| Le petit monde de Pino #shorts |