Kaltlese prueft jetzt auch Grammatik und Faktenspringen
Merts Auftrag am 2026-08-19, nachdem er im fertigen Reel ueber "Von da an fahren Menschen. Oben wird teuer." gestolpert ist- "pruefe in zukunft bei der kaltlese noch ob alles gramatikalisch sinn macht und nicht von fakt zu fakt gesprungen wird". WARUM DAS DURCHGERUTSCHT IST- der Satz entstand beim KUERZEN. Das Skript war zu lang, die Zeile wurde zusammengestrichen, und dabei fiel das "es" heraus. Alle Pruefungen blieben gruen- skript.py zaehlt Zeichen und Zeilen, sprechtext.py sucht Meta-Saetze, die Kaltlese fragte nach Verstaendnis. Keines davon liest Grammatik. Und der Leser VERSTEHT so einen Satz trotzdem, meldet ihn also nicht von allein. NEU sind zwei Pflichtfragen im Auftrag (kaltlese.FRAGEN und FRAGEN_LANG)- (4) Ist jeder Satz richtiges Deutsch, mit der richtigen Fassung daneben. (5) Wird von Fakt zu Fakt gesprungen, also stehen zwei Zeilen nebeneinander, ohne dass die zweite aus der ersten folgt. Frage 5 misst dasselbe wie der rote Faden in SKRIPT.md, aber am fertigen Text und aus dem Ohr eines Fremden statt aus der Absicht des Autors. Bewaehrt hat sich das Muster schon bei der Langform- dort ist "wo hast du den Faden verloren" die schaerfste der vier Fragen.
Sprechtempo wird geregelt, die Betonung bleibt Gluecksache
Merts Vergleich der beiden Fassungen von AKTE 09 (2026-08-19)- "ich finde die geschwindigkeit bei version 2 besser aber betonung und sprechweise von version 1 besser". BEIDE LIEFEN MIT DENSELBEN EINSTELLUNGEN- derselbe Text, dieselbe Stimme, Stabilitaet Ausdruck, Faktor 1,06. Der Unterschied kommt allein aus der Erzeugung- Fassung 1 sprach 654 Zeichen in 67,8s (9,6 Zeichen/s), Fassung 2 668 Zeichen in 64,7s (10,3). ElevenLabs wuerfelt Tempo UND Betonung bei jedem Lauf neu; das steht als Rauschen von rund fuenf Sekunden schon in skript.PAUSE_SEKUNDEN. WAS SICH LOESEN LAESST- das Tempo. Ein fester Prozentsatz verschiebt beide Laeufe um denselben Betrag und laesst den Abstand stehen. Deshalb regelt bau.tempo_fuer() jetzt auf eine ZIELRATE (10,3 Zeichen je Sekunde der rohen Aufnahme, genau Merts Tempo), begrenzt auf 0,95 bis 1,25. Fassung 1 haette damit 1,068 bekommen, Fassung 2 bleibt bei 0,998. WAS SICH NICHT LOESEN LAESST- die Betonung. Dagegen hilft nur mehrmals erzeugen und aussuchen. Kostet je Lauf den halben Text vom Kontingent.
Lebendiger sprechen- Ausdruck, Betonung aus dem Sternchen, 6 % straffer
Merts Wahl nach fuenf Hoerproben- "alles zusammen ist am besten aber minimal zu langsam gesprochen". Alle drei Hebel sind eingebaut. (1) STABILITAET auf STABIL_AUSDRUCK (0.0) statt neutral. Der Preis ist bekannt- die Stufe buext gelegentlich aus (falsche Betonung, selten ein verschlucktes Wort). Dagegen hilft Abhoeren, nicht ein Rueckschritt auf neutral — genau der war fuenf Tage lang die stille Antwort auf dieselbe Beschwerde. (2) ``*wort*`` WIRD ZUR BETONUNG. Bis dahin wurde der Marker fuer die Stimme schlicht geloescht- das Skript sagte also, welches Wort das wichtigste im Satz ist, und ausgerechnet der Sprecher erfuhr es nie. Jetzt schreibt stimme.mit_betonung() es fuer eleven_v3 gross (ab drei Buchstaben, sonst liest das Modell Buchstabenfolgen). Ein Marker, zwei Wirkungen- rot im Bild, betont im Ohr. Die Wortanzahl aendert sich nicht, Wortzuordnung und Szenenzeilen bleiben parallel. (3) TEMPO 1,06 ueber atempo, tonhoehenerhaltend. WICHTIG- angewendet ganz am Anfang von render(), BEVOR die Wortzeiten bestimmt werden. Wer die Stimme erst beim Abmischen strafft, bekommt ein Reel, in dem jedes Wort frueher faellt als sein Untertitel, und das sieht man erst im fertigen Video. WIEDER EIN STILLER REGLER- ElevenLabs nimmt "speed" in den Stimmeinstellungen an, und eleven_v3 ignoriert ihn. Gemessen mit festem seed- speed 0,7 und speed 1,2 liefern beide exakt 7,63 s. Zweiter Fall derselben Sorte an einem Tag (siehe Stabilitaet). MERKSATZ- bei diesem Anbieter gilt ein Regler erst als wirksam, wenn eine Messung ihn bestaetigt. DER TEXT IST DER STAERKSTE HEBEL und liegt beim Autor- kurze Saetze, Auslassungspunkte, eine Frage mittendrin, ``*wort*`` gezielt, Regie mit Haltung ([curious] am Haken). Steht als eigener Abschnitt in SKRIPT.md.
Der Ausdrucks-Regler hatte seit dem 14.08. keine Wirkung — v3 kennt drei Stufen
Merts Befund am 2026-08-19- "die stimme spricht mir zu monoton, zu wenig gefuehle, nicht sympathisch genug". Derselbe Satz wie am 14.08., und dazwischen war der Regler eigens dafuer verstellt worden. NACHGEMESSEN mit festem seed- eleven_v3 nimmt nur 0.0 (Ausdruck), 0.5 (neutral) und 1.0 (streng). Zwischenwerte werden STILL auf die naechste Stufe gerundet. 0.30 und 0.50 liefern Aufnahmen exakt gleicher Laenge (149.673 Bytes), 0.0 und 1.0 jeweils andere. Die Senkung von 0.45 auf 0.30 hat also nichts getan — beide runden auf 0.5, und der Kommentar daneben erklaerte ausfuehrlich eine Wirkung, die es nie gab. Genau die Fehlerklasse, vor der die Projektanweisung warnt- kein Fehler, kein rotes Feld, nur ein Regler, der ins Leere greift. EINGEBAUT- stimme.STABIL_AUSDRUCK / STABIL_NEUTRAL / STABIL_STRENG stehen jetzt als Namen im Code, die Vorgabe ist der ehrliche Wert 0.5. Wer einen Zwischenwert hinschreibt, sieht sofort, dass es ihn nicht gibt. ZUR AUSWAHL LIEGEN drei Hebel als Hoerprobe bereit (uploads/_stimmproben/gefuehl/)- der Regler auf Ausdruck, zusaetzliche Regie-Marken, und ein anders GESCHRIEBENER Text (kurze Saetze, Auslassungspunkte, Grossschreibung als Betonung). Gewinnt der Satzbau, gehoert die Regel in skript.py und wirkt in jedem kuenftigen Reel.
Zischlaute- eine breite Senke bei 8,5 kHz statt zweier schmaler
Runde 2 nach Merts "das einzige was noch stoert sind die fs". Ein S wiegt zwischen 5,5 und 8,5 kHz, ein F hoeher und breiter (9-14 kHz) — der EQ aus Runde 1 nahm davon gemessen 2 %. Sechs Aufbauten, gemessen als Minderung der S- und F-Spitzen gegen die rohe Aufnahme- Runde-1-EQ S-21/F-2 · zweite Senke bei 11 kHz S-32/F-27 · Hoehen-Dach ab 9 kHz S-19/F-34 · EQ+Regler ueber 8,5 kHz S-6/F-70 · breiter gezogen S-16/F-18 · EQ+De-Esser S-35/F-18. MERTS WAHL WIEDER NICHT DER BESTE MESSWERT ("breiter gezogen ist am besten")- drei Oktaven um 8,5 kHz decken S und F mit EINER Bewegung ab. Zwei getrennte Senken nehmen mehr weg, hinterlassen aber eine Delle dazwischen; der Stimme fehlt dann etwas, ohne dass man sagen kann was. EINGEBAUT als bau.STIMME_KLANG = equalizer=f=8500:width_type=o:width=3:g=-7, ganz vorne in der Sprecherkette. Gilt ab sofort fuer jedes Reel.
Zischlaute- Merts fester EQ gewinnt gegen jeden gemessenen De-Esser
Merts Beobachtung am fertigen Reel (2026-08-19)- "s und t werden stark gehoert, so ein stoerendes Geraeusch". Sieben Fassungen desselben Satzes wurden in der Cloud gerechnet und gemessen (Anteil im Zischband an der Gesamtenergie, 99. Perzentil)- unbehandelt 56,1 % · De-Esser leicht/mittel/ stark 50,5/41,3/37,2 % · Mehrband sanft 23,1 % · Mehrband stark 32,2 % · fester EQ 46,7 %. MERT WAEHLT DEN SCHLECHTESTEN MESSWERT, und zwar bewusst- "tatsaechlich ist der feste eq am besten". Die dynamischen Fassungen greifen in die Huellkurve ein und nehmen der Stimme mit der Zischspitze auch ein Stueck Deutlichkeit. Der EQ nimmt weniger weg, aber immer gleich viel — und das hoert man nicht als Eingriff. MERKSATZ FUER ALLES WEITERE- die Messung sagt, WIE VIEL weg ist, nicht ob es besser klingt. Wer hier nachdreht, laesst Mert hoeren statt auf die Prozente zu schauen. EINGEBAUT als bau.STIMME_KLANG (equalizer=f=7000:width_type=o:width=2:g=-6), ganz vorne in der Sprecherkette — vor apad und vor dem asplit, damit auch die Steuerspur fuers Ducking dieselbe Stimme hoert. Abschaltbar ueber _tonspur(entzischen=False). probe_ton.py bestaetigt alle Graphen. OFFEN- das F stoert weiter ("das einzige was noch stoert sind die fs"). Es liegt hoeher als das S, rund 9-14 kHz, und der EQ bei 7 kHz nimmt davon gemessen nur 2 %. Runde 2 mit sechs Aufbauten liegt zum Anhoeren bereit (uploads/_stimmproben/, Dateien 0_ bis 5_). NEBENBEI- der ffmpeg-Hook blockt auch "ffmpeg -h filter=…", also den reinen Hilfetext, der nichts rechnet. Kein Umgehen, nur gemeldet.
Stimmwerkstatt- Bibliothek zuerst, Entwurf zweitens, Merts Stimme moeglich
Merts Auftrag am 2026-08-19- "schau ob wir ueber ElevenLabs eine natuerlichere Stimme finden bzw. erzeugen koennen ... wenn das auch nicht klappt, schau ob es moeglich ist meine Stimme zu nutzen". WAS DER ZUGANG HERGIBT (nachgesehen, nicht geraten)- Stufe Starter, aktiv. Instant Voice Cloning ist FREIGESCHALTET, professionelles Klonen nicht (das braucht Creator). Stimmentwurf per Beschreibung geht ebenfalls. Bestes Modell fuer Deutsch bleibt eleven_v3 — es gibt nichts Neueres. NEUES WERKZEUG formate/vox/stimmwerkstatt.py mit fuenf Befehlen- bibliothek (suchen, gratis), holen (ins Konto legen), entwerfen (Stimme aus einer Beschreibung), behalten (Entwurf sichern), klonen (eigene Aufnahme), probe (mehrere Stimmen, gleicher Text, gleiche Einstellungen). BEWUSST GETRENNT von stimme.py- das ist der heisse Pfad, den mach.py bei jedem Reel aufruft. Herumprobieren an der Werkstatt soll den Produktionsweg nicht anfassen. REIHENFOLGE DER WEGE, nach Naturtreue- (1) Bibliothek, das sind Klone echter Sprecher, (2) eigene Aufnahme von Mert, (3) Entwurf per Beschreibung, der klingt am ehesten synthetisch, weil kein Mensch dahintersteht. GEMESSENE KOSTEN- ein Entwurf zieht die Zeichen des Probetextes EINMAL ab, obwohl drei Fassungen zurueckkommen (305 Zeichen fuer drei Stimmen). Eine Probe kostet wie normales Sprechen, bei eleven_v3 also halber Text. Klonen kostet nichts vom Kontingent, es belegt einen Stimmplatz (1 von 10 belegt). Der ganze Versuch hat 1.065 der 38.758 Monatszeichen gekostet. FALLE BEIM HINZUFUEGEN- eine Bibliotheksstimme braucht die Kennung des BESITZERS mit, und die laesst sich aus der Stimmkennung allein nicht nachschlagen. Deshalb gibt "bibliothek" gleich "besitzer/kennung" aus und "holen" nimmt genau das entgegen. LIEGT ZUM ANHOEREN- uploads/_stimmproben/ (ueber Syncthing auch auf dem PC), Erklaerung in 00_ANHOEREN.md.
Humor darf jetzt in den Sprechertext — nachgemessen an Simplicissimus2026-08-19
Merts Auftrag am 2026-08-19- "schau dir mal die Transkripte von Simplicissimus-Videos an und schau ob wir anhand deren Skript bei uns was anpassen können ... ich sage sogar wir haben etwas zu wenig Humor, vor allem leichter schwarzer Humor bzw. Sarkasmus wäre gut." VERMESSEN- drei Videos, 9,9 / 3,5 / 3,3 Mio Aufrufe, alle 6-10 min, also die kurzen und erfolgreichsten des Kanals (Transkripte über den youtube-transcript- Skill geholt). WAS UNSERE REGEL BLOCKIERT HAT- in SKRIPT.md stand seit dem 2026-08-11 "Humor- trocken, im BILD, und nie im Sprechertext". Abgeleitet aus EINER Vorlage, dem Cardboard-Cinema-Biberreel. Genau das, was Mert jetzt mehr will, war damit verboten. DER BEFUND- Simplicissimus erzählt auch keine Witze, der Sprecher bleibt Reporter. Der Humor sitzt trotzdem im gesprochenen Satz, aber nicht als Gag, sondern in WELCHE Tatsache gewählt und WOHIN sie gestellt wird. Die Grundregel ist also nicht widerlegt, sie war nur zu weit gefasst. SECHS BAUARTEN, alle jetzt dokumentiert- die nachgeschobene Tatsache ("er entscheidet sich für Craig David, von dem er noch nie was gehört hat"), das wertende Verb ("verballert"), das absurde letzte Listenglied ("Mafia-Mitglieder, Stripclubbesitzer, Drogendealer und eine Mormonenfamilie"), die zu genaue Zahl ("knapp 192 Mal gehustet"), das Amtswort für das Lächerliche ("Husteranalysen") und die entwertende Folge ("wurden zu britischen B-Promis"). WIR HATTEN ES SCHON, OHNE ES ZU BENENNEN- die stärkste Zeile des Rust-Reels ist eine davon- "Doch oben ist Feiertag, die Büros sind leer." BUDGET- zwei bis drei Stellen je Reel, die Pointe ist eine davon. Zusammen mit den Bilddetails höchstens rund fünf trockene Momente auf 45 Sekunden. ZWEITER FUND, HAKEN- Simplicissimus verrät im besten Video den AUSGANG ("er ist ein Betrüger") und öffnet dafür eine neue Frage ("aber wie?"). Das widerspricht unserer Regel "der Haken darf den Einsatz nennen, niemals den Ausgang" nur an der Oberfläche- beide sagen, dass nach dem Haken genau EINE Frage offen sein muss. Tauschen geht nur, wenn die Methode die eigentliche Geschichte ist (Betrug, Trick, Einbruch) - bei einer Entscheidung wie Petrow wäre es tödlich, seine Methode war nichts zu tun. DAS BMI-REEL IST DAS ERSTE SKRIPT GANZ OHNE SOLCHE STELLE- 18 Zeilen, alle ernst, dazu sechs Regieanweisungen wie [serious] und [whispers]. Die Länge war die gemessene Ursache für den Ausfall; die fehlende Trockenheit erklärt, warum es sich beim Hören zäh anfühlt. NICHT GEAENDERT- die Sperre bleibt, wo Menschen leiden (Radium, Petrow). Über Institutionen darf gespottet werden, über Opfer nicht.
Papier-3D bleibt, aber die Bewegung wird minimal — Aufbau ist aus2026-08-17
Merts Entscheidung am 2026-08-17, nachdem die Längensperre stand- "ich würde sagen 3d papierstil aber bewegung so minimal wie nur möglich, keine aufbau der szene oder ähnliches. Nur eine dezente bewegung sodass das bild zum leben erweckt. Weil die video clips waren offensichtlich ki generiert." WAS SICH GEÄNDERT HAT- aufbau_standard bei papier3d steht auf False (war True), und beide Drift-Vorlagen (BEWEGUNG und BEWEGUNG_3D in prompts.py) verlangen jetzt ausdrücklich die kleinste Bewegung, die noch lebendig wirkt. WARUM DIE ANSAGE PRÜFBAR FORMULIERT IST- im Auftrag steht "a still frame from the middle of the clip is nearly indistinguishable from the first frame". Ein Adjektiv wie "subtle" stand schon am 2026-08-02 drin und hat nicht gereicht; das Modell legt "small" gegen die Bildbreite aus. Eine Probe, die man am fertigen Clip nachstellen kann, hält besser als ein Wunsch. DRITTE STELLUNG DIESES SCHALTERS, erste aus diesem Grund. 2026-08-12 an, 2026-08-14 nur über 5 s — da ging es darum, ob der Aufbau in kurze Szenen PASST. Jetzt geht es darum, was man dem Clip ANSIEHT- ein Bild, das sich vor den Augen zusammensetzt, könnte keine Kamera aufnehmen. MITGEZOGEN- die zwei offenen Drehbücher mit "aufbau: true" (vox_kinotuer, vox_rust_papier3d). Bei beiden ist der betroffene Clip noch nicht erzeugt, es geht also nichts verloren. vox_bmi bleibt unangetastet (fertiges Video). NEBENWIRKUNG, angenehm- ohne Aufbau bestellt der Flow-Bot 4-Sekunden-Clips statt 10, also 7 statt 15 Tokens je bewegter Szene. NACHGEZOGEN AM SELBEN TAG, auf Merts "mach die voreinstellung auf papier3d und akte 03 umstellen"- STIL_STANDARD steht jetzt auf "papier3d". Vorher blieb die Voreinstellung auf Collage, obwohl die Entscheidung für den Papier-Stil schon am 2026-08-12 gefallen war; prompt kam das nächste Reel im alten Stil heraus ("außerdem wurde noch der alte stil von bild erstellt"). Eine Entscheidung, die nur in EINER Datei steht, ist keine Entscheidung, sondern eine Bitte. ZWEI SPERREN MUSSTEN DAFÜR VORHER WEG- (1) hintergrund und palette sind bei papier3d jetzt PFLICHT und werden in prompts.pruefe laut eingefordert (mach.py reicht den Stil dafür durch). Vorher fielen sie still auf den Stil-Standard zurück, und weil der für ALLE Szenen derselbe ist, hätte ein Drehbuch ohne die Angaben das ganze Reel vor dieselbe Wand gestellt — nicht kaputt, nur langweilig, also unsichtbar. Ausgenommen sind Beleg-Szenen und das Schlussbild, das die Felder gar nicht hat. (2) Die zehn Collage-Drehbücher in uploads/ haben jetzt ausdrücklich "stil: collage" im Kopf, sonst hätten sie sich beim Umlegen lautlos umgefärbt. AKTE 03 (uploads/vox_kinotuer) ist auf papier3d umgestellt- 13 Szenen mit Hintergrund, Zusatz und durchgehender Palette (Russ und Glut). Gegengeprüft am gebauten Auftrag- 14 von 14 Bildaufträgen tragen die Papier-Merkmale, 6 bewegte Szenen, kein einziger Aufbau, Cliplängen 3,2 bis 5,5 s.
Reel-Länge ist eine SPERRE bei 60 s — und der Titel muss ein Loch lassen2026-08-17
Anlass war Merts Verdacht am 2026-08-15- das neue BMI-Reel macht kaum Aufrufe, also sei es "shadowbanned, weil es als KI-Inhalt gekennzeichnet wurde", und der neue 3D-Papierstil sehe mehr nach KI aus als die flache Collage. WIDERLEGT, mit Zahlen. Das Video ist öffentlich, fertig verarbeitet, ohne Ablehnung und ohne Ländersperre; /shorts/<id> läuft ohne Umleitung, YouTube führt es also als Short. Die KI-Kennzeichnung setzen wir gar nicht — youtube_hochladen.py kennt das Feld containsSyntheticMedia nicht. AM 2026-08-18 von Mert im Studio nachgesehen und mit Bildschirmfotos belegt- unter "AI use" ist WEDER Ja NOCH Nein angekreuzt, die Frage ist unbeantwortet. Es gibt also keinen KI-Hinweis am Video, weder von uns noch von Hand. Die Shadowban-These ist damit endgueltig vom Tisch. Und der 36-Minuten-Midgley (34 Aufrufe, 6,7 % gesehen), den Mert mitgezählt hatte, ist kein Short- Langform im Feed gibt es nicht. DIE ECHTE URSACHE IST DIE LÄNGE. Aufrufe am Upload-Tag- Petrow 37,8 s → 704 · Radium 49,6 s → 745 · Rust 63,5 s → 1.484 · BMI 91,8 s → 77 nach zehn Stunden. Faktor 20. Der Algorithmus misst den ANTEIL, den die Leute sehen; Rust kam auf 132 % (mehrfach angesehen), auf 96 Sekunden ist das unmöglich. WARUM ES PASSIEREN KONNTE- die 35-55-s-Regel stand seit dem 2026-08-10 im Code, aber nur als WARNUNG unter fünfzehn Warnungen. Das BMI-Skript hatte 1.371 Zeichen bei einem Richtwert von 715, und niemand hat es gestoppt. Seit 2026-08-17 ist es ein FEHLER- pruefe(text, grenze_sekunden=...) in skript.py, scharf geschaltet in formate/vox/mach.py, also BEVOR Credits fliessen. WARUM DIE GRENZE NICHT IN DER VORGABE VON pruefe() STEHT- formate/archivdoku ruft dieselbe Prüfung für die Langform auf, wo zwanzig Minuten richtig sind. Stünde die Reel-Grenze in der Vorgabe, risse der nächste Grenzwert still die Langform mit. Vorgabe ist deshalb None, wer sperren will sagt es ausdrücklich. DIE GRENZE STAND EINEN HALBEN TAG AUF 60 und liegt seit dem 2026-08-17 auf 70. Merts erste Vorgabe war "unter 60 s erzwingen"; beim Nachprüfen an den eigenen Altdaten fiel auf, dass RUST daran gescheitert wäre — geschätzt 67 s, gemessen 63,5 s, und mit 3.125 Aufrufen das beste Video des Kanals. Eine Sperre, die das beste Ergebnis verbietet, misst das Falsche. Die Kante liegt zwischen 67 s (funktioniert, mehrfach angesehen) und 92 s (Totalausfall), nicht bei 60. Mert hat auf Nachfrage auf 70 entschieden. Gegengeprüft- Petrow 35 s, Songs 38 s, Radium 49 s, Kelsey 56 s und Rust 67 s gehen durch, BMI mit 97 s bleibt draußen. MERKE- eine Sperre gehört gegen die eigenen Altdaten gehalten, BEVOR sie scharf geschaltet wird. Hier war es ein halber Tag und ein Commit; beim nächsten Grenzwert kann es ein verworfenes Reel sein. TITEL- Merts Zusage lautete "Titel als Frage", die eigenen Zahlen haben das widerlegt- kein einziger der drei erfolgreichen Titel ist eine Frage. Alle drei sind Aussagen mit einem Loch ("Ein 18-Jähriger flog 1987 nach Moskau. Niemand schoss."). Der Flop ist der einzige VOLLSTÄNDIGE Satz- "Dein BMI kommt nicht aus der Medizin, sondern aus der Astronomie" stellt die Überraschung fest und löst sie im selben Atemzug auf. Regel deshalb- nach dem Titel muss eine Frage offen sein, die das Video beantwortet. Ein Fragezeichen ist erlaubt, aber die schwächere Bauart. Steht in formate/vox/SKILL.md. NICHT GEPRÜFT- ob der 3D-Papierstil auf die Haltequote wirkt. Die YouTube-Statistik hinkt drei Tage hinterher, die Abbruchkurve des BMI-Reels gibt es frühestens am 2026-08-18.
Ton bei Vox- Papier auf das, was wir selbst malen. Und ein dumpfer Schluss statt StilleTon
Aus der Vermessung eines Cardboard-Cinema-Reels, das Mert mitgebracht hat (2026-08-11). Gemessen, nicht geraten- 111 Geraeusche auf 79 s, aber nur 8 davon liegen auf einem Schnitt und 70 frei mitten in der Einstellung. Der Ton bebildert dort die HANDLUNG, nicht die Montage — ein Whoosh auf jeden Bildwechsel ist genau das, was dieses Video nicht tut. Uebertragbar ist das bei uns nicht über die Clips- ein Vox-Reel hat 11,7 Szenen, davon 1,8 bewegt, und darin driftet nur Rauch und Staub. Es gibt nichts anzufassen. Vertont wird deshalb, was ReelForge SELBST zeichnet und wessen Zeitpunkt es dadurch auf die Millisekunde kennt- Szenenwechsel und Kritzeleien. Material ist Papier, nicht Whoosh — Cardboard klingt nach Pappe, weil es aus Pappe ist; Aktenlage klingt nach Akte. Zweitens- die Pointe des Vorbilds kommt NICHT über Stille, obwohl es so klingt. Um den Schnitt ins Schwarze fallen Hoehen und Mitten weg (Hochband 38 auf 1), während der Bass steigt- eine Sekunde lang klingt es, als hielte man sich die Ohren zu. Stille ist ein Loch, Dumpfheit ist eine Handlung. Liegt bei uns auf der Schlusskarte. Beides einzeln abschaltbar (Regler "geraeusch" und "dumpf"), weil am 07.08. schon einmal ein Geraeusch komplett rausflog, obwohl nur der Pegel falsch war. NACHGEZOGEN am selben Tag- der erste Pegel (-28 dB) war unhoerbar, Merts Urteil am Testreel "ich hoere keine Soundeffekte". Die Musik liegt bei -10 dB, das Papier lag also 18 dB darunter. MESSBAR IST NICHT HOERBAR. Jetzt -18 dB und ein Schieberegler statt einer festen Zahl. Außerdem ATMO- ein durchgehender Klang unter bewegten Szenen (Typ "ton: motor" im Drehbuch, vier Typen im Repo). Der Einwand kam von Mert- ein Propeller klingt, Rauch nicht; "nichts anzufassen" galt nur für Einzelanschlaege. Kosten- einmalig zwei ElevenLabs-Geraeusche, die im Repo liegen; pro Reel null. Code- formate/vox/geraeusch.py, bau.DUMPF, probe_ton.py.
Reels zurück auf 35-55 s — die Laengenregel war falsch begruendet2026-08-10
Merts Urteil zum fertigen 105-Sekunden-Reel (2026-08-10)- "ich versteh die story einfach nicht, ich als zuschauer versteh kaum worum es geht ... die schnitte sind manchmal zu schnell ... zu viel storytelling und zu wenig offene fragen oder ein bezug zum zuschauer." DIE LAENGENREGEL WAR DER GRÖSSTE EINZELFEHLER. Am 2026-08-08 auf 60-90 s gesetzt, weil TikToks Creator Rewards erst ab 60 Sekunden zahlen. Uebersehen wurde, dass dieselben Rewards 10.000 FOLLOWER und 100.000 Aufrufe in 30 Tagen verlangen. Der Kanal hat ZWEI Abonnenten. Die Regel optimierte also auf einen Zustand, der Jahre entfernt ist, und bezahlte mit genau der Kennzahl, die einen dorthin bringt. GEMESSEN AM EIGENEN KANAL (aktenlage_zahlen.py, 60 Tage)- Petrow 37,8 s ergab 72,6 % gesehen und einen Abonnenten, Radium 49,6 s ergab 59,6 % und keinen. Zwoelf Sekunden mehr kosteten dreizehn Prozentpunkte. Der Algorithmus misst nicht die Laenge, sondern den ANTEIL. JETZT- 455-715 Zeichen (35-55 s), 12-18 Zeilen, Warnung ab 65 s. Die drei alten Reels liegen damit wieder innerhalb der Regel. DER ZWEITE FEHLER, und der ist erzaehlerisch- ich habe EINSATZ und AUSGANG verwechselt. Fassung 1 nannte in Zeile 1 den Einsatz und in Zeile 3 schon den Ausgang; danach war nichts mehr offen. Fassung 2 schob den Ausgang nach hinten und nahm dabei den EINSATZ mit- 75 Sekunden lang sah man jemandem beim Aktenschieben zu, bevor irgendwo stand, worum es geht. Beides falsch, und zwar gegenlaeufig. Richtig- Einsatz in Sekunde eins, Ausgang am Ende. DIE SCHNITTE- die Taktvorgaben stammen vom Phantom-Reel (12 Szenen, 58 s, rund 22 Einstellungen). Bei 27 Szenen auf 105 s ergaben dieselben Werte rund 50 Schnitte. Nicht der Takt war falsch, sondern die Szenenzahl; mit 15 Szenen liegt man wieder bei rund drei Sekunden je Szene. PROVOKANTE JA/NEIN-FRAGEN SIND JETZT ERLAUBT. Merts Vorschlag "Hat diese Frau tausende Kinder gerettet, indem sie nichts tat?" waere an der eigenen Regel gescheitert (nur W-Fragen im Haken). Die Regel war zu grob- schlecht ist die Frage nach dem WISSEN des Zuschauers ("Wusstest du …?"), gut ist eine Frage, die eine unglaubliche BEHAUPTUNG aufstellt. Das Paradox sieht kein Programm, also Warnung statt Fehler. ERGEBNIS- dritte Fassung, 56,1 s, 15 Szenen, kein einziges neues Bild noetig. WAS DIE KALTLESE NICHT KANN, und das ist die Lehre für das Werkzeug- sie hat die zweite Fassung BESTANDEN. Verstanden hat der Leser sie; wissen wollte niemand, wie es weitergeht. Sie misst Verstaendlichkeit, nicht Spannung, und ein LESER hat den Text vor sich, während der Zuschauer ihn einmal hoert.
Verstaendlichkeit- was der Autor kennt, kennt der Zuschauer nicht2026-08-10
Drei Befunde von Mert am fertigen AKTE-01-Reel (2026-08-10), alle drei in Code oder Regel gegossen — und alle drei Auspraegungen DESSELBEN Fehlers- der Autor haelt für allgemein bekannt, was er selbst gerade gelernt hat. (1) "keiner weiss was plazenta heißt, das darf in zukunft nicht erneut passieren." sprechtext.py hat jetzt eine Fachwortliste und bricht ab. NACHGESCHAERFT von Mert am selben Tag- "fachwoerter duerfen vorkommen aber gezielt genutzt wenn man etwas erklären moechte". Verboten ist also nicht das Wort, sondern das UNERKLAERTE Wort; steht direkt dahinter eine Erklärung (Beistrich, Gedankenstrich, Klammer, mindestens drei Woerter), geht es durch. Erkennt die Maschine die Erklärung im Satz nicht, erkennt der Hoerer sie auch nicht — das ist kein Fehlalarm zum Wegdruecken. (2) EIN ALTER MARKENNAME IST KEIN WIEDERERKENNUNGSWERT. Ich wollte "Contergan" ins Skript nehmen mit der Begruendung "das kennt in Deutschland jeder". Merts Antwort- "ich kenne contergan zb nicht." Damit ist die Annahme von der Zielgruppe selbst widerlegt- der Kanal lebt zu 97 % vom Shorts-Feed, das Publikum ist jung, und ein Markenname von 1961 ist dort kein Anker, sondern noch ein unbekanntes Wort. Getarnter als ein Fachwort- bei dem merkt man selbst, dass es Fachsprache ist, einen Eigennamen haelt man für Allgemeinwissen. Regel und Prueffrage in SKRIPT.md. (3) "das skript hat keinen schoenen roten faden, es springt zu schnell von fakten zu fakten und hat wenig spannung." skript.py misst jetzt die Faktendichte. GEMESSEN statt geraten- Petrow 25 %, Radium und Phantom je 33 % (tragen) gegen Midgley und Kelsey je 40 % (beide verworfen). Warnung ab einem Drittel. DIE URSACHE WAR ABER EINE ANDERE, und die Zahl ist nur ihr Symptom- das Skript verschenkte die AUFLOESUNG in Zeile 3 ("eine Frau, die sechsmal nein sagt"). Danach können 22 Zeilen nur noch bestaetigen, was feststeht. Neu geschrieben steht sie in Zeile 24, der Druck auf Kelsey läuft über sechs Zeilen statt einer, Faktendichte 22 %. WAS DABEI AUFFIEL- die Kaltlese-Probe hatte die ERSTE Fassung BESTANDEN. Verstanden hat sie jeder; wissen wollte niemand, wie es weitergeht. Die Probe misst Verstaendlichkeit, nicht Spannung, und das ist keine Schwaeche, die man ihr austreiben kann. Beides braucht seine eigene Prüfung. (4) "der abschluss mit nächste akte und abbonnieren ist nicht sichtbar genug." Aus dem weichen Verlauf wurde eine fast deckende Karte mit gerissener Oberkante, groesserem Titel und der Abo-Zeile als rotem Stempelrahmen — dieselbe Sprache wie die Kanalmarke am Anfang.
AKTE 01 steht — und der Weg dahin hat sechs eigene Fehler ausgeworfen2026-08-10
Fertig am 2026-08-10- uploads/vox_kelsey/REEL.mp4, 98,7 s, 25 Szenen, 1080x1920. Frances Kelsey, FDA, 1960/61 (Contergan). Erstes Reel mit Kanalmarke, Schlusskarte, echten Belegen und Quellenregister. DIE LAUFZEIT IST DER BEWEIS, nicht das Hinsehen- Sprecher 95,5 s plus Nachklang 0,8 plus Nachlauf 2,4 ergibt 98,7. Ein Reel ohne Schlusskarte waere 96,3 lang. So lässt sich auf dem Pi prüfen, was man dort nicht ansehen kann. ZWEI ECHTE BELEGE aus derselben gemeinfreien Aufnahme (Preisverleihung 07.08.1962, Werk der US-Bundesregierung)- Szene 8 ihr Portraet, Szene 23 die ganze Szene mit Kennedy. Vorher stand an beiden Stellen ein erfundenes Gesicht, während der Sprecher ihren Namen sagt. ELF ZAHLEN, ELF BELEGE. Der Quellenblock für die Videobeschreibung faellt aus quellen.yaml ab (fakten --block). WAS DER WEG ZUTAGE GEFOERDERT HAT — sechs Fehler in frisch gebautem Code, alle am echten Objekt und keiner in der Theorie- (1) tpad hielt das letzte Bild fest 2 s; mit der Schlusskarte war die Bildspur zu kurz. (2) apad verlaengerte die Stimme um einen FESTEN Betrag, der Mix endet aber mit "duration=shortest" — Bild 98,70 s, Ton 96,24 s, Abbruch nach vier Minuten Rechenzeit. Gefangen von der Laengen-Gegenprobe in abmischen(), die genau dafür am 2026-07-30 eingebaut wurde. (3) material.pruefe mass die ROHE Belegdatei statt der behandelten und forderte dazu auf, das einzige echte Foto zu beschneiden. (4) _zielname liess bei Belegen den nah-Teil weg — ausgerechnet das echte Bild hatte keine Zielpunkte. (5) AGENT.md nannte die Zahl der Szenen statt der Auftraege. (6) abnahme haette den Beleg als "gehört nicht dazu" gemeldet. UND EIN FEHLER, DER KEINER WAR- der Bildauftrag verlangte für die bewegte Hakenszene einen ruhigen Hintergrund und bestellte im Stilblock zugleich Landkarten und rote Faeden. Das Modell hat geliefert, was dastand. DAS AUSROLLEN WAR DER TEUERSTE EINZELFEHLER- das erste fertige Reel lief durch render:v45 und hatte KEINE der drei Neuerungen drin, weil der Cloud-Job den Code aus dem Abbild nimmt, nicht aus dem Ordner. Kostete einen kompletten Durchlauf. Steht als Aufgabe im Manifest und wurde trotzdem uebersehen; deshalb hier noch einmal- wer am Renderweg etwas ändert, rollt aus, BEVOR er baut.
Die Kaltlese-Probe ist eine Sperre, keine Bitte mehr2026-08-10
Merts Ansage am 2026-08-10- "die kaltlese sollte teil des automatismus sein bevor du mir den offiziellen auftrag gibst". WAS FALSCH WAR- die ganze Maschinerie stand seit dem 2026-08-02 bereit (kette.txt, frage, pruefe, Fingerabdruck gegen veraltete Kopien) und war trotzdem folgenlos, weil sie NIEMAND ERZWANG. Genau der Zustand, den der Dateikopf von kaltlese.py selbst als Ursache benennt- "es gab eine laute automatische Prüfung für Laenge und keine einzige für Verstaendlichkeit, die laute gewinnt immer". Ein drittes Dokument haette daran nichts geändert. JETZT STEHT SIE IM WEG- mach.py bricht ab, bevor es die Kosten auch nur NENNT, wenn keine bestandene Probe zum AKTUELLEN Skript vorliegt (kaltlese.bestanden, Ergebnis in .kaltlese_ergebnis.json mit Fingerabdruck). Wird danach eine Zeile geändert, verfaellt sie. DER LESER IST AUSTAUSCHBAR, DIE SPERRE NICHT- "kaltlese lesen" schickt GENAU den Inhalt von kaltlese.md an ein frisches Modell und prüft die Antwort. Ohne ANTHROPIC_API_KEY geht derselbe Weg von Hand oder über einen Unteragenten. Bewusst über urllib statt über ein Paket- ein Eintrag in requirements.txt haette den Unterbau des Render-Abbilds ungueltig gemacht, also sechs Minuten Bau für eine Prüfung, die mit dem Rendern nichts zu tun hat. WARUM EIN KLEINES MODELL LIEST (Haiku, nicht das beste)- je faehiger das Modell, desto eher rekonstruiert es fehlende Glieder aus eigenem Wissen und meldet "verstanden", während der echte Zuschauer aussteigt. Der Auftrag verbietet das Ergaenzen zwar, aber ein Verbot ist schwaecher als ein Modell, das die Luecke gar nicht fuellen kann. SOFORT EINGEFAHREN, ZWEIMAL- (1) Der Kaltleser von AKTE 01 raetselte, ob Kelseys Weigerung überhaupt etwas bewirkt hat. Es fehlte der Satz, dass das Mittel in Amerika nie VERKAUFT wurde; ohne ihn liest sich ihr Nein als folgenlos und die Pointe faellt in sich zusammen. Eine Zeile mehr, zweiter Durchgang sauber. Mechanisch waeren beide Fassungen durchgegangen — alle sieben Kettenglieder kamen schon beim ersten Mal an. Gefunden hat es der FREITEXT, nicht die Wortprobe. (2) DER FEHLALARM, der beinahe teurer geworden waere- kette.txt war als Prosa geschrieben statt mit Alternativen ("Unterschrift der Zulassungsbehoerde"), der Leser schrieb "unterschreibt die Zulassung nicht". Derselbe Schritt, und der Vergleich meldete FEHLT, weil er Woerter EXAKT vergleicht. Das ist die teuerste Richtung eines Fehlalarms- er treibt dazu, ein funktionierendes Skript zu "reparieren". lade_kette warnt jetzt bei Zeilen ohne "|". OFFEN GESAGT- die Kette wurde NACH der Messung erweitert. Das ist nur zulaessig, weil jede ergaenzte Formulierung für sich begruendbar ist ("Zulassung" gehört offensichtlich zu diesem Schritt), nicht weil sie in der Antwort stand. Wer die Kette so lange anpasst, bis es passt, hat die Probe abgeschafft.
Abo-Grund, echte Belege, Quellenregister — drei Antworten auf EINE Messung2026-08-10
Gebaut 2026-08-10 auf Merts Auswahl, nachdem ich Vox und ReelForge auf Verbesserungen durchgesehen hatte. Ausloeser aller drei Punkte ist dieselbe Zahl- 2.021 Aufrufe, EIN Abonnent, bei guter Bindung (72,6 %). Die Leute schauen zu Ende und gehen. Es fehlte kein Bild, es fehlte ein Grund zu bleiben. MERTS EIGENE IDEE WAR ECHTES FOOTAGE — und die gab es schon. formate/archivdoku ist eine vollstaendige Reel-Art aus echtem Archivmaterial, die er am 2026-07-31 selbst beerdigt hat ("tote idee, das ist null catchy"). Das wurde ihm gesagt, und stattdessen die nie probierte Variante gebaut- echtes Material als ZUTAT in der Collage statt als eigenes Format. (1) KANALMARKE UND SCHLUSSKARTE (formate/vox/marke.py, neu). Kleiner Stempel oben links in den ersten 1,8 s und am Ende "NÄCHSTE AKTE- <Titel>" plus Kanalname. Gesteuert über drei freiwillige Zeilen im drehbuch.yaml (kanal, akte, naechste_akte), von dort über mach.py und wolke.py in die Cloud. WARUM DIE MARKE KEIN VORSPANN IST- ein halbsekuendiges Logo am Anfang waere der teuerste Fehler bei Kurzvideo. Die Marke liegt deshalb ÜBER dem laufenden Haken und kostet null Laufzeit. Die Schlusskarte kostet dagegen rund drei Sekunden (bau.abmischen bekommt nachlauf=2,4) — bewusst bezahlt- Bindung hat der Kanal gemessen schon, Abonnenten nicht. WO SIE SITZT- komplett oberhalb der 402-Pixel-Grenze, ab der YouTube Shorts unten selbst einblendet. Erste Fassung stand 250 px über dem Rand und waere auf YouTube ausgerechnet mit der Kanalzeile zugedeckt worden; am Standbild gesehen, nicht im fertigen Video. (2) ECHTE BELEGE (formate/vox/beleg.py, neu). Eine Szene mit "beleg: true" und "quelle:" bekommt KEINEN Bildauftrag- ihr Bild wird beschafft, nicht erfunden. Beim Rendern kommt es in den Papierschnitt-Look (Graustufe, 45-Grad-Halbtonraster, gerissene Kante, leicht schief, roter Akzent daneben gedruckt, gealtertes Papier). Erkannt wird es am Dateinamen ("05_beleg_midgley.jpg") — dieselbe Bauart wie der Einstiegspunkt der Archiv-Doku, aus demselben Grund- was NEBEN der Datei liegt, kann sich von ihr loesen, im Namen kann es das nicht. DAS ARGUMENT, DAS DEN AUSSCHLAG GAB- bis hierher wurde jedes Bild erfunden, auch das Gesicht eines real existierenden Menschen (das Midgley-Portraet). Das ist derselbe Fehler wie eine erfundene Jahreszahl im Bild, gegen die der Bildauftrag seit dem 2026-08-02 ausdruecklich absichert- das Bild widerspricht der Wirklichkeit, und bei einem Erklaerformat ist das ein Glaubwuerdigkeitsproblem. Der Kanal heißt Aktenlage und zeigte bis jetzt keine einzige echte Akte. GRENZE, UND SIE IST BEGRUENDET- hoechstens ein Drittel der Szenen. Darüber ist es wieder die Archiv-Doku, die Mert abgelehnt hat. prompts.pruefe und beleg.vorbereiten warnen beide. RECHTE- nur gemeinfrei. CC-BY verlangt Nennung, CC-BY-SA verlangt dieselbe Lizenz fuers Ergebnis (bei einem monetarisierten Video unbrauchbar). "quelle:" ist Pflicht, sonst bricht die Drehbuchpruefung ab- ohne Nachweis kann man einem falschen Rechteanspruch auf YouTube nicht fristgerecht widersprechen. SPART AUSSERDEM 40 Credits je ersetztem Bild (bestellen.kosten zählt Belege nicht mit, bestellen.bestelle bricht ab statt sie erzeugen zu lassen). (3) QUELLENREGISTER (formate/vox/fakten.py, neu). Liegt ein quellen.yaml neben dem Skript, prüft mach.py vor jeder Bestellung- jede Jahreszahl und jede Zahl im Skript braucht einen Beleg, sonst läuft nichts. Fehlt die Datei, bleibt es bei einem Hinweis — alles andere haette alle bestehenden Reels auf einen Schlag gesperrt. DIE BELEGE ZITIEREN WOERTLICH aus dem Skript statt Zeilennummern zu nennen. Eine Zeilennummer waere kuerzer und genau deshalb falsch- zwei Dateien, die zeilenweise zueinander passen müssen, laufen auseinander, sobald jemand einen Satz einfuegt. Am umgeschriebenen Midgley-Satz nachgestellt- der Pruefer meldet beides, das verwaiste Zitat UND die nun unbelegte Zahl. EHRLICHE GRENZE- das Werkzeug prüft die BUCHFUEHRUNG, nicht die Wahrheit. Kein Programm weiss, ob 1921 stimmt. Der Gewinn ist, dass "einmal nachschlagen" zur Bedingung wird statt zur guten Absicht — und der Quellenblock für die Videobeschreibung faellt nebenbei ab (alle Vorbilder haben einen). DIE ZAEHLUNG FAENGT BEI 01 AN (Merts Entscheidung am selben Tag, auf meinen Vorschlag hin). Die fünf vorhandenen Reels (Eiffel, Midgley, Petrow, Phantom, Radium) zählen NICHT mit- sie tragen keine Nummer im Bild, und eine Reihe, die für den Zuschauer bei 06 beginnt, wirft nur die Frage auf, wo die ersten fünf sind. Nachgeschnitten wird ohnehin nichts (Regel vom 2026-08-09, nur nach vorn). Form "AKTE 01", zweistellig — einstellig würde die Marke ab der zehnten Folge im Bild springen, weil sie links sitzt und nach rechts wächst. mach.py meldet eine abweichende Form und sieht außerdem in den Nachbarordnern nach, ob eine Nummer schon vergeben ist- zwei Reels mit "AKTE 03" faellt beim Bauen niemandem auf, sondern erst dem Zuschauer, der beide sieht. WAS NOCH NICHT GEPRÜFT IST- die drei Bausteine sind einzeln am Standbild nachgesehen (Marke, Schlusskarte, behandelter Beleg als PNG), die neue ffmpeg-Filterkette für die zwei Einblendungen aber noch NIE gelaufen. Auf dem Pi geht das nicht (Render-Sperre). Erster echter Beweis ist das nächste Reel; bis dahin gilt die Kette als unbewiesen, nicht als fertig.
Kanalseite auf Abonnenten eingestellt- Trailer, Versprechen, Stichwoerter2026-08-09
Am 2026-08-09 gesetzt, direkt nach der Messung (2.021 Aufrufe, EIN Abonnent). Merts Ansage- "dann lass den schritt machen". TRAILER FÜR NICHT-ABONNENTEN- der Petrow-Short (meistgesehen UND beste Bindung, 72,6 % der Laenge). Vorher lief bei einem Klick auf die Kanalseite gar nichts; der Besucher sah eine Liste und musste selbst wählen. Der Trailer uebernimmt genau den Moment, in dem jemand ueberlegt, ob er bleibt. BESCHREIBUNG- Merts Zeile bleibt oben stehen, darunter steht jetzt, WAS man bekommt und warum man abonnieren sollte. Vorher sagte sie nur, was der Kanal ist. STICHWOERTER- vorher keine. Damit fehlte YouTube der Anhaltspunkt, in welche Nachbarschaft der Kanal gehört. AM 2026-08-10 NACHGEZOGEN (Mert- "alles gute hinweise setze sie um")- BANNER (aktenlage_banner.py, neu). Die entscheidende Zahl- das Bild ist 2048x1152, ueberall sichtbar ist aber nur ein Streifen von 1235x338 in der MITTE. Auf dem Fernseher sieht man alles, auf dem Handy nur den Streifen. Wer den Kanalnamen ausserhalb setzt, hat ihn für die Mehrheit nicht gesetzt; das Skript bricht ab, wenn der Text nicht hineinpasst. Grundlage ist wie beim Vorschaubild ein Bild aus den Videos selbst. BESCHREIBUNG NEU GESCHRIEBEN. Merts alte Zeile "Geschichten die NIEMAND aufgeschrieben hat" ist raus, auf seine Ansage hin- zu Midgley gibt es ein Veritasium-Video mit 30 Mio. Aufrufen, die Behauptung war angreifbar. Jetzt "Geschichten, die man dir nie erzaehlt hat" — genauso stark und nicht widerlegbar. ZWEITE FASSUNG NACH VORBILDERN (Mert- "schau dir mal lieber an wie andere youtube kanaele beschreibungen aussehen"). Statt Ratgeber-Artikeln die Beschreibungen echter Kanaele über die Schnittstelle abgefragt (Simplicissimus 2,5 Mio, MrWissen2go Geschichte 1,5 Mio, Terra X History 1,4 Mio, Y-Kollektiv 1,2 Mio, ARTE 2,7 Mio). GEMEINSAMES MUSTER, alle fünf- (1) Der inhaltliche Teil ist KURZ, ein bis drei Sätze. Alles danach ist Organisation (Links, Impressum, Netiquette), nicht Werbung. (2) Direkte Anrede- "Du bist ein Geschichts-Nerd?", "Bei Terra X History bekommt Ihr", "Hier findet ihr". (3) Gesagt wird, WAS man bekommt, nicht wie toll es ist. Simplicissimus- "Schoen, verstaendlich, kritisch und fundiert. Wir machen Essays zu Fragen, die du dir noch nie, oder viel zu oft gestellt hast." (4) WER EINEN RHYTHMUS HAT, NENNT IHN- "Neue Folgen gibts jeden Montag!", "jeden Sonntag". Steht bei uns bewusst NICHT drin, solange Mert keinen zugesagt hat. (5) Geschaeftliche Kontaktadresse ist ueblich. Fehlt bei uns noch. Rausgeflogen ist dadurch die Zeile "manchmal als ganze Ermittlung über eine halbe Stunde" (Merts Urteil- "finde ich scheisse"). Kein Vorbild beschreibt seine Videolaengen; jetzt steht dort "Kurze Faelle, lange Recherchen." KOMMENTARE unter beiden Shorts gesetzt- ein Nachtrag zur Geschichte plus die Aufforderung zu abonnieren. ANHEFTEN MUSS MERT, die Schnittstelle kann das nicht. FEHLER DABEI, korrigiert- in der ersten Fassung stand "jede Woche einen Fall". Diesen Zeitplan hat Mert nie zugesagt; ein Versprechen, das der Kanal nicht haelt, ist schlimmer als keines. Jetzt steht dort "Hier liegen mehr solcher Akten". NOCH OFFEN- der Abo-Hinweis am Ende der Reels. Gehört in den Renderweg, lässt sich auf dem Pi aber nicht ansehen (kein Rendern) — deshalb zusammen mit dem nächsten Reel bauen und im selben Lauf prüfen, statt blind einzubauen.
GEMESSEN- 97 % kommen aus dem Shorts-Feed, 0,9 % aus der Suche2026-08-09
Erste echte Zahlen des Kanals (aktenlage_zahlen.py, neu am 2026-08-09; liest die YouTube-Analyse-Schnittstelle, Zeitraum 28 Tage, 2.021 Aufrufe). ANLASS WAR MERTS WIDERSPRUCH- "ich denke das sehr wenig leute blei im benzin suchen". Er hatte recht, und zwar deutlicher als gedacht. HERKUNFT DER AUFRUFE- Shorts-Feed 96,9 % · andere YouTube-Seite 1,4 % · SUCHE 0,9 % (19 Aufrufe) · Kanalseite 0,7 %. DIE SUCHBEGRIFFE SIND REINES RAUSCHEN- "monte polizei", "cobra kai edit", "horrorfilme", "lustige videos zum totlachen", "ronaldo edit 2008 skills". KEIN EINZIGER hat mit unseren Themen zu tun. Das sind Leute, die etwas anderes gesucht haben und im Shorts-Feed hier gelandet sind. WAS DARAUS FOLGT- Suchbegriffe im TITEL kaufen nichts. Der Titel gehört ganz dem Klickanreiz; die Begriffe stehen in Beschreibung, Stichwoertern und Untertiteln, wo sie nichts kosten. Für die Langform gilt das nur eingeschraenkt- 36 Minuten laufen nicht im Shorts-Feed, dort ist Suche und Vorschlag der einzige Weg. Erste Messung dazu fruehestens in ein bis zwei Wochen. DAS EIGENTLICHE PROBLEM STEHT WOANDERS- 2.021 Aufrufe ergaben EINEN Abonnenten (0,05 %). Die Zuschauerbindung ist gut (72,6 % bzw. 59,9 % der Laenge), die Leute schauen also zu Ende und gehen dann. Es fehlt ein Grund zu bleiben, nicht Reichweite. Das ist der nächste Hebel, nicht SEO. WAS DAS WERKZEUG NICHT KANN- Suchvolumen vorhersagen. YouTube veroeffentlicht keine echten Volumen; jede Zahl in einem SEO-Werkzeug ist geschaetzt. Wer vorab Themen vergleichen will, nimmt den Reiter "Recherche" im Studio (nur niedrig/mittel/hoch) oder Google Trends mit YouTube-Filter. TECHNIK- Recht yt-analytics.readonly (nur lesen) am 2026-08-09 dazugenommen, Mert hat sich neu angemeldet. Außerdem musste die Analytics-Schnittstelle im Google-Projekt beatfactory-497615 erst freigeschaltet werden (gcloud services enable youtubeanalytics.googleapis.com).
YouTube-Beschreibungen- kein KI-Hinweis, 10-12 Hashtags, Suchbegriffe nach vorn2026-08-09
Merts Ansage am 2026-08-09 nach dem Start des ersten Langvideos- "die KI raus" und "fünf Hashtags sind nicht zu wenig für YouTube". KEIN KI-HINWEIS MEHR- der Satz "Bilder und Sprecherstimme sind KI-erzeugt" ist aus allen drei Videos raus. Vertretbar, weil YouTubes Offenlegungspflicht auf REALISTISCHE Darstellungen zielt, die jemanden taeuschen können — unsere Bilder sind erkennbar Papiercollagen, und die Stimme ahmt niemanden nach. Wer es doch kennzeichnen will, nimmt den Schalter "verändertes Material" im Studio, nicht die Beschreibung. HASHTAGS 10-12 STATT 5- die 5er-Regel stammt von TikTok/Instagram und galt hier faelschlich mit. Laut YouTube-Hilfe werden Hashtags erst ab 60 ignoriert; über dem Titel erscheinen bis zu drei, also die staerksten nach vorn. NICHT geprüft ist, ob mehr Hashtags überhaupt etwas bringen — die Wirkung liegt bei Titel, den ersten ~160 Zeichen und den Untertiteln. Mehr als ein Dutzend waere Deko. SUCHBEGRIFFE IN DIE ERSTEN 160 ZEICHEN- vorher stand dort nur die Todesszene, "Blei im Benzin" und "Ozonschicht" kamen zwei Absaetze später. Kostet etwas Dramatik in der Beschreibung, die aber ohnehin niemand vor dem Klick liest — dafür sind Titel und Vorschaubild da. WERKZEUG- Skill "youtube-seo" (kostja94/marketing-skills, 2.5K Installationen, 871 Sterne, drei Sicherheitspruefungen bestanden, kein API-Schluessel) liegt jetzt in skills-lock.json. Kompakte Checkliste für Titel/Beschreibung/Tags. Für Kanal-ANALYSE gibt es nichts Brauchbares- die gefundenen Analyse-Skills liegen alle unter 500 Installationen oder haengen an einem kostenpflichtigen Fremddienst. Zahlen holt man besser direkt über die YouTube-Schnittstelle.
Erstes Langvideo veroeffentlicht — Titel, Vorschaubild, Untertitel2026-08-08
Am 2026-08-08 hochgeladen, geplant für Sonntag 2026-08-09 18-00 Uhr- "Er hat die ganze Welt vergiftet — und du traegst es bis heute in dir" (Video AMpF0dm4AWM, 36-02, 13 Kapitelmarken, 16 Stichwoerter). MERTS VORGABE- "titel und thumbnail müssen allgemein extrem anziehend sein und gerne auch uebertreibend". Umgesetzt mit einer Grenze, die keine Geschmacksfrage ist- jeder Satz auf Titel und Bild kommt im Video woertlich vor. Ein Versprechen, das nach einer Minute platzt, holt den Klick und verliert ihn wieder — und genau das misst YouTube. TITEL UND BILD SAGEN VERSCHIEDENES- die Zahl im Bild ("800 MILLIONEN IQ-PUNKTE WEG"), die Behauptung im Titel. Zweimal derselbe Satz verschenkt die halbe Fläche. VORSCHAUBILD AUS DEM FILM SELBST (formate/vox/langvorschaubild.py, neu)- kein eigens bestelltes Bild. Kostet nichts, und der Zuschauer bekommt beim Klicken genau das, was er gesehen hat. Regeln- Gesicht nicht ueberdecken (das Laecheln IST der Klickgrund), hoechstens vier Woerter, unten rechts frei für die Laufzeit-Einblendung, dunkler und kontrastreicher als das Original. UNTERTITEL BRAUCHEN "youtube.force-ssl"- das Recht "youtube" reicht NICHT, obwohl es größer klingt. Aufgefallen erst, als das Video schon oben lag. Scope in youtube_verbinden.py ergaenzt, Mert hat sich am selben Tag neu angemeldet, Spur hängt dran (Stand "serving"). Wichtig, weil die Langform bewusst KEINEN Text ins Bild brennt- ohne Untertiteldatei ist das Video über seinen gesprochenen Inhalt nicht auffindbar. OHNE ABSPANN- das Abschlussbild wurde für dieses Video nie bestellt, also endet es eine Sekunde nach dem letzten Wort. Folge- keine YouTube-Verweiskaesten, sie laegen über dem Schlusssatz. Neu rendern (rund 1 EUR, anderthalb Stunden) waere für ein Verweisziel, das es noch gar nicht gibt. Beim nächsten Langvideo von Anfang an mitbestellen. DER VERWEIS IN DEN SHORTS TRAEGT SICH SELBST NACH (aktenlage_verweis.py, cron am 9.8. alle 10 min zwischen 18 und 23 Uhr). Merts Frage- "kannst du das nicht automatisch mit dem job morgen machen". Wichtig- einen Job, an den man sich haengen könnte, gibt es NICHT — das Veroeffentlichen macht YouTube selbst, auf dem Pi läuft dafür nichts. Das Skript wartet deshalb nicht auf die Uhr, sondern fragt den ZUSTAND ab (privacyStatus == public) und darf beliebig oft laufen- steht der Link schon drin, tut es nichts. Ein Termin kann sich verschieben, eine Verarbeitung haengen; ein toter Link in den zwei meistgesehenen Videos des Kanals waere schlimmer als gar keiner.
Geld-Strategie: Langform verdient, Reels werben — und Reels gehen auf 60-90 s2026-08-08
Entschieden von Mert am 2026-08-08 nach einer Recherche zu Monetarisierung und Nische (Web + YouTube-Transkripte). DER BEFUND, DER ALLES ANDERE ERKLAERT- Kurzvideos zahlen fast nichts. YouTube Shorts bringen 0,01-0,15 $ je 1.000 Aufrufe. TikToks Creator Rewards zahlen überhaupt nur für Videos AB 60 SEKUNDEN (dazu 10.000 Follower und 100.000 Aufrufe in 30 Tagen). Das Geld liegt in der Langform- Doku/Geschichte bringt rund 5-12 $ je 1.000 Aufrufe, deutschsprachiges Publikum etwa die Haelfte davon. (1) REELS GEHEN AUF 60-90 SEKUNDEN. Bis dahin galten 20-25 s, später 30-43 s. Der alte Befund ("unter 25 s werden Reels am haeufigsten zu Ende gesehen") ist NICHT widerlegt, sondern ueberstimmt- ein 40-Sekunden-Reel kann viral gehen und trotzdem null einbringen. Bezahlter Preis- weniger Durchsichten je Video. Umgesetzt in SKRIPT.md (Drei-Boegen-Aufbau statt einem) und skript.py (780-1.170 Zeichen, 24-36 Zeilen, Warnung ab 100 s). Wer bewusst nur Reichweite will, darf weiter kuerzer — dann ohne Erwartung an Einnahmen. NUR NACH VORN, NICHTS WIRD NACHGEZOGEN (Merts Klarstellung 2026-08-09- "es sollen keine jetzigen Videos ueberarbeitet werden, sondern nur für die Zukunft Vorgaben gemacht werden"). Die vorhandenen Reels — Eiffel, Midgley, Petrow, Phantom, Radium, Songs — bleiben wie sie sind- nicht nachschneiden, nicht neu vertonen, nicht ersetzen. Auch nicht "kurz mal auf 60 s bringen". Der Pruefer meldet bei alten Skripten eine Laengenwarnung; die ist richtig und trotzdem folgenlos. Aus demselben Grund werden aus dem ersten Langvideo VORERST KEINE neuen Reels geschnitten — der Vorschlag lag auf dem Tisch und wurde nicht angenommen. (2) REELS ENTSTEHEN AUS DER LANGFORM, nicht daneben. Aus einem 20-Minuten-Skript fallen drei bis fünf Reels, deren Recherche schon bezahlt ist; jedes verweist am Ende auf das lange Video. Damit ist die Reihenfolge festgelegt- erst die Langform, dann die Reels. Auf YouTube laufen dieselben 60-90-s-Stuecke als Shorts und fuehren zum Langvideo. (3) NISCHE BLEIBT, WIRD ABER SCHAERFER GEFASST- Wissenschafts-, Technik- und Ermittlungsgeschichten mit einem menschlichen Fehler im Zentrum (Midgley, Radium-Maedchen, Petrow, Phantom). NICHT reines True Crime- der deutsche Markt ist dort voll (Crimify, ZDFinfo, Wahre Verbrechen) und Gewaltthemen druecken den Werbepreis. Das Vorbild ist belegt- der Kanal "Fern" (~5 Mio. Abos in gut zwei Jahren, erzaehlte Einzelfall-Dokus) stammt von denselben Gruendern wie Simplicissimus. Simplicissimus macht Zeitgeschehen mit grossem Team; die erzaehlte Einzelfall-Doku in dieser Bauart macht auf Deutsch niemand — das ist die Luecke. (4) AKTUELLE THEMEN SIND ERLAUBT, ABER IN DERSELBEN NISCHE (Merts Zusatz). Sie ziehen Aufmerksamkeit, kosten aber den Vorteil der Nische- Geschichte veraltet nicht, Videos von vor drei Jahren bringen noch Aufrufe. Faustregel- der Rahmen bleibt "wie es dazu kam", das aktuelle Ereignis ist der Anlass, nicht der Inhalt. AUSGEBAUT AM 2026-08-09 ZUR ARBEITSTEILUNG DER FORMATE. Merts Idee- "die Long Form Videos werden hauptsaechlich Storys aus alter Zeit die man noch in Zukunft anschauen wird und die Reels werden hauptsaechlich News von der heutigen Zeit." Der erste Teil ist uebernommen, der zweite abgewandelt, und Mert hat der Abwandlung zugestimmt ("deine ist gut"). UEBERNOMMEN- Langvideo = zeitlose Geschichten (verdient über Jahre, der Rueckenkatalog traegt), Reel = darf am Tagesgeschehen haengen (ist nach zwei Tagen ohnehin tot, Vergaengliches ist dort billig). Die Zuordnung Haltbarkeit zu Format ist richtig gedacht. ABGEWANDELT- das Reel erzaehlt KEINE Nachricht, es geht durch die Schlagzeile hinein und holt die Geschichte aus dem Archiv- nicht "das ist gestern passiert", sondern "das ist gestern passiert, und es ist schon einmal passiert, 1937, und das hat man daraus gelernt". WARUM NICHT EINFACH NEWS- der Algorithmus lernt pro KONTO, nicht pro Format. Dieselbe Ueberlegung, aus der hier keine Musikvideos laufen- mischt man zwei Sorten, lernt er gar nichts. Nachrichten-Reels bringen Nachrichten-Publikum, und das klickt keine 36-Minuten-Doku über einen Chemiker aus den 1920ern. Die Reichweite kaeme an, nur nicht dort, wo sie hin soll — und der einzige Zweck der Reels ist dieser Fluss ins Langvideo. Dazu- News heißt Tempo statt Sorgfalt und ein falscher Satz beschaedigt einen Kanal, dessen Kapital Verlaesslichkeit ist; News-Reels sind das ueberfuellteste Format überhaupt; und 90 Sekunden sind für eine Nachricht langsam. KEINE QUOTE (Merts Nachschaerfung am selben Tag)- hier stand kurz "etwa jedes dritte Reel". Sein Einwand- "am besten nicht fest jedes dritte Reel, sondern je nachdem ob es etwas Aktuelles gibt, wo man eine gute Story zu erzaehlen kann." Richtig, und die Quote ist raus. Eine Quote zwingt dazu, einen Aufhaenger zu suchen, auch wenn keiner da ist — dann biegt man sich eine schwache Verbindung zurecht, und genau daran merkt der Zuschauer, dass er verkauft wird. DER PRUEFSTEIN, DER DIE QUOTE ERSETZT- die Schlagzeile zudecken. Bleibt ohne den aktuellen Bezug immer noch eine Geschichte übrig, die man erzaehlen würde? Ja = bauen. Nein = News-Reel mit historischer Verzierung, gehört nicht auf diesen Kanal. Das erledigt auch die Sorge hinter der Quote- ein aktueller Fall mit tragfaehiger Geschichte dahinter ist selten, die Reels bleiben also von selbst ueberwiegend zeitlos, ohne dass jemand mitzaehlt. Echte News gehören auf ein EIGENES Konto, nicht auf Aktenlage — solange der Kanal keine 1.000 Abonnenten hat, ist das aber doppelte Arbeit ohne Gegenwert. Ausgeschrieben in formate/vox/SKRIPT.md ("Themenwahl"). (5) HOCH-CPM-NISCHEN (Finanzen, Recht, Software) WURDEN GEPRÜFT UND VERWORFEN. 15-50 $ je 1.000 klingt gut, ist aber genau das Muster, das YouTube am haertesten trifft- KI-Stimme plus Finanzratschlag plus kein echter Absender. WAS DIE POLITIK-RECHERCHE ERGEBEN HAT- YouTubes Regel "Inauthentic Content" (Juli 2025, im Juli 2026 nachgeschaerft) verbietet nicht KI, sondern Fliessband ohne Wert- gleiche Schablone täglich neu befuellt, erfundene Person als Moderator, fremdes Material nur umsortiert. Aktenlage ist sauber (eigene Recherche, eigenes Skript, Marke statt erfundener Person). Vollautomatik bleibt deshalb ausgeschlossen- die Maschine uebernimmt Produktion, nicht Urteil.
YouTube-Anbindung für Aktenlage — abgeschaut bei beatfactory2026-08-07
Gebaut 2026-08-07 auf Merts Auftrag "schau wie beatfactory mit YouTube redet und verknuepf das mit Aktenlage". WIE ES BEI BEATFACTORY LÄUFT- Google-Cloud-Projekt beatfactory-497615 haelt den Zugangsschluessel (client_secret.json), die einmalige Anmeldung liegt als youtube_token.json daneben, und beat_generator.py frischt sie bei jedem Lauf selbst auf. Rechte- youtube.upload + youtube. Läuft seit Monaten, drei Videos am Tag auf "Valvet Beats", alle oeffentlich. WAS HIER NEU IST- youtube_verbinden.py (einmalige Anmeldung, schreibt aktenlage_youtube_token.json) und youtube_hochladen.py (Hochladen, Kategorie Bildung, Sprache Deutsch, standardmaessig NICHT oeffentlich). DERSELBE ZUGANGSSCHLUESSEL, GETRENNTE ANMELDUNG- das Google-Projekt wird mitbenutzt, der Kanal nicht. Grund- ein frisches Projekt müsste bei Google erst durch die Prüfung, bis dahin sind Uploads gesperrt; beatfactory-497615 ist nachweislich durch (die Videos dort sind oeffentlich). Was man sich einhandelt- beide Kanaele teilen sich das Tageskontingent von 10.000 Einheiten. Ein Upload kostet 1.600, beatfactory verbraucht rund 5.000 am Tag. Für ein Reel alle zwei Tage reicht das dreifach; wer täglich mehr als zwei hochlaedt, läuft rein. SCHUTZ GEGEN DEN FALSCHEN KANAL- auf dem Pi liegen jetzt zwei YouTube-Anmeldungen. youtube_hochladen.py fragt vor jedem Upload den Kanalnamen ab und bricht ab, wenn nicht "Aktenlage" drinsteht. Ein Reel auf dem Beat-Kanal faellt sonst erst auf, wenn es oeffentlich ist. ANMELDUNG GEHT NUR MIT MERT- der Pi hat keinen Bildschirm, und Google leitet nach dem Login auf localhost zurück (= der Rechner mit dem Browser, nicht der Pi). Das Skript lässt deshalb die Adresse aus der Adresszeile zurueckkopieren; mit --adresse/--code lässt sich das auf zwei Schritte aufteilen (fuers Handy). ERLEDIGT AM 2026-08-08- verbunden ist der Kanal "Die Aktenlage" (UC59TfweCIGW9YlptJslzmNA), Rechte youtube.upload + youtube. IM AUGE BEHALTEN- Google zeigt beim Anmelden "Google hasn t verified this app". Das heißt nur, dass die App nie zur Prüfung eingereicht wurde. Steht sie bei Google auf "Test" statt "In Produktion", verfaellt die Anmeldung nach 7 TAGEN und muss neu gemacht werden. Beweis dafür oder dagegen gibt es erst durch Warten- läuft "youtube_hochladen.py --stand" nach dem 2026-08-15 noch, ist alles gut; kommt "invalid_grant", im Google-Cloud-Projekt beatfactory-497615 den Zustimmungsbildschirm auf "In Produktion" stellen und einmal neu anmelden. NOCH NICHT AUTOMATISCH- ReelForge laedt weiterhin nichts von selbst hoch. Bewusst- der Kanal ist neu und im Warmup, und TikTok bleibt Hauptplattform.
Langform: eigenes Format, Querformat als WERT, Untertitel als Datei
Gebaut 2026-08-06, die ganze Kette vom Drehbuch zum Video (langschnitt, langstimme, languntertitel, langwolke, langmach; Format "voxlang", gesperrt bis zur Abnahme eines echten Videos). DREI ENTSCHEIDUNGEN, die man auch anders haette treffen können- (1) QUERFORMAT IST EIN WERT, KEIN SCHALTER. bau.py bekommt die Leinwand durchgereicht, Vorgabe bleibt Hochformat. Ein globaler Schalter waere kuerzer gewesen und genau deshalb falsch- hier arbeiten mehrere Sessions im selben Ordner, und ein Reel, das still im Querformat rauskommt, faellt erst am fertigen Video auf. (2) EIGENES FORMAT statt Schalter an vox- Leinwand, Takt und Textebene sind in allen drei Punkten gegenteilig; als Schalter bekaeme jeder Regler zwei Bedeutungen. (3) KEIN EINGEBRANNTER TEXT (Merts Entscheidung). Eingebrannte Untertitel sind eine Hochformat-Konvention — im Feed wird stumm gescrollt. Auf YouTube wird mit Ton geschaut, und Reel-Text lässt ein Langvideo nach Reel aussehen. Stattdessen eine SRT-Datei aus demselben Renderlauf, in dem die Wortzeiten ohnehin vorliegen. DIE STIMME KOMMT IN STUECKEN- 28.938 Zeichen passen in keine einzige Anfrage. Wer einfach zerschneidet, hoert an jeder Naht einen Neuanfang; deshalb geht zu jedem Stück der Text davor und danach als Zusammenhang mit (previous_text/next_text). Zusammengefuegt wird in der Cloud, weil auf dem Pi nicht gerendert wird. TAKT- 4,5s je Einstellung statt 2,8 beim Reel, und zwei begruendete Schnitte müssen 2,5s auseinanderliegen statt 1,3. Gemessen am Midgley-Drehbuch- 504 Einstellungen, Mittel 3,7s, keine unter 1,7 oder über 5,4. Mit den Reel-Werten waere jedes Bild in acht bis zehn Schnipsel zerfallen.
Ein Langvideo ist nicht ein langes Reel — die Zeitgrenze haette es zerrissen
Gemessen 2026-08-06 an acht echten Laeufen- ein 45-Sekunden-Reel braucht 3 bis 4,5 Minuten, davon rund 1,5 Minuten Anlauf. Also zwei Minuten echte Rechenzeit. Ein 31-Minuten-Video ist das Einundvierzigfache, hochgerechnet gut 80 Minuten — die Zeitgrenze des Cloud-Jobs stand auf EINER Stunde. Der Lauf waere nach 60 Minuten abgebrochen worden, ohne Ergebnis, nach einer Stunde Warten. Grenze auf VIER STUNDEN gesetzt (gcloud run jobs update reelforge-render --task-timeout=4h). Eine Obergrenze kostet nichts, solange sie nicht gebraucht wird; für die Reels ändert sich nichts. NOCH KEINE MESSUNG, sondern eine Hochrechnung — der erste echte Lauf ist auch die erste Messung.
Flow braucht einen laufenden PC — die fehlenden Bilder kamen über OpenArt
Merts Frage 2026-08-03, während er einige Tage weg war- "wir haben ja jetzt ein fertiges Flow-Skript, waere es jetzt nicht möglich eine Browser-Automation laufen zu lassen?" NEIN. flow_bot hängt sich an ein Chrome, in dem Mert SELBST angemeldet ist — das ist keine Bequemlichkeit, sondern die einzige Bauart, die Google zulaesst. Es braucht also einen eingeschalteten Rechner mit dieser Anmeldung; sein PC war aus (Syncthing meldete das Geraet offline). Auf dem Pi nachbauen scheidet aus- kein Google-Konto, und seine Zugangsdaten gehören nicht dorthin. DER WEG, DER OHNE PC GEHT- langbestellen.py über OpenArt. Entscheidend ist, dass es DASSELBE Bildmodell ist (nano-banana-pro), derselbe Auftragstext, dasselbe Format — die 22 Nachzuegler sehen aus wie die 91 aus Flow (alle 2752x1536). Unterschied ist allein der Preis- bei Flow 0, bei OpenArt 40 Credits je Bild. 22 Bilder = 880 Credits, von Mert freigegeben. Damit ist das Midgley-Bildmaterial vollstaendig.
Sprechertext darf nie zugeben, dass er ein Text ist — mechanisch geprüftneu
Mert am 2026-08-02 zum Langform-Skript: "Die Frage, um die es in den nächsten fünfundzwanzig Minuten geht" — "man sagt sowas nicht. Schau dir mal Skripte von Dokus an." Zutreffend, und der Fehler saß an acht Stellen. Es sind zwei Sorten. ERSTENS redet der Text über das Video (Laufzeit, "Halbzeit", "in diesem Kapitel", "wie eingangs erwähnt"). Solange der Text nur von Midgley redet, sitzt der Zuschauer 1924 in der Fabrik; fällt "in den nächsten 25 Minuten", sitzt er wieder auf dem Sofa vor einem Video — derselbe Effekt wie eine Kamera im Spiegel. ZWEITENS benotet der Erzähler sein eigenes Material ("das ist wichtig", "der Grund, warum diese Geschichte hängenbleibt"). Wer das schreibt, hat es nicht wichtig gemacht, sondern bittet den Zuschauer darum, es wichtig zu finden — Wirkung genau umgekehrt. Reparatur ist fast immer: Satz streichen, das Material stand ohnehin direkt dahinter. Neu: formate/vox/sprechtext.py prüft beide Sorten als FEHLER (blockiert den Bau) und dazu die Maße fürs Ohr als WARNUNG — Satzlänge (25/35), gestapelte Nebensätze, Passivanteil, unhörbare Zahlen. Nicht über Kommas gezählt: Aufzählungen sind gutes Handwerk. Einzige erlaubte Selbstnennung bleibt die gesprochene Kapitelkarte, weil sie Überschrift ist und kein Verweis.
Long-Form wird in KAPITELN gebaut — jedes mit eigener Karte, im Bild und gesprochen2026-08-02
Merts Entscheidung am 2026-08-02, zum Start der Long-Form-Videos (Querformat, 20-30 Minuten): "ich faende es geil wenn wir jedes Video so aufbauen dass es verschiedene Kapitel gibt. Und die Kapitel bekommen ein eigenes Bild mit Text im Bild — Kapitel 1. Thema xyz — und der Sprecher sagt das auch." DAS PASST GENAU ZU DEM, WAS DIE RECHERCHE SAGT. Ein 25-Minuten-Video ist nicht EINE lange Ursachenkette, sondern sechs bis zehn kurze. Der haeufigste Grund für einen Steilabfall in der Retentionskurve ist nicht schlechter Inhalt, sondern GLEICHFOERMIGKEIT — und eine Kapitelkarte ist ein Musterbruch, also genau das Gegenmittel. Nebenbei liefert sie die Zeitmarken für die YouTube-Beschreibung. DIE REGEL FÜR DIE KARTE, aus dem Schlussbild der Reels gelernt: der Text steht IM Bild, nicht als Stempel darüber, und er muss KURZ sein. Ein Wort ("RADIUM") kommt zuverlaessig, ein ganzer Satz kann als Buchstabensalat zurueckkommen. "KAPITEL 1 — DAS KLOPFEN" ist die Größenordnung; länger als rund 24 Zeichen wird der Titel nicht. Zweimal hat das beim Schlussbild buchstabengenau geklappt, das ist die Erfahrungsgrundlage. VOR DEM ERSTEN KAPITEL STEHT IMMER EIN INTRO (Merts Zusatz, gleicher Tag)- "um die Neugier und Spannung aufzubauen und eine offene, nicht beantwortete Frage im Raum stehen zu lassen." Deckt sich mit der Recherche: der Haken eines Langvideos hat rund 20 Sekunden und muss DREI Dinge tun — den Klick rechtfertigen, den Einsatz nennen und die erste Neugier-Schleife oeffnen, OHNE sie zu schließen. Das Intro ist damit kein Vorspann, sondern das Versprechen, das die restlichen 25 Minuten einloesen. Und die Frage, die es offenlaesst, ist die Klammer über alle Kapitel. WAS DAMIT AUCH ENTSCHIEDEN IST- der Schnittvertrag "eine Zeile = eine Szene = eine Datei" gilt für Long-Form NICHT. 25 Minuten waeren rund 300 Dateien; Long-Form läuft mit B-Roll, die 10 bis 20 Sekunden unter der Erzaehlung liegt. Die Kapitelkarte ist dabei die einzige Szene, die weiter fest an EINER Zeile hängt.
UEBERHOLT am 2026-08-08 — Google Flow hat ein Tageslimit, damit ist es für Long-Form raus2026-08-02
ACHTUNG, DIESER EINTRAG GILT NICHT MEHR. Mert am 2026-08-08- "ich habe ein Abo auf Google Flow, damit kann ich viel mehr Bilder am Tag generieren." WARUM DER ALTE BEFUND TROTZDEM RICHTIG GEMESSEN WAR- ohne Abo gibt Google Flow 50 Credits am TAG. Das deckt sich genau mit dem, was hier beobachtet wurde ("nach zehn Bildern plus Testlaeufen macht Flow dicht"). Gemessen wurde also der kostenlose Stand, nicht das Werkzeug. Mit Abo läuft die Zuteilung nicht mehr täglich, sondern MONATLICH- AI Plus 200, AI Pro 1.000, AI Ultra 10.000 bzw. 25.000 Credits im Monat. Nicht verbrauchte Credits verfallen, sie wandern nicht in den nächsten Monat. DER BEWEIS LAG DIE GANZE ZEIT IM ORDNER, und zwar vom SELBEN ABEND. Die 113 Bilder in uploads/lang_midgley/bilder stammen aus Flow, geholt und benannt von flow_bot.py- 83 Stück am 2026-08-02 zwischen 21:27 und 23:08, weitere 22 am 2026-08-03 in fünf Minuten, 8 Nachbesserungen am 2026-08-07. Der Eintrag hier entstand also am Nachmittag desselben Tages, an dessen Abend der Bot das Gegenteil bewiesen hat — korrigiert wurde er nie. DAMIT IST BEIDES ERLEDIGT- das Kontingent und der Beweisdurchlauf. flow_bot.py läuft, holt und benennt. KORREKTUR AM 2026-08-10 (von Mert): Hier stand "1.000 Credits im Monat". Falsch war nicht nur die Zahl, sondern das Modell- BILDER kosten in Flow gar keine Credits, sie sind nur nach Stueckzahl je Tag gedeckelt. Credits (200 im Monat plus 50 am Tag) gehen ausschliesslich für VIDEO drauf, 7 je 4-Sekunden-Clip. Die Bildkosten eines Reels sind damit NULL. Naeheres in der Aufgabe "Flow-Credits im Auge behalten". DIE LEHRE, DIE BLEIBT- zwei Stück. Erstens- ein beobachtetes Limit ist nicht dasselbe wie das Limit des Werkzeugs; hier wurde aus einer Beobachtung am Gratis-Zugang eine Grundsatzentscheidung ("für Long-Form raus"). Zweitens, und das ist die teurere- ein Eintrag, der eine Sackgasse festhaelt, muss zurück auf den Tisch, sobald jemand die Sackgasse durchfaehrt. Sonst planen sechs Tage später alle um ein Hindernis herum, das es nicht mehr gibt. DER URSPRUENGLICHE EINTRAG, zur Nachvollziehbarkeit- Gefunden am 2026-08-02, nachdem der halbe Abend in einen Browser-Roboter für Flow geflossen war (flow_bot.py). Nach den zehn Bildern eines Reels plus einigen Testlaeufen macht Flow dicht bis zum nächsten Tag. WARUM DAS DIE ENTSCHEIDENDE ZAHL IST- die ganze Diskussion drehte sich um Geschwindigkeit (Agent 30 Minuten, Skript 10, OpenArt 2). Ein Tageslimit ist aber eine haertere Grenze als Tempo: für ein Reel mit 10 Bildern reicht ein Tag bequem, für ein Long-Form-Video mit rund 150 Bildern reicht er nicht — egal wie schnell das Skript ist. WAS BLEIBT- flow_bot.py ist fertig gebaut und die Anker sind gelernt; es fehlt nur der Beweisdurchlauf. Für Reels ist der Weg brauchbar und kostet keine Credits. Für Long-Form fuehrt kein Weg an einer Schnittstelle vorbei — entweder OpenArt (6.000 Credits je Video, also zwei im Monat) oder direkt bei Google (3 bis 9 Dollar je Video, parallel, kein Browser). Siehe tasks. WAS DER ABEND SONST GEBRACHT HAT, unabhängig von Flow- die Bestellung läuft jetzt vierfach parallel, es gibt eine Abnahme die laut scheitert, und auftrag.json mit festen Zielnamen. Das gilt für jeden Bildweg.
Jedes Reel endet mit einer Frage — und sie steht IM Bild, nicht als Stempel2026-08-01
Merts Wunsch am 2026-08-01- "ganz am Ende des Videos sollte so eine Frage kommen, das regt nochmal die Spannung auf und lässt den Zuschauer eine Frage zum Denken. Es muss keine Frage direkt an den Zuschauer sein, kann so eine generelle Frage sein." Und praeziser nachgeschoben- "Sprecher plus grosse Frage, vllt die Frage nicht als Caption sondern im KI-Bild drinne, und das Bild zeigt Motive die das gesamte Video nochmal zusammenfassen, wie eine Sammlung der Motive aus dem Video." WAS GEBAUT WURDE- die letzte Szene ist jetzt ein eigener Typ ("schluss: true"). Ihr Bildauftrag sammelt die Hauptmotive der uebrigen Szenen zu einer Collage (hoechstens fünf — mehr wird ein Wimmelbild, und die Schrift geht darin unter) und verlangt die Frage als grosse, lesbare Schrift im Bild. Im Schnitt wird die Szene NICHT geteilt und faehrt ruhig (die Frage will gelesen werden), und sie bekommt KEINEN Textstempel — zweimal dieselbe Frage liest sich als Fehler. DAS RISIKO, ausdruecklich- das ist die unzuverlaessigste Stelle der ganzen Kette. Ein Etikett aus einem Wort ("RADIUM") kommt sauber; ein ganzer Fragesatz kann als Buchstabensalat zurueckkommen, und dann ist das Letzte, was vom Reel haengen bleibt, ein Fehler. Drei Gegenmassnahmen- die Frage darf hoechstens 58 Zeichen haben, sie wird im Auftrag dreimal genannt und als einziger Text im Bild verlangt, und sie MUSS von Hand angesehen werden. Automatisch prüfen lässt sich das nicht. WAS DER BAU ABLEHNT, bevor Credits fliessen- Frage ohne Fragezeichen, Frage länger als 58 Zeichen, Frage die vom gesprochenen Satz abweicht, mehr als ein Schlussbild, Schlussbild das nicht die letzte Szene ist, Schlussbild mit bewegt: true. NOCH NICHT AM ECHTEN BILD GEPRÜFT — kostet 40 Credits. Siehe tasks.
Sinnhaftigkeit wird jetzt geprüft — Bild ans WORT gebunden, nicht an die Position2026-08-01
Merts wichtigster Befund (2026-08-01)- "Es gibt immer noch Stellen die nicht so viel Sinn machen. Bei "kein Zeuge" wird auf den Hubschrauber gezoomt." Dazu die Bitte dahinter- "schau dass so eine Sinnhaftigkeit im ganzen Video gecheckt wird und auch für alle zukuenftigen Videos". ZWEI VERSCHIEDENE FEHLER, die er zusammen genannt hat. (A) BILD SITZT AUF DEM FALSCHEN WORT. Der Hubschrauber IST die Fahndung, nicht "kein Zeuge" — und der Fehler war strukturell, kein Vertipper. Die Einstellungsfolge war rein POSITIONSGEBUNDEN (Eintrag 1 für die erste Einstellung, 2 für die zweite). Wie viele Einstellungen eine Zeile bekommt, hängt aber von ihrer Laenge ab (Fuellschnitte) und lässt sich beim Schreiben nicht sicher vorhersagen. Wer sich verzaehlt, verschiebt die GANZE Folge — und das Video sieht trotzdem normal aus. Genau die Fehlerklasse, gegen die das Projekt sonst ueberall absichert. BEHOBEN- ein Eintrag darf an ein Wort gebunden werden ("ol@Fahndung"). Das Bild sitzt dann auf dem Wort, so wie der Schnitt auch, und die Anzahl der Einstellungen ist egal. Fehlt das Ankerwort in der Zeile, bricht der Bau ab statt still zurueckzufallen. Nachgezaehlt- von 10 Bildszenen des Phantom-Reels waren SECHS geraten, nicht nur die eine, die Mert gesehen hat. (B) DAS BILD TRAEGT DIE INFORMATION NICHT. Merts Beispiel- bei "Biografie: Osteuropaeerin, wohnungslos, seit Jahren auf der Flucht" zeigt das Bild drei LEERE Karteikarten; er haette gern die drei Merkmale darauf. Die Ursache stand in UNSEREM Auftrag, nicht im Modell- er verlangte woertlich "a SINGLE small typewriter caption strip" und "no other labels". Das Modell lieferte also genau das Bestellte. BEHOBEN- "etikett" nimmt jetzt bis zu DREI Beschriftungen, jede einem Element zugeordnet. Hoechstens drei, weil Schrift die unzuverlaessigste Stelle jedes Bildmodells ist. WIRKT ERST AUF NEUE BILDER und kostet je Szene 40 Credits — siehe tasks. DIE ANTWORT AUF "FÜR ALLE ZUKUENFTIGEN VIDEOS"- ein neuer Pruefbogen, "ausschnitt --sinn". Er legt jeden Satz neben genau den Bildausschnitt, der dabei läuft, für das ganze Reel auf EINEM Bogen, und meldet zusätzlich, was sich mechanisch prüfen lässt (Zeile ohne Angabe, geratene Zuordnung, Anker dessen Wort fehlt). Automatisch ENTSCHEIDEN, ob Bild und Satz zusammenpassen, kann kein Programm zuverlaessig — die Entscheidung SICHTBAR machen kann es. Der Bogen ist Pflicht vor jedem Reel und steht so im Skill. DABEI EIN FEHLER IM PRUEFWERKZEUG SELBST GEFUNDEN- die erste Fassung zeigte eine Spalte je SATZ und uebersah damit alle Einstellungen von Zeilen mit nur einem Satz, also genau die Haelfte der Faelle. Ein Pruefbogen, der die Haelfte nicht zeigt, ist schlimmer als keiner — man haelt ihn für vollstaendig.
Dritte Runde: keine schraegen Fahrten, laengere Mindestschnitte, freie Zielpunkte2026-08-01
Merts Urteil zur zweiten Fassung (2026-08-01)- "besser, aber 2 Sachen" — daraus wurden drei Änderungen. (1) "ES SOLLEN KEINE QUER-KAMERAFAHRTEN GEBEN. Nur horizontal, vertikal und Zoom in oder Zoom out." Die Schwenks waren immer schon sauber auf einer Achse — schraeg wurde es bei den NAHEN Einstellungen, und zwar unbeabsichtigt- der Ausschnitt wurde je Bild neu ins Bild hineingeklemmt. Liegt das Ziel nah am Rand, greift diese Klemme, und weil das Fenster beim Zoomen seine Größe ändert, ändert sich auch der geklemmte Wert. Der Mittelpunkt wanderte dadurch während des Zooms — in x und y verschieden stark, also schraeg. JETZT wird EINMAL geklemmt, für das größte Fenster der Fahrt; was dort hineinpasst, passt bei jedem groesseren Zoom erst recht. Der Mittelpunkt steht fest, übrig bleibt ein reiner Zoom. (2) "DIE SCHNITTLAENGE DER KUERZESTEN CLIPS IST ETWAS ZU KURZ." Jetzt ZWEI Schwellen statt einer, weil nicht jeder Schnitt gleich viel wert ist- Fuellschnitte (die nur den Takt halten) mindestens 1,50 s, begruendete Schnitte an Satzgrenzen mindestens 1,30 s. Grund für die Ausnahme- bei einer Aufzaehlung wie "Ein erwuergter Rentner. Ein Einbruch. Ein Motorrad." dauert der mittlere Satz nun einmal nur gut eine Sekunde; ihn auf Fuell-Laenge zu zwingen hiesse, genau den Schnitt wegzulassen, den Mert an dieser Szene gut fand. OFFEN- die kuerzeste Einstellung dort bleibt damit bei rund 1,4 s. Ist ihm das immer noch zu kurz, kostet es den dritten Schnitt — das ist seine Entscheidung, nicht meine. (3) "DER ZOOM IN BEIM GEKLAUTEN MOTORRAD IST ZU WEIT OBEN." Stimmte, und das Sechser-Raster konnte es nicht loesen- die oberen Rasterpunkte schnitten das Motorrad unten ab, die mittleren erwischten das Etikett "TATORTE" am Rand. Beim Sessel dasselbe in der anderen Richtung. Deshalb gibt es jetzt FREIE Zielpunkte im Drehbuch- "x80y38" ist ein Punkt in Prozent, "x80y38z205" zusätzlich mit eigenem Zoom. Das Raster bleibt als Abkuerzung für den Normalfall. Szene 4 läuft jetzt auf x18y36z205 / mm / x80y38z205 — alle drei Motive vollstaendig, kein Etikett angeschnitten. DAZU das Pruefwerkzeug nachgezogen- "ausschnitt --weit-nah" zeigt jetzt die GANZE Folge statt nur der ersten nahen Einstellung. Vorher haette es genau den Fehler nicht gezeigt, um den es hier ging.
Nachgebessert: geschnitten wird nach BEGRUENDUNG, und die Kamera faehrt ueberall gleich schnell2026-08-01
Merts Urteil zum ersten Reel mit dem neuen Schnitt (2026-08-01, drei getrennte Befunde — alle drei zutreffend, alle drei behoben). (1) "DIE KAMERAFUEHRUNGEN SOLLTEN IMMER DIE SELBE GESCHWINDIGKEIT SEIN." Der Fehler war grundsätzlich- je Fahrt stand ein FESTER Zoomwert (Schwenk 1,18), verteilt über die Dauer der Einstellung. Seit die Einstellungen unterschiedlich lang sind (1,2 bis 2,8 s), heißt derselbe Weg in kuerzerer Zeit schlicht- schneller; die kuerzeste Einstellung fuhr mehr als doppelt so schnell wie die laengste. Bei 4,8 s je Bild fiel das nicht auf, mit dem neuen Takt sofort. JETZT ist die Geschwindigkeit die feste Größe (bau.TEMPO = 6 % des Bildes je Sekunde) und der Weg ergibt sich aus der Dauer. Nachgerechnet- 1,2 s / 2,0 s / 2,8 s fahren alle mit exakt 6,00 % je Sekunde. DAZU RUECKGAENGIG- die weiche Beschleunigungskurve vom 2026-07-31 (smoothstep). Die war richtig, SOLANGE eine Einstellung 4,8 s stand: dann gibt es eine lange Mitte mit gleichmaessigem Lauf, und das Anfahren wirkt wie eine echte Kamera mit Masse. Bei 2 Sekunden besteht die Einstellung nur noch aus Anfahren und Bremsen — die Geschwindigkeit ist NIE konstant. Jetzt linear. Den harten Einsatz verdeckt der Schnitt, dafür ist er da. (2) "MANCHE KAMERAFUEHRUNG IST ZU ABGEHACKT, ALSO ZU WENIG FPS." Es lag NICHT an der Bildrate, die war immer 30. Es lag an zoompan- der Filter schneidet sein Fenster auf GANZE Pixel und lieferte direkt in 1080x1920. Eine langsame Fahrt verschiebt sich damit um weniger als einen Pixel je Bild — heraus kommt kein Gleiten, sondern Stehen mit gelegentlichem Ein-Pixel-Sprung, und je LANGSAMER die Fahrt, desto schlimmer. Jetzt rechnet zoompan in doppelter Größe und wird danach heruntergerechnet (bau.PUFFER); aus dem ganzen Sprung wird ein halber, den die Verkleinerung in Zwischentoene aufloest. (3) "DIE AUSWAHL MACHT NICHT OFT SINN ... man merkt es stark bei der Szene: ein erwuergter Rentner, ein Einbruch, ein Motorrad. In dieser Szene sind diese 3 Dinge die genannt werden aufgefuehrt, die Schnitte aber sind komplett Wirrwarr." Der schwerste der drei Befunde, und der Fehler war konzeptionell- die erste Fassung schnitt nach DAUER (alle 2,8 s) und nahm dafür irgendeine Wortgrenze. Ein Schnitt ohne Grund liest sich als Zufall, egal wie gut der Takt stimmt. ZWEI ÄNDERUNGEN. Erstens WO- gesucht wird jetzt nach Begruendung, in dieser Reihenfolge- Satzgrenzen innerhalb der Zeile (steht schon in der Zeichensetzung und kostet nichts), dann das markierte Wort, und erst danach ein Fuellschnitt als Notnagel. "Ein erwuergter Rentner. Ein Einbruch. Ein Motorrad." sind damit automatisch drei Einstellungen. Zweitens WAS- "nah" im Drehbuch ist jetzt eine FOLGE (nah: [ml, mm, or]) statt eines einzelnen Zielpunkts, mit "weit" als gueltigem Eintrag für die Totale. Die Folge wird der Reihe nach abgearbeitet, das Bild folgt also dem Satz. Ein Schnitt an der richtigen Stelle nuetzt nichts, wenn danach das Falsche im Bild ist. GEPRÜFT- 11 Probengruppen (reine Rechnung, kein ffmpeg), darunter Merts Szene- drei Sätze ergeben drei Einstellungen, die Schnitte sitzen auf den Satzanfaengen, und die Ziele stehen in der Reihenfolge Sessel → Fenster → Motorrad.
Vox schneidet INNERHALB der Zeile — der Takt war das Problem, nicht das Bild2026-08-01
Gebaut 2026-08-01. GEMESSEN STATT GEAHNT- am Phantom-Reel stehen 12 Szenen auf 58,6 s, ein Bild also im Schnitt 4,8 Sekunden, die laengste Szene 7,6. Reels dieser Machart schneiden alle 1,5-2,5 s. Dazu Kamerafahrten von 13-17 % über diese ganze Zeit, rund 3 % je Sekunde — unter etwa 5 % je Sekunde liest das Auge eine Fahrt gar nicht als Bewegung. Das ist Merts "zu wenig Bewegung, zu wenig Input fuers Gehirn", und es ist NICHT die Bildqualitaet. WAS JETZT PASSIERT- eine Szene zerfaellt in mehrere EINSTELLUNGEN desselben Bildes- Totale, naeher heran, Totale, geschnitten auf dem *markierten* Wort (dessen Zeit liegt ohnehin auf ~90 ms genau vor und lag ungenutzt herum). Phantom- 25 Einstellungen statt 12, im Mittel 2,3 s. Kostet NICHTS- es kommt kein Material dazu, nur ein anderer Ausschnitt. Neu- formate/vox/einstellung.py (Planung) und formate/vox/ausschnitt.py (ansehen/ausrichten, Pillow, läuft auf dem Pi). DER VERTRAG BLEIBT- eine Skriptzeile ist weiter EINE Szene und EINE Datei. Die offene Aufgabe "Mehr als ein Bild je Skriptzeile" ist damit anders geloest als gedacht- ohne den Vertrag aufzuweichen und ohne Credits. WARUM KEIN ZWEITES BILD BESTELLT WIRD- war mein erster Plan (+200 Credits je Reel). Mert hat den Testauftrag in Google Flow durchlaufen lassen und ihn zerlegt- "es sieht aus wie das Original, das ist im Endeffekt einfach nur ein Ausschnitt vom Original Bild, dafür braucht man keine extra KI-Bildgenerierung". Er hat recht, und mein Test war schlecht gebaut- ich hatte "dieselbe Collage, nur naeher" bestellt, und das IST per Definition ein Ausschnitt. WIE WEIT MAN HINEINGEHT- 1,85. Rechnerisch waere bei 1,42 Schluss (Bilder sind 1536 px breit, Video 1080), aber am echten Bild nachgesehen ist das zu vorsichtig- ein Halbton-Bild besteht aus Druckpunkten, groessere Punkte liest man als groeberes RASTER, nicht als Unschaerfe. Erst ab etwa 2,4 verlieren die Teile ohne Raster ihre Kante. Vergleichsstreifen- uploads/vox_phantom_takt/vorschau/. WOHIN GEZIELT WIRD- neues Feld "nah" im Drehbuch (ol/om/or/ml/mm/mr), das holen.py in den DATEINAMEN zieht — derselbe Grund wie beim Einstiegspunkt der Archiv-Clips- was neben der Datei liegt, kann sich von ihr loesen. EINE FALLE, DIE SOFORT ZUGESCHLAGEN HAT- die nahe Einstellung kann ein Etikett mitten durchschneiden ("KEINE AUSSAG"). Das liest sich als Fehler, nicht als Bildausschnitt. Deshalb wird der Zielpunkt je Szene von Hand gesetzt und mit ausschnitt --weit-nah abgenommen, statt sich auf die Mitte zu verlassen. WAS NICHT GETEILT WIRD- Videoclips. Sie bauen über ihre Laufzeit die Collage auf; ein Schnitt hinein zerschlaegt genau das, wofür bezahlt wurde. Eine lange bewegte Szene bleibt damit die einzige Stelle über 3 Sekunden. AUSGEROLLT UND GESCHNITTEN am selben Abend (render:v35, auf Merts Ansage "mach du das" — sonst waere das Ausrollen Sache der Reelforge-Sitzung). Guenstiger Fall- seit dem vorigen Abbild v34 lag ausser dieser Vox-Arbeit nichts Neues vor, es ging also keine fremde Baustelle mit live. Die Pflichtpruefungen liefen sauber durch, der Held-Stil bei 1,2 % Abweichung (Grenze 2 %). ECHTE ZAHL AUS DEM RENDERPROTOKOLL- "28 Einstellungen, im Mittel 2.1s, laengste 6.3s" (meine Schätzung vorher war 25 / 2,3 s). Die laengste ist Szene 10, ein Videoclip — die werden bewusst nie geteilt. WAS NOCH NICHT BEURTEILT IST- ob es im BEWEGTEN Bild traegt. Belegt ist bisher nur, dass der Plan angewandt wurde (Protokoll) und dass die Datei sauber ist (1080x1920, 30 B/s, 58,6 s). Ob der Takt gut aussieht, sagt Mert. Siehe tasks.
Der Textstempel sitzt eine Zeile höher — ein Wert für alle drei Plattformen2026-08-01
Entschieden 2026-08-01 nach Merts Befund am fertigen Reel- "die Captions sind zu niedrig für YouTube, es müsste genau um eine Zeile hoch. Bei 2 Zeilen Caption muss Zeile 2 auf Hoehe von Zeile 1." Der Abstand nach unten geht von 300 auf 402. 102 IST GEMESSEN, NICHT GEGRIFFEN- es ist die Zeilenhoehe der Stempelschrift bei Größe 84 (Bricolage Grotesque Bold, Oberlaenge 79 + Unterlaenge 23). Der Stempel ist unten ausgerichtet und wächst nach oben — eine Zeilenhoehe mehr Abstand verschiebt den ganzen Block um genau eine Zeile, gleich ob er ein- oder zweizeilig ist. KEINE VARIANTEN JE PLATTFORM, ausdrueckliche Ansage von Mert ("bau jetzt nicht 3 verschiedene Varianten für die 3 Social-Media-Plattformen"). Also gilt die strengste Vorgabe für alle- YouTube Shorts blendet unten mehr ein als TikTok (Titel, Kanalzeile, Beschreibung), und unsere Zahl stammte von den engeren Plattformen. Das kostet auf TikTok etwas Bildflaeche und ist der Preis dafür, nur EINE Datei hochzuladen statt drei. NEBENBEI- das ist der UNTERE Rand. Der offene Punkt zum RECHTEN Rand ist eine andere Sache und von Mert abgeraeumt (Karte weiter unten).
Der rechte sichere Rand bleibt — Merts Entscheidung, gegen meinen Einwand
Am 2026-08-01 vorgelegt und von Mert abgeraeumt: "das passt schon". Die Aufgabe ist damit aus der Liste raus. HIER STEHT SIE, damit die nächste Session die Messung nicht noch einmal erhebt und die Frage nicht noch einmal stellt. WAS GEMESSEN WAR (2026-07-31): Der sichere Bereich in caption_engine.py stammt aus INSTAGRAMS Flaechenangaben, Hauptplattform ist aber TikTok. Oben, unten und links ist Instagram strenger, also gedeckt. Nur rechts nicht: TikTok braucht 120-140 px für seine Knopfleiste, gesperrt sind 90. Die breitesten Textzeilen ragen damit rechnerisch 30-50 px unter die Knoepfe. MEIN EINWAND, unveraendert: am gerenderten Video sieht man das NIE, weil dort keine Knoepfe sind — es faellt erst am Handy auf, und dann an jedem bereits hochgeladenen Reel gleichzeitig. WAS DAGEGEN SPRICHT, ihn trotzdem anzufassen: es ist eine Rechnung, keine Messung am echten Geraet, und caption_engine.py benutzen ALLE Formate — eine Änderung dort verschiebt auch Lyrics, Story und Spotify. WIEDER AUFMACHEN, WENN: an einem echten TikTok-Upload sichtbar Text unter der Knopfleiste steht. Dann SICHER_X rechts von 0.917 auf rund 0.885 — und zwar aus der Session "Reelforge", nicht aus einer Format-Session.
ÜBERHOLT — Kritzelebene und wortweiser Text sind verworfen
Am 2026-08-01 gebaut, am selben Tag an einem echten Reel geprüft und von Mert verworfen. Die Karte darunter beschreibt, was gebaut wurde; sie gilt technisch weiter, aber die Sache ist AUS (beide Vorgaben stehen auf False). DER TESTFILM- uploads/vox_phantom_kritzel/REEL.mp4, dasselbe Reel wie uploads/vox_phantom/ mit denselben Bildern und derselben Stimme, nur mit den zwei Neuerungen. Merts Urteil- "komplett tot, die Zeichnung muss wenn schon in der Videogenerierung mit dabei sein, so als würde man wirklich auf das Blatt schreiben". Auf Nachfrage- der wortweise Textstempel hat AUCH nichts gebracht. WARUM ES NICHT GING- Meine erste Erklärung war, dass die Kritzelei am Bildschirm klebt, während die Kamera über das Papier faehrt (vier von sechs sassen auf einem wandernden Bild). Merts Widerspruch- "es lag nicht nur an der Kamerafahrt, man merkt einfach dass es nach dem Video hinzugefuegt wurde". Er hat es gesehen, ich nicht — sein Befund gilt. Der Kern ist damit- eine zweite Ebene bleibt eine zweite Ebene. Ein Strich, der nicht im selben Arbeitsgang wie das Bild entsteht, hat nicht dessen Papierfaser, Druckraster, Licht und Schatten. DER WEG, DER BLIEBE, falls es je wieder aufkommt- NICHT das Videomodell (es kennt die Sprecherspur nicht, lässt sich nicht steuern, und jede Szene mit Strich müsste ein Clip statt eines Standbilds sein). Sondern das BILDmodell- nano-banana-pro kann image2image und kostet dabei genauso viel wie ein neues Bild (40 Credits, am 2026-08-01 direkt bei OpenArt abgefragt). Szenenbild normal erzeugen, dann dasselbe Bild mit "male einen roten Kringel um X, direkt aufs Papier, aendere sonst nichts" zurueckschicken, und im Schnitt auf dem gesprochenen Wort von A auf B umschalten. Rund +200 Credits je Reel (+30 %). Mert hat das gehört und entschieden- "wir lassen es erstmal sein". WAS BLEIBT- die eigentliche Ursache für "zu wenig Bewegung" ist wahrscheinlich, dass EINE SKRIPTZEILE EIN BILD bedeutet. Ein Sechs-Sekunden-Beat ist sechs Sekunden derselbe Bildausschnitt. Das behebt kein Strich. Siehe tasks.
Vox bekommt eine Kritzelebene — und der Text läuft Wort für Wort
ÜBERHOLT durch die Karte darüber — gebaut wurde es so, benutzt wird es nicht. Entschieden 2026-08-01 nach Merts Befund: "unsere vox videos stossen noch nicht genug dopamin aus, es hat zu wenig bewegung, zu wenig input für das gehirn". Er hat recht — ein Beat war sechs Sekunden Standbild mit langsamer Fahrt und einem Textblock, der einmal aufblendet. ZWEI ANTWORTEN, beide kostenlos, weil gezeichnet statt bestellt. (1) WORTWEISER STEMPEL: jedes Wort erscheint auf seinem gesprochenen Wort. Die Wortzeiten lagen seit jeher auf ~90 ms genau vor und wurden nie benutzt. Der Satz steht dabei von Anfang an vollstaendig da und wird nur DURCHSICHTIG geschaltet — sonst ändert sich mit jedem Wort der Zeilenumbruch und der Block zappelt. Regler "wortweise". (2) KRITZELEBENE (formate/vox/kritzel.py + kritzel.json): Pfeile, Kringel, Haken, Kreuze, Klammern, im Skript gesteuert ([kringel@Fehlalarm]) und auf das gesprochene Wort gesetzt. Sie zeichnen sich in 0,34 s auf, halten bis 2,8 s und kochen dabei (alle zwei Zeichenbilder wird der Strich neu ausgewuerfelt). Als PNG-Folge mit 15 B/s über das Bild gelegt — 15, weil nur Teiler von 30 taugen, sonst humpelt der Takt wie beim Stop-Motion-Wert vorher. Regler "kritzel". VERWORFEN: Edge Boil auf dem BILD selbst. Vom Bildmodell kommt ein flaches Pixelbild ohne Ebenen; ein Displacement-Filter lässt nicht die Schnittkante zittern, sondern Textur, Schatten und Staub gleichzeitig — Hitzeflimmern statt Handarbeit. Beim selbst gezeichneten Strich ist Kochen dagegen richtig. NOCH OFFEN: einzelne WOERTER einkringeln. Dafür müsste man wissen, wo libass das Wort hingemalt hat; das ginge nur, wenn der Stempel selbst mit Pillow gesetzt wird (Umbau des Textsystems). Bewusst verschoben, bis Mert die jetzige Stufe beurteilt hat.
Zonen und Masse der Kritzeleien stehen in kritzel.json, nicht im Code
Weil ZWEI Seiten sie lesen: kritzel.py malt das Video damit, ui.jsx zeichnet die Vorschau damit. Zwei Zahlenlisten würden auseinanderlaufen, und man saehe es erst am fertigen Reel — die Lehre aus dem Bewegungssystem der Strichfiguren, wo die Vorschau die Bewegung bis heute nicht zeigt. Nachgeprueft: beide Seiten rechnen für dieselbe Form/Zone identische Bildkoordinaten. WAS DIE VORSCHAU BEWUSST NICHT NACHBAUT: das Zittern und das Kochen. Das waere eine zweite Umsetzung derselben Sache. Die Vorschau beantwortet WO, WIE GROSS und WANN; wie der Strich aussieht, zeigt der Kontaktbogen (python3 -m formate.vox.kritzel --blatt), und der läuft mit Pillow auf dem Pi, faellt also nicht unter die Render-Sperre.
Die Zeichenroutinen sind aus strichfiguren KOPIERT, nicht importiert
Rund neunzig Zeilen (_wackeln, _anteil, _male, _rausch) stehen jetzt zweimal da. Absicht: Vox ist freigeschaltet und läuft bei Mert, Strichfiguren ist eine Baustelle mit einer EIGENEN Sitzung, die dort jederzeit umbenennen darf. Ein laufendes Format darf nicht von einer Baustelle abhaengen. DABEI EIN FEHLER GEFUNDEN, der in der Vorlage noch steckt: die Gesamtlaenge für den Aufbau wurde an den GLATTEN Strichen gemessen, verbraucht aber an den GEWACKELTEN — und die sind länger, weil sie schlaengeln. Das letzte Bauteil wird dadurch nie fertig gezeichnet, auch bei aufbau=1.0. Hier behoben (erst wackeln, dann messen); in formate/strichfiguren/figur.py steht er noch — gehört der Sitzung RF-Strichfiguren, siehe tasks.
Telegram erinnert alle zwei Tage ans Hochladen
Eingerichtet 2026-07-31 auf Merts Wunsch. Der Kanal lebt von Regelmaessigkeit, das Hochladen ist Handarbeit und geht im Alltag unter. WAS LÄUFT: /home/pi/scripts/aktenlage_reminder.py, geweckt von aktenlage-reminder.timer täglich um 18:30. Die Nachricht enthaelt die drei Sachen, die man beim Hochladen falsch machen kann (Datei aus ReelForge statt von TikTok, Beschreibung mit Haken, eigene Frage als Kommentar). WARUM TÄGLICH GEWECKT: systemd kann "jeden zweiten Tag" nicht sauber — "*-*-1/2" zählt Tage im MONAT und schickt am Monatswechsel zwei Tage hintereinander. Also weckt der Timer täglich und das Skript entscheidet anhand des letzten Versands. Schwelle 47 statt 48 Stunden, sonst rutscht jede zweite Erinnerung um einen ganzen Tag. ZUGANG: Bot und Chat-ID aus /home/pi/instapi/.env, kein zweites Geheimnis. NEBENBEFUND FÜR DEN HUB: weekly_report.py und monthly_cost_review.py lesen aus /home/pi/investpi/.env — die Datei gibt es nicht (mehr). Beide brechen still ab und laufen trotzdem weiter im Timer.
Der Kanal heißt Aktenlage — und traegt NUR die Erklaer-Reels
Entschieden von Mert am 2026-07-31, aus vier Vorschlaegen gewählt. NAME: "Aktenlage". Ein Wort, deutsch, nach einmal Hoeren richtig zu schreiben, als Wort selten genug, um auffindbar zu sein. Der Ermittler-Ton passt zum Papiercollage-Look, ohne ihn zu erklären. WARUM NICHT "VOX" IM NAMEN: VOX ist ein deutscher Fernsehsender (RTL-Gruppe), dazu gibt es Vox Media in den USA. "Vox-Stil" bleibt unser INTERNES Wort für die Optik — als Marke waere es im deutschsprachigen Markt eine Markenrechtssache und obendrein verwirrend. EIN KONTO = EIN THEMA. Auf diesen Kanal gehören nur die Erklaer-Reels (Petrow, Radium-Maedchen, Phantom von Heilbronn). NICHT die Lyric-Videos, Spotify-Kacheln oder die Archiv-Doku: der Algorithmus lernt bei jedem Video neu, wem er den Kanal zeigt — mischt man zwei Sorten, lernt er gar nichts und beide laufen schlechter als allein. Die Musik-Sachen haben bei instapi ohnehin eigene Accounts (Mila, Riley). BEKANNTES RISIKO DES NAMENS: Er klingt nach True Crime. Die bisherigen Geschichten sind breiter (Beinahe-Atomkrieg, Arbeitsmedizin, Ermittlungspanne). Gegenmittel: die Kanalbeschreibung weit halten und unter den ersten fünf Videos mindestens eines haben, das kein Verbrechen ist — die ersten Videos setzen die Erwartung, nicht der Name. NOCH OFFEN: ob der Handle @aktenlage auf TikTok, YouTube und Instagram frei ist. Von hier aus nicht seriös pruefbar (beide Plattformen liefern Maschinen nur eine leere Huelle aus) — steht als Aufgabe bei Mert.
Strichfiguren- Objekte. Die Pose zeigt die Haltung, erst das Objekt den Inhalt
Gebaut 2026-08-01 nach Merts Einwand- "was ist aber mit den ganzen zeichnungen oder emoji oder bilder um zu wissen worueber er spricht. das wurde doch bestimmt in den tutorials angegeben". Es stand tatsächlich drin und war uebersehen worden. WAS IN DER MASTERCLASS STEHT- jede Szene besteht aus Charakter PLUS Gegenstaenden. "Erstelle 20 Prompts, die exakt zum Skript passen"; Beispiele- "brennende Schwerter, Einraeder, Erdbeben, und natürlich der Charakter", "Fussspuren hinter ihr, die ein Wort bilden". Ein Bild je rund zehn Woerter Skript. WAS VORHER WAR- jede Szene NUR die Figur auf leerem Papier. Die Pose zeigt eine HALTUNG, nie den INHALT- man sah "jemand denkt nach", nie "jemand denkt an eine Erinnerung". Eine Figur allein auf weissem Grund ist ein Piktogramm, kein Erklaervideo. Genau das war der Rest von Merts "Kinderkacke". JETZT- objekte.json mit 19 Gegenstaenden (Karton, Aktenschrank, Buch, Stift, Bilderrahmen, Staffelei, Uhr, Bett, Tuer, Treppe, Berg, Handy, Blitz, Pfeil, Kreuz, Haken, Sprechblase, Waage, Menge), im Skript über [objekt:name] angefordert. Reine Daten wie posen.json- Linien, Kreise, Boegen in einem 0..1-Kasten. Ein neues Objekt ist ein Eintrag, keine Codeaenderung. ZWEI ENTSCHEIDUNGEN, DIE ERKLÄRUNG BRAUCHEN- (1) "menge" wird aus echten kleinen Strichfiguren gezeichnet statt aus einem Symbol. Eine Menge aus fremden Piktogrammen waere eine andere Bildwelt; dieselbe Figur in klein ist dieselbe Welt. Kostet nichts, weil der Zeichner ohnehin da ist. (2) DIE FIGUR WEICHT AUS. Sie ist rund 1000 px hoch in einem 1080 px breiten Bild — mittig bleibt für nichts daneben Platz. Steht ein Objekt am Boden, rutscht sie auf 30 % der Breite, wie ein Zeichner es taete. Der erste Objekt-Kontaktbogen zeigte genau diesen Fehler- fast jedes Objekt lief aus dem Bild, weil die Groessen an der Figurenhoehe hingen und die fast der Bildbreite entspricht. WAS DER GESPROCHENE TEXT DAZU SAGT- nichts. Objekte sind Anweisungen in eckigen Klammern, der Sprechertext bleibt Wort für Wort derselbe (per diff nachgewiesen). Ein bestehendes Reel lässt sich also bebildern, ohne die Stimme neu zu erzeugen. FEHLER, DEN NUR DIESER DIFF GEFUNDEN HAT- stimmentext() entfernte die neuen Anweisungen nicht, ElevenLabs haette "[objekt:bilderrahmen]" vorgelesen bekommen. Es gibt jetzt EINE Liste aller Bild-Anweisungen und einen Kommentar daneben.
Strichfiguren- Gesicht, Animationsprinzipien, neue Captions (dritter Anlauf)
Umgebaut 2026-07-31 nach Merts Urteil "immernoch Kinderkacke" plus einem Screenshot der fallenden Figur (sah aus wie eine gerissene Marionette) und "die captions sind auch fuerchterlich". Vorher wurde nachrecherchiert- Alan Beckers Anleitung zur Strichfiguren-Animation, eine Stick-Figure-Masterclass, die Caption-Praxis für Kurzvideos. 1. DIE ANIMATIONSPRINZIPIEN HABEN GEFEHLT. Becker woertlich- "bei JEDER grossen Bewegung muss es vorher eine Bewegung in die Gegenrichtung geben" (Ausholen) und "wenn eine grosse Bewegung stoppt, geht man ein Stück über den Endpunkt hinaus und dann zurück" (Ueberschwingen). Die Uebergaenge blendeten geradlinig weich von Pose zu Pose — genau daran erkennt man "geschoben" statt "gezeichnet". Jetzt- Ausholen (-12 %), Ueberschwingen (+10 %, gedaempfte Feder), Nachziehen (Huefte fuehrt, Kopf folgt, Arme kommen zuletzt an) und ein winziger Nicker auf jedes Text-Haeppchen (sekundaere Aktion). Alles skaliert mit der Größe des Posenwechsels- bei fünf Grad waere Ausholen ein Zucken. 2. DIE FIGUR HAT EIN GESICHT UND EIN ERKENNUNGSZEICHEN. Augen, Brauen, Mund; acht Mimiken in posen.json, jede Pose bringt ihre passende mit, [gesicht:froh] ueberschreibt sie, und die Mimik blendet beim Szenenwechsel mit. DIE BRAUEN tragen den Ausdruck, nicht der Mund. Dazu eine Haarlocke als Alleinstellungsmerkmal- EIN Strich, aber er macht aus "irgendein Strichmaennchen" DIESE Figur. Beides von Mert angestossen und von der Recherche gedeckt- die Masterclass nennt die Fehlpaarung von Mimik und Aussage als Hauptkritikpunkt an KI-Figuren und Eigenstaendigkeit als den Unterschied zwischen wachsenden und untergehenden Kanaelen. 3. CAPTIONS KOMPLETT NEU. Vorher- ganzer Satz klein am unteren Rand PLUS ein grosses Wort oben, das erst erschien, wenn es gesprochen wurde. Bei einem Satz mit Schluesselwort am Ende stand die halbe Bildflaeche sekundenlang leer, und der Satz war zu klein zum Lesen. Jetzt EIN System- Haeppchen von hoechstens drei Woertern, 94 px fett, im unteren Mitteldrittel, auf die Wortzeiten gesetzt, ein Wort je Satz farbig, heller Rand für Lesbarkeit über der Figur. 39 Einblendungen statt 13 statischer Sätze. DER FEHLER HINTER ALLEN DREIEN, und das ist die eigentliche Lehre- EINE Rechercheaussage ("es wirkt, wenn es ruhig und stetig ist") wurde zur Bauregel, und daraus folgte "die Figur steht still, braucht kein Gesicht, braucht keine grossen Captions". Die zweite Quelle sagt das Gegenteil- "Diese Videos haben deutlich mehr Bewegung, als man denkt. Wer statische Videos mit stillstehenden Strichmaennchen macht, schiesst sich selbst ins Knie." EINE Quelle ist keine Recherche.
Zwischenschritt- Strichfiguren bewegen sich überhaupt
Umgebaut 2026-07-31 nach Merts Urteil zum ersten Reel- "das ist keine Animation, das sind 3 Zeichnungen ganz billig hintereinander gereiht". Er hatte recht- je Szene wurde EIN Bild gezeichnet und bis zu sieben Sekunden gehalten. DER DENKFEHLER, und er ist lehrreich für jede kuenftige Reel-Art- Aus einem Rechercheergebnis ("es wirkt, wenn es ruhig und stetig ist") wurde eine Bauregel ("die Figur steht still"). Gemeint war- keine hektischen Effekte, keine harten Schnitte. NICHT- ein eingefrorenes Standbild. Eine Figur, die sich gar nicht bewegt, ist kein ruhiges Video, sondern ein Foto. Eine Rechercheaussage ist eine Richtung, keine Bauvorschrift — wer sie woertlich in Code giesst, kommt am anderen Ende heraus. WAS JETZT LÄUFT (bewegung.py, drei Ebenen)- LEERLAUF, jede Pose atmet und verlagert das Gewicht um wenige Grad; ZYKLUS, gehen/rennen/winken haben echte Schluesselbilder in posen.json und laufen sie in der Schleife; UEBERGANG, zwischen zwei Szenen wird ueberblendet statt geschnitten — der größte Einzelgewinn, weil aus dreizehn Bildern EINE Figur wird, die dreizehn Dinge tut. Dazu kocht der Strich (alle zwei Bilder neu ausgewuerfelt), wie jedes Blatt einer echten Zeichentrickfolge. 12 ZEICHENBILDER JE SEKUNDE, nicht 30- gezeichnete Animation läuft klassisch "auf Zweier". 30 saubere Zwischenschritte sehen nach Rechner aus, 12 nach Hand. Das Video bleibt 30 Bilder/s. MÖGLICH IST DAS NUR, weil eine Pose aus WINKELN besteht statt aus einem Bild — zwischen zwei Bildern kann man nicht ueberblenden, zwischen zwei Winkeln schon. Die Funktion dafür (figur.mischen) gab es von Anfang an und wurde bewusst nicht benutzt. WAS ES KOSTET- 623 statt 169 Bilder je Reel. Aufgefangen durch zwei Messungen- nur noch den Ausschnitt zeichnen, in dem die Figur steht (212 -> 30 ms je Bild), und PNG mit compress_level=1 (653 -> 154 ms, verlustfrei). Die Renderzeit blieb bei rund drei Minuten. BELEG, dass sich wirklich etwas bewegt- dieselbe Laenge und dieselben Encoder-Einstellungen liefern jetzt 711 statt 468 kbit/s. NEUES WERKZEUG- "blatt.py --bewegung gehen" bzw. "--bewegung zeigen,fallen" zeigt Zyklus oder Uebergang als Bildstreifen. Vorher sah man Bewegung erst nach einem Cloud-Lauf. Es fand sofort einen Fehler- die Arme schwangen quer über den Koerper (ein Seitenansichts-Gang an einer von VORN gezeichneten Figur) und sahen aus wie verschraenkt.
Ein fertiges Reel ist kein Beweis — das erste Strichfiguren-Video war komplett falsch
Passiert 2026-07-31 beim ersten erfolgreichen Cloud-Lauf der Strichfiguren. WAS HERAUSKAM: ein vollstaendiges, abspielbares Reel. 1080x1920, 52 Sekunden, 30 Bilder/s, sauberer Ton, richtige Schrift, Text an der richtigen Stelle. Nichts war rot, keine Fehlermeldung, keine Warnung. WAS FALSCH WAR: Keine einzige der dreizehn Posen aus dem Skript war angekommen. Alle dreizehn hatte die Maschine geraten, alle Requisiten waren verloren. Das Reel zeigte die richtigen Sätze zu zufaelligen Figuren. URSACHE: render() wertete das Skript aus und gab dann das BEREINIGTE Skript an die Planung weiter, die es ein zweites Mal auswertete. Im bereinigten Text stehen keine eckigen Klammern mehr — die zweite Auswertung griff zwangslaeufig ins Leere. WIE ES AUFFIEL, und das ist der eigentliche Punkt: an EINEM fehlenden Fragezeichen im Vorschaubild. Nicht an einer Fehlermeldung. Der einzige Grund, warum es sich danach in zwei Minuten belegen liess statt in zwei Stunden, war die Protokollzeile "13 von 13 Posen GERATEN", die der Rueckfall über sich selbst schreibt. DREI LEHREN, die für jedes Format hier gelten: (1) Ein Rueckfall MUSS sich selbst melden. Die Regel stand vorher schon in CLAUDE.md; hier hat sie zum ersten Mal nachweislich einen Fehler gerettet, der sonst durchgegangen waere. (2) Nach einem Cloud-Lauf gehört das PROTOKOLL gelesen, nicht nur die Datei geprüft. Die Datei war in jeder messbaren Hinsicht in Ordnung. (3) Behoben wurde nicht der Fehler, sondern seine Möglichkeit: plane() nimmt jetzt die fertigen Beats statt eines Textes. Es gibt keine zweite Auswertung mehr, die falsch liegen könnte.
Strichfiguren: die Figur ist DATEN, nicht Bild — und sie steht still
Gebaut 2026-07-31 als Geruest der Reel-Art (available noch False). ENTSCHEIDUNG 1 — POSEN ALS ZAHLEN IN EINER JSON-DATEI. Eine Figur ist ein Satz Gelenkwinkel in posen.json, sonst nichts. Daraus zeichnet Python das Video (Pillow) UND der Browser die Vorschau (SVG). Zwei Zeichner, EINE Wahrheit: waeren die Posen doppelt hinterlegt, würde die Vorschau irgendwann etwas anderes zeigen als das fertige Reel — und das faellt erst am fertigen Video auf. Nebenwirkung, die den Ausschlag gab: eine neue Pose ist ein JSON-Eintrag, keine Codeaenderung. Spiegeln kostet nichts (alle Winkel negieren), die Bibliothek ist also doppelt so gross wie sie aussieht. ENTSCHEIDUNG 2 — DIE FIGUR BEWEGT SICH NICHT. Sie wird in rund einer halben Sekunde GEZEICHNET (man sieht den Strich entstehen) und steht danach still. Das ist keine Sparmassnahme, sondern das Format: in der Recherche ist genau dafür ein Kanal zerlegt worden — "constant motion effects, und das geht gegen das, was dieses Format ausmacht; es wirkt, wenn es ruhig und stetig ist". Nebenbei kostet es statt 1200 Einzelbildern je Reel rund 15 je Szene. figur.mischen() könnte Posen ueberblenden und wird bewusst nicht benutzt. ENTSCHEIDUNG 3 — DAS GROSSE WORT IST EINE TEXTEBENE, KEIN GEMALTES BILD. Ins Szenenbild gemalt müsste es mit dem Szenenanfang erscheinen, also bis zu zwei Sekunden zu frueh. Als ASS-Ebene sitzt es auf der Millisekunde des gesprochenen Wortes. GENAU DARIN LIEGT DER VORSPRUNG: Beide ausgewerteten Tutorials bestehen im Kern aus derselben Handarbeit — Tonspur ins Schnittprogramm, an jedem Satz zerschneiden, Figur draufziehen, Zeitpunkt von Hand nachschieben. Eine halbe Stunde je Reel. ReelForge kennt die Wortzeiten auf rund 90 ms. ENTSCHEIDUNG 4 — REGIE STEHT IM SKRIPT, NICHT IN EINER ZWEITEN DATEI. "[pose:nachdenken] Satz." Bei Vox beschreibt drehbuch.yaml Bildmotive für ein Bildmodell, da geht kein Weg dran vorbei; hier ist die Bildinformation EIN Wort. Zwei Dateien, die zeilenweise zueinander passen müssen, laufen auseinander, sobald jemand einen Satz einfuegt — und dann sind ALLE Posen um eins verschoben, was man dem Video nicht ansieht. Ohne Angabe wird die Pose aus dem Satz geraten, das steht aber im Protokoll ("3 von 5 Posen GERATEN") — kein stiller Rueckfall. WAS DAS FORMAT KOSTET: nichts ausser der Sprecherstimme. Keine Bilder, kein Filmmaterial, keine Lizenzfragen — und es traegt genau die abstrakten Themen (Psychologie, Philosophie), an denen die Archiv-Doku ausfaellt. ZWEI FEHLER, DIE BEIM BAUEN ZUGESCHLAGEN HABEN und als Warnung hier stehen: (1) Bei der Ueberabtastung wurde die Position mit dem Faktor multipliziert, die Figurenhoehe aber nicht — Ergebnis war eine halb so grosse Figur an der richtigen Stelle, die sauber gezeichnet aussah. Man sucht den Fehler danach im Layout. (2) Das Zittern des Strichs wurde je Teilstueck neu gewuerfelt statt als weiche Welle über den ganzen Strich — das sieht nicht handgezeichnet aus, sondern klumpig. Beides faellt nur am Bild auf, nirgends wird etwas rot. Deshalb: nach JEDER Änderung an posen.json oder figur.py "python3 -m formate.strichfiguren.blatt" laufen lassen und hinschauen.
Archiv-Doku auf Eis — Mert: 'null catchy'. Nächstes ist der Strichfiguren-Erklaerer
Entschieden von Mert am 2026-07-31, nachdem er die Vorschau des ersten Reels (Hundekopf-Wiederbelebung, Sowjetunion 1940) gesehen hat: "tote idee, das ist null catchy". Die Kette ist technisch VOLLSTAENDIG und funktioniert — sie wird nicht gelöscht, sondern liegt still (available: False). Gebaut wird stattdessen die vorgemerkte Idee 2, der Strichfiguren-Erklaerer. EINWAND, DER GESAGT WURDE UND NICHTS GEÄNDERT HAT: Beurteilt wurde aus einer Vorschau, die ausdruecklich KEINE Bewegung zeigt (Standbilder statt Filmausschnitte, kleine Vorschaubilder statt 640x480). Bei "catchy" traegt Bewegung viel. Merts Entscheidung steht trotzdem — und die Gegenthese ist plausibel: koerniges Wochenschaumaterial gegen saubere Animation ist auf TikTok ein harter Kampf. WAS AUS DIESER ARBEIT BLEIBT UND WEITER BENUTZT WIRD: (1) vox.schnitt/vox.bau können Clips an beliebiger Stelle einsteigen lassen ("03_ab12_name.mp4") — rueckwaertskompatibel, Vox unveraendert. (2) vox.wolke kann beliebige Formate in der Cloud rendern (art=, zusatz=) statt nur "vox". Jede kuenftige Reel-Art braucht das. (3) archivdoku/vorschau.py — Reel im Browser ansehen VOR dem Schnitt, mit echtem Ton. Das ist formatunabhaengig gedacht und sollte für die Strichfiguren mitbenutzt werden; es beantwortet genau die Frage, an der die Archiv-Doku gescheitert ist, und zwar BEVOR Rechenzeit und Kontingent draufgehen. (4) Die Erkenntnis, dass ein neues Format einmalig ein Render-Ausrollen braucht. LEHRE FUERS NÄCHSTE FORMAT: Die Vorschau haette früher kommen müssen — vor dem Skript, nicht nach Material, Sprecher und Musik. Ein Format, das nicht traegt, faellt so nach zehn Minuten auf statt nach einem halben Tag.
Archiv-Doku: der Einstiegspunkt steht im DATEINAMEN, und ohne ihn wird abgebrochen
Gebaut 2026-07-31, letztes fehlendes Glied der Kette (holen.py). DAS PROBLEM, gemessen im ersten vollstaendigen Durchlauf: Von fünf gewaehlten Treffern war einer eine 45-Sekunden-Wochenschau — die anderen vier liefen 13, 14, 19 und 45 MINUTEN. Für Szenen von je fünf Sekunden. Lange Archivrollen beginnen mit Verleihlogo, Vorspann und Titel; der Cutter nahm bisher immer den ANFANG eines Clips (richtig für Vox, wo ein bestelltes 3-Sekunden-Clip ab der ersten Sekunde das ist, was man wollte). Drei der fünf Szenen haetten damit SCHRIFT statt Motiv gezeigt — und das Reel waere fertig und abspielbar gewesen. LÖSUNG 1 — der Einstieg steht im Dateinamen: "03_ab12_rakete.mp4" heißt ab Sekunde 12. Nicht in einer beigelegten Datei, weil der ganze Vertrag zwischen Format und Schnitt "ein Ordner, je Szene eine Datei, alphabetisch" ist und der Ordner als Ganzes in die Cloud geht: was NEBEN der Datei liegt, kann sich von ihr loesen, im Namen kann es das nicht — und man sieht es beim Blick in den Ordner. Gelesen wird es von vox.schnitt.AB_MUSTER, umgesetzt in vox.bau (ffmpeg -ss VOR -i, also schnelles Suchen über den Index statt Durchdekodieren). Ohne "_abN_" ist der Wert 0 — Vox merkt von der Änderung nichts. LÖSUNG 2 — harter Abbruch statt Warnung: Nimmt eine Szene einen Film über drei Minuten ab Sekunde 0, bricht holen.py ab und nennt die betroffenen Szenen mit Laufzeit. Eine Warnung waere hier zu wenig gewesen: der Fehler erzeugt kein Fehlerbild, sondern ein fertiges Reel mit Filmtiteln drin. Uebersteuerbar mit --vorspann-egal, falls der Anfang ausnahmsweise stimmt. NICHT GEBAUT und bewusst so: kein eigener Zuschneide-Schritt. Der Schnitt in der Cloud kuerzt jede Szene ohnehin auf ihre Laenge und muss nur wissen, wo er einsteigt. Zuschneiden auf dem Pi waere außerdem Rendern und damit gesperrt. OFFEN: 507 MB für fünf Szenen. Kurze Beitraege (Wochenschauen) sind das passendere Material, aber die Laufzeit steht NICHT im Suchindex des Archivs (geprüft: das Feld runtime ist leer) — sie im Kontaktbogen anzuzeigen kostet eine Extra-Anfrage je Vorschlag, also rund 60 je Reel. Bis dahin faellt die Laufzeit erst beim Holen auf.
Zielplattform: TikTok zuerst, YouTube und Instagram nebenher
Entschieden von Mert am 2026-07-31: Die Reels gehen hauptsaechlich auf TikTok, werden aber auch auf YouTube und Instagram gestellt ("kostet ja nichts"). Das stimmt fuers Hochladen — dieselbe Datei, 1080x1920 passt ueberall. Drei Folgen sind aber NICHT gratis und deshalb hier festgehalten: (1) DER SICHERE BEREICH IST AUF DIE FALSCHE PLATTFORM EINGESTELLT. Die Werte in caption_engine.py stammen aus Instagrams Angaben. Oben, unten und links ist Instagram strenger als TikTok, das deckt also mit ab — aber rechts verlangt TikTok 120-140 px für seine Knopfleiste, gesperrt sind nur 90. Die breitesten Zeilen ragen darunter. Als Aufgabe erfasst, Änderung gehört der Session "Reelforge" (gemeinsame Datei). (2) NIE DAS FERTIGE VIDEO VON TIKTOK HERUNTERLADEN und woanders hochladen — das traegt TikToks Wasserzeichen, und die anderen Plattformen druecken solche Videos in der Reichweite. Immer die Originaldatei aus ReelForge auf jede Plattform einzeln laden. (3) DAS HOCHLADEN NACH TIKTOK IST HANDARBEIT. instapi kann TikTok bisher nur für Interaktion (tiktok_engage.py), nicht zum Veroeffentlichen. Bei einer Reel-Art, die auf taegliches Posten ausgelegt ist, wird das der Engpass — nicht das Bauen.
Herkunftsnachweis je Reel — Nennungspflicht und Schutz vor falschen Anspruechen
Eingebaut 2026-07-31 mit der Archiv-Doku (quellen.herkunft_schreiben). Beim Zusammenstellen des Szenen-Ordners entsteht eine HERKUNFT.txt samt herkunft.json: je Szene Quelle, Titel, Lizenz, Urheber, Fundstelle — und daraus erzeugt ein Block mit den Nennungen, die in die Videobeschreibung gehören. ZWEI GRUENDE, BEIDE KONKRET: Erstens verlangen die CC-Lizenzen mit "BY" (die Mehrheit bei Openverse, auch die NASA-Fotos) die Nennung von Urheber und Lizenz. Kostenlos heißt nicht bedingungslos — ein Reel ohne Nennung verstoesst gegen die Lizenz. Zweitens melden sich auf YouTube für gemeinfreies Archivmaterial regelmäßig angebliche Rechteinhaber, meist weil jemand eine restaurierte Fassung angemeldet hat. Gemeinfreies Material ist als Grundlage solcher Ansprueche ausdruecklich nicht zugelassen, ein Widerspruch hat also gute Aussichten — aber nur, wenn man binnen Frist belegen kann, woher der Ausschnitt stammt. WARUM BEIM ZUSAMMENSTELLEN UND NICHT BEIM RENDERN: sonst fehlt der Nachweis für jedes Reel, das nie fertig wurde — und die Quelle hinterher zu rekonstruieren ist genau die Arbeit, die dann niemand mehr leistet.
Nächste Reel-Art: Archiv-Doku — echtes Filmmaterial statt KI-Bildern
Entschieden 2026-07-31 nach einer YouTube-Recherche zu automatisierbaren Reel-Arten ohne KI-Bilder (Merts Vorgabe). Statt zwoelf Bilder bei OpenArt zu bestellen, holt die Kette je Szene echtes, frei lizenziertes Material: Internet Archive (gemeinfreie Filmrollen), Openverse (800 Mio. CC-Werke, API OHNE Schluessel), Pexels optional. Beide Hauptquellen am 2026-07-31 live gegen die echte API geprüft, nicht nur behauptet. WARUM DIESE ART ZUERST: Der Vox-Schnitt ist materialunabhaengig — sein ganzer Vertrag ist "ein clips/-Ordner, je Szene eine Datei, alphabetisch sortiert" (schnitt.py). Bilder bekommen eine Kamerafahrt, Videos laufen wie sie sind. Es braucht also KEINEN neuen Renderer, nur einen neuen Weg, diesen Ordner zu fuellen. Damit sind rund 80 % der Kette (Skript, Sprecher, Wortzuordnung, Schnitt, Textstempel) schon gebaut. DER HEBEL: Kosten je Reel fallen von rund 560 Credits auf nahe null. Der teuerste Posten der Vox-Kette ist Bewegung (2 Clips a 42 Credits) — echtes Filmmaterial bringt seine Bewegung mit. Das trifft zugleich einen gemessenen Formatfaktor: in der ausgewerteten Stichprobe viraler Shorts bewegt sich fast immer schon in der ersten Millisekunde etwas, ein Standbild am Anfang ist die Ausnahme. EHRLICHE GRENZE, DAMIT SIE NIEMAND SPÄTER ENTDECKT: Die automatische Suche liefert oft Unbrauchbares. Das wird bewusst ein HALB automatisches Format — die Maschine schlaegt je Szene mehrere Clips vor, ein Mensch wählt. Und es traegt nur Themen, die jemand gefilmt hat (Raumfahrt, Krieg, Industrie, Zeitgeschichte); für Psychologie oder Philosophie faellt es aus — dafür ist die Strichfiguren-Idee gedacht. VERWORFEN wurde das meistempfohlene Format aus der Recherche (Reddit-Geschichte oben, Gameplay unten): Das Material ist fremdes Eigentum, und die Auswertung dazu misst bei geteiltem Bild 30 % schlechteres Behalten — für ein Erklaer-Format der Tod der eigentlichen Wirkung.
Vorgemerkte Reel-Art: Strichfiguren-Erklaerer, vom Rechner gezeichnet
Festgehalten 2026-07-31 als die eigentlich neue Produkt-Idee — noch NICHT gebaut, bewusst hinter der Archiv-Doku eingereiht (Merts Entscheidung: erst der schnelle Hebel, dann das eigene Produkt). WAS ES IST: Der minimalistische Erklaer-Stil mit Strichfiguren, wie ihn Kanaele im Psychologie-/Philosophie-Bereich fahren. Belegt, nicht geraten: ein Kanal in diesem Stil kam laut Recherche in 90 Tagen auf 20 Mio. Aufrufe, in der Nische liegen einzelne Videos über 1 Mio., Publikum vor allem 18-24. WARUM ES FUNKTIONIERT (die Begruendung ist der Grund, nicht die Zahl): Strichfiguren senken die Denklast. Kein Gesicht lenkt ab, der Zuschauer hängt am Inhalt statt an der Person. WARUM ES ZU REELFORGE PASST WIE NICHTS SONST: Solche Figuren werden GEZEICHNET, nicht generiert — also von Code. Das ist genau die Staerke, die hier schon steht (die Caption-Engine setzt Text auf ~90 ms genau auf den Ton). Keine Materialkosten, keine Rechtefragen, jedes Thema möglich — auch abstrakte, an denen die Archiv-Doku scheitert. EHRLICHE KOSTEN: der teuerste Bau der drei geprueften Ideen. Eine Figurenbibliothek und ein Bewegungssystem sind grob zwei bis drei Wochen, nicht zwei Tage. Genau deshalb steht die Archiv-Doku davor. DRITTE, NICHT GEWAEHLTE IDEE (der Vollstaendigkeit halber): Rangliste/Zahlen-Reel, reine Typografie, vollautomatisch, null laufende Kosten, starker Kommentar-Treiber — aber Massenware, in einer Woche nachgebaut. Als Kommentar-Maschine jederzeit nachrueckbar, als Aushaengeschild ungeeignet.
Reparatur des abgerissenen Schlusses gilt jetzt für ALLE Formate
Hochgezogen 2026-07-31 aus formate/vox/zuordnen.py nach caption_timing. DER FEHLER: Bei laengeren Aufnahmen bricht die Zuordnung vor dem Ende ab. Die betroffenen Woerter verschwinden nicht — sie bekommen alle das Ende der Tonspur mit Laenge null. Gemessen 2026-07-29: die letzten 13 von 76 Woertern bei 37 Sekunden, 9 von 118 bei 55 Sekunden. Das Protokoll meldete trotzdem "76 Woerter zugeordnet". WARUM ER TROTZ LÖSUNG ZWEI TAGE OFFEN WAR: Entdeckt und geloest wurde er dort, wo er auffiel — in Vox. Entstanden ist er in caption_timing, das ALLE Formate benutzen. Damit hatte ein Format ein Pflaster, während lyrics_std, story_scroll und spotify weiterliefen; bei ihnen waeren die letzten Songzeilen still falsch getimt gewesen, ausgerechnet an der Pointe. Die Vox-Session durfte die gemeinsame Datei nicht anfassen (Revier-Regel) und hat es korrekt als Aufgabe gemeldet — die Regel hat also funktioniert, nur hat zwei Tage lang niemand die Aufgabe aufgegriffen. LEHRE FUERS NÄCHSTE MAL: Wird ein Fehler in einem Format behoben, der im KERN entsteht, ist die Arbeit erst halb fertig. Das Pflaster im Format hilft einem, die anderen laufen weiter hinein — und niemand sieht es, weil ihr Protokoll Vollzug meldet. Die Reparatur ist jetzt standardmaessig AN, damit kein Format sie vergessen kann. formate/vox/zuordnen.py bleibt als duenne Huelle (übrig ist dort nur noch die Vox-eigene Entscheidung "keine Stimmentrennung"), damit die uebrigen Vox-Dateien unveraendert weiterlaufen. EINE VERBESSERUNG BEIM HOCHZIEHEN: Das Nachlegen bekommt jetzt DIESELBEN Einstellungen wie der Hauptlauf, also bei Musik MIT Stimmentrennung. Vox reichte sie nicht durch — dort egal (Vox trennt ohnehin nicht), für Musik waere es ein stiller Qualitaetsabfall gewesen: reparierter Schluss auf dem vollen Mix (230 ms Versatz), Rest des Songs auf der isolierten Stimme (90 ms). Die letzten Zeilen waeren sichtbar hinterhergelaufen, ohne dass ein Protokoll etwas gemeldet haette. GEPRÜFT: 13 Logik-Tests (Abriss-Erkennung inkl. Abgrenzung gegen echte Null-Laengen-Woerter mittendrin, Zusammensetzen, Reihenfolge, Durchreichen der Trennung, lauter Fehler bei falscher Wortzahl und bei unreparierbaren Resten). Reine Rechnung ohne Ton — der Volllauf mit echtem Audio steht noch aus, siehe tasks.
Die Caption-Messungen laufen in der Cloud — der Pi rendert nicht mehr mit
Umgestellt 2026-07-31. caption_check.py und caption_vorlage.py rendern mit ffmpeg echte Standbilder und ziehen Minuten CPU. Das faellt unter Merts harte Regel "auf diesem Pi wird nichts gerendert" — trotzdem stand in ausrollen.sh schlicht "python3 caption_check.py". Damit hat JEDES Ausrollen auf dem Pi gerendert, mit demselben Muster, das am 30.07. den harten Reset des ganzen Boards ausgeloest hat. NEU: cloudbuild.prüfen.yaml nimmt das fertige Render-Abbild als Umgebung (ffmpeg, Schriften, Pakete sind darin) und fuehrt darin den frisch hochgeladenen Code aus — also den ABGELEGTEN Stand, nicht den Arbeitsordner, aus demselben Grund wie beim Bauen. Bedient wird es mit "./ausrollen.sh prüfen"; ausrollen.sh ruft es selbst auf. WARUM NICHT EINFACH EINE WARNUNG: Die Regel stand bereits in Fettschrift mit Verbotszeichen in der globalen CLAUDE.md, samt Schilderung des Resets — und wurde trotzdem uebergangen, weil der lokale Aufruf der kuerzeste Weg war. Ein kurzer Weg gewinnt gegen einen Merksatz. Deshalb ist der erlaubte Weg jetzt der bequeme. GRENZE: Die Pakete kommen aus dem Abbild, also aus dem Stand des letzten Unterbaus. Wer requirements.txt anfasst, prüft gegen alte Pakete — erst "unterbau", dann messen. NACHGEZOGEN am selben Tag: Das Cloud-Build-Protokoll verschluckt bei viel Ausgabe Zeilen — ein Lauf zeigte 201 Messzeilen statt 264, und ob wirklich weniger gemessen wurde oder nur weniger ankam, liess sich nicht nachzaehlen. Für einen Pruefstand der schlechteste Zustand: "Alles sauber" ohne belegbaren Umfang; ein Lauf über die halbe Menge saehe genauso aus. caption_check druckt deshalb am Ende EINE Zeile mit der Zahl gemessener Bilder ("Umfang: 264 Bilder gemessen (8 Stile × 7 Sätze × 3 Gruende, 0 davon leer)"). Eine Zeile geht praktisch nie verloren. ZUSÄTZLICH eine technische Sperre, pi-weit: /home/pi/scripts/kein_rendern_auf_dem_pi.sh hängt als Hook in ~/.claude/settings.json und blockiert ffmpeg-Aufrufe, lokale whisper/demucs/moviepy-Laeufe und die heimlich rendernden Pruefskripte — mit Nennung des erlaubten Weges in der Fehlermeldung. ffprobe und "ffmpeg -version" bleiben frei, ebenso das Suchen nach dem Wort in Dateien. 19 Testfaelle, darunter der Aufruf, der am 31.07. durchgerutscht ist ("timeout 900 python3 caption_check.py") — der wurde vom ersten Entwurf der Sperre NICHT erkannt, weil das Praefix "timeout 900" das Muster brach. Der Selbsttest hat es gefunden; ohne ihn waere eine Sperre ausgeliefert worden, die genau den Anlassfall durchlaesst.
Captions gehen weich weg und haben eine Glut
Gebaut 2026-07-31 aus einer YouTube-Recherche zu Caption-Looks. ZWEI Änderungen an caption_engine.py: (1) WEICHES VERSCHWINDEN — bis dahin stand bei jedem Wort nur "end = Gruppenende", libass schaltete es von einem Bild aufs nächste ab. Eingeblendet wurde über eine Kurve, ausgeblendet gar nicht; im Video wirkt das wie ein Schnitt. Jetzt laufen Deckkraft und Unschaerfe gemeinsam hoch. Faellt weg, wenn ein Wort zu kurz vor dem Gruppenende einsetzt — sonst zuckt es, statt zu gehen. (2) GLUT — eine zweite, unscharfe Kopie jedes Wortes auf tieferer Ebene, Kontur in der Textfarbe. ASS kennt kein echtes Leuchten, so macht man es. Beide über die Stil-Sätze abschaltbar (aus_dauer_ms=0 bzw. glut=0 stellt exakt den alten Stand her). FOLGEARBEIT am Pruefstand, sonst haette er Fehlalarm geschlagen: kollisionen() lässt Glut-Stile aus (sie liegen per Bauart genau auf ihrem Wort), und zeitpunkte() fotografiert nicht mehr 80 ms vor Schluss, sondern vor Beginn des Ausblendens — sonst misst man ein halb aufgeloestes Wort und bekommt "Kontrast 0" für ein Bild, an dem nichts falsch ist. Die Dauer wird aus der Engine abgeleitet, nicht doppelt gepflegt. STAND 2026-07-31: abgelegt und DURCHGEMESSEN in der Cloud (Stand 2c8df46, Abbild render:v18) — alle drei Schritte gelaufen, 264 Kaesten gemessen, "Alles sauber", Vorlagen-Abweichung 1,2 % von 2 % erlaubt, also unveraendert gegenueber vorher: der Held-Look ist durch Ausblenden und Glut NICHT abgedriftet. Der erste Messlauf war rot und hat zwei echte Fehler gefunden, siehe den Eintrag "sicherer Bereich stand doppelt". VERWORFEN: ein abdunkelnder Verlauf hinter dem Text (Scrim), wie ihn CapCut-Tutorials zeigen. Der Pruefstand misst Kontrastwerte von 128 bis 241 bei einer Schwelle von 65 — er würde ein Problem loesen, das nicht existiert, und dabei die Messung blenden: eine dunkle Fläche zählt für caption_check als Text und verfaelscht Kasten, Fläche und Lage. Vox hat einen Scrim, weil dort andere Gruende gelten.
Sicherer Bereich nach Instagrams Bedienelementen statt nach Gefuehl
Korrigiert 2026-07-31 in caption_check.py. Vorher ein gleichmaessiger Rahmen (0,055/0,945 und 0,115/0,860); der Kommentar sprach schon von Bedienelementen, die Zahlen taten es nicht. Gefaehrlich war die untere Kante: sie lag bei 1651 px, die Beschreibungs- und Tonzeile von Instagram reicht aber bis 1520 px herauf — Text durfte also 131 px weit in einen Bereich stehen, der im fertigen Reel zugedeckt ist. Am gerenderten Bild sieht man das NICHT, die Bedienelemente kommen erst auf dem Handy dazu. Neu: oben 250 px (Name/Zeit), unten 400 px (Beschreibung, Ton, Fortschritt), rechts 90 px (Knopfleiste); links bleibt reiner Randabstand. ⚠️ HERKUNFT: aus Instagrams Flaechenangaben, NICHT selbst am Geraet nachgemessen — in diesem Projekt der schwaechere Beleg, deshalb steht es im Code ausdruecklich dran. Gegenprobe als Aufgabe für Mert eingetragen. NACHTRAG: Die Werte standen anfangs nur im Pruefstand, während die Engine weiter nach den alten hartkodierten Grenzen setzte — der erste Messlauf hat das prompt aufgedeckt. Sie stehen jetzt in caption_engine.SICHER_X/SICHER_Y und sonst nirgends.
Ausgerollt wird der abgelegte Stand, nicht der Arbeitsordner
Umgestellt 2026-07-30 auf Merts Frage, ob die Reel-Arten nicht unabhängig voneinander ausgerollt werden könnten. ANTWORT ZUERST, weil sie ein Missverstaendnis ausraeumt: Unabhängig gebaut sind das ARBEITEN (eigene Session, eigenes Revier), das TESTEN (vorschau <name> gibt eine eigene Cloud-Adresse, ohne Merts Verkehr umzuleiten) und die SICHTBARKEIT (available=False). NICHT das Ausrollen — es gibt genau EINEN Oberflaechen-Dienst und EINEN Render-Job, beide aus dem gesamten Ordner gebaut. Ein Ausrollen je Reel-Art gibt es nicht und gab es nie; dafür braeuchte es getrennte Dienste je Format, was den Aufwand nicht wert ist. WAS SICH GEÄNDERT HAT: Gebaut wird jetzt aus dem zuletzt abgelegten Stand (git archive HEAD in einen Temp-Ordner), nicht mehr aus dem Arbeitsordner. Vorher lud "gcloud builds submit" den Ordner hoch, wie er gerade dalag — samt halb getippter Dateien anderer Sessions. Das sieht man dem Abbild nicht an und faellt fruehestens im fertigen Video auf. Merts Bedingung war ausdruecklich, dass die laufende Vox-Arbeit dabei nicht gelöscht oder abgebrochen wird; genau deshalb kein stash und kein checkout — der Arbeitsordner wird überhaupt nicht angefasst, nur kopiert wird der abgelegte Stand. Trockenprobe bestaetigt: 115 Dateien im Bau, alle Schriften und Vorschaubilder dabei, Vox-Dateien in der ABGELEGTEN Fassung, und der Arbeitsordner danach unveraendert. NEUE FALLE IN DIE ANDERE RICHTUNG, deshalb mit umgebaut: wer seine EIGENE Arbeit nicht abgelegt hat, rollt aus und wundert sich, dass sich nichts geändert hat. Die Nachfrage vor dem Ausrollen warnt jetzt nicht mehr vor Gefahr, sondern sagt an, WAS ausgeliefert wird (Commit-Nummer und -Titel) und was nicht. NICHT GELOEST: abgelegte, aber unfertige Arbeit einer anderen Session geht weiterhin mit raus. Dagegen hilft nur available=False. Ebenfalls offen: die Pflichtpruefungen laufen weiter im Arbeitsordner, prüfen also unter Umstaenden minimal etwas anderes als das, was ausgeliefert wird. Der Unterschied ist genau die ungespeicherte fremde Arbeit — waere die kaputt, bricht die Prüfung ab und es wird nichts ausgerollt. Also die sichere Richtung, aber bewusst so und hier notiert. NACHTRAG NOCH AM SELBEN TAG, beim ersten echten Ausrollen aufgefallen: der Stand wurde ZWEIMAL gelesen — einmal für die Ansage, einmal beim Bauen. Die Pflichtpruefungen dauern rund drei Minuten, und genau in dieser Zeit hat die Vox-Session etwas abgelegt. Angesagt war 241a4c4, gebaut wurde da9e866. Diesmal harmlos (Vox steht auf available=False), aber die Bauart war falsch: haette der fremde Commit ZWISCHEN den beiden Bauschritten gelegen, waeren Oberflaeche und Render-Abbild aus verschiedenen Staenden gekommen, ohne dass es jemand sieht. Der Stand wird jetzt EINMAL beim Start festgehalten (STAND) und danach nicht mehr neu gelesen.
Zweiter Caption-Look "Plakat" — Blocksatz statt Treppe
Gebaut 2026-07-30 auf Merts Auftrag ("erstelle eine weitere Variante, die sich vom Held abhebt — die mit wichtigste Sache ist die Anordnung und die Abstaende"). Bewusst ein GEGENENTWURF, kein zweiter Anlauf: Held lebt von Rhythmus (zwei Schriften im Wechsel, ein Wort gross, Treppe nach rechts unten, verzahnte Zeilen), Plakat von Ordnung (EINE Schrift, DM Sans fett, zwei saubere senkrechte Kanten). Etwas Halbes dazwischen haette die Auswahl aufgeblaeht, ohne etwas Neues zu können. DIE DREI REGELN, alle drei über Anordnung und Abstaende: (1) JEDE ZEILE FUELLT DIE BLOCKBREITE, und zwar über die SCHRIFTGROESSE — nicht die Größe steht fest und die Zeile wird, wie sie wird, sondern umgekehrt. Drei kurze Woerter ergeben eine grosse Zeile, zwei lange eine kleine. Der Groessenunterschied ist also nicht gesetzt, sondern eine Folge der Sprache. (2) WO DIE GRÖSSE ANSTOESST, ÜBERNEHMEN DIE ABSTAENDE. Ein einzelnes kurzes Wort müsste sonst absurd gross werden; stattdessen greift ein Deckel und der Rest wird mit Buchstabensperrung und Wortluecken aufgefuellt (die klassische Plakat-Technik). Nachgemessen statt angenommen: libass addiert je Buchstabenzwischenraum exakt den \\fsp-Wert (gegengeprueft am gerenderten Bild — 4,00 Zwischenraeume bei einem Wort aus 5 Buchstaben, über drei Sperrwerte stabil). (3) DER ZEILENABSTAND KOMMT AUS DER ECHTEN TINTE: Unterlaenge der oberen plus Oberlaenge der unteren Zeile plus feste Luft. Eine Zeile ohne Unterlaengen rueckt von selbst naeher heran. Nebeneffekt: es KANN nicht zusammenstossen, das Nachschieben wie beim Held entfaellt. BETONT wird über die Anordnung, nicht über einen Groessensprung: das Kernwort steht ALLEIN auf seiner Zeile und fuellt die Breite für sich. ZWEI FUNDE AUS DEM PRÜFEN, beide im ersten Entwurf noch drin: (a) VIER ZEILEN KIPPEN DEN LOOK. Weil jede Zeile die Blockbreite fuellt, werden bei vier Zeilen alle gleich gross — heraus kommt ein Absatz statt einer Aussage ("vielleicht / verschwinde / ich einfach / heute Nacht"). Deshalb MAX_WOERTER ["plakat"] = 5: mit hoechstens fünf Woertern und drei je Zeile sind rechnerisch nie mehr als DREI Zeilen möglich. Das ist eine Zusicherung, keine Faustregel, und caption_check.pruefe_plakat() haelt sie fest. (b) DIE BETONUNG KONNTE SICH UMDREHEN. Ein langes Kernwort muss klein gesetzt werden, um in die Breite zu passen, während eine kurze Nachbarzeile an ihren Deckel stoesst und GRÖSSER herauskommt als das Wort, auf das es ankommt. Im Bild sieht das nicht nach Fehler aus, sondern nach falscher Aussage. Regel jetzt: erst die Kernzeile setzen, alle anderen bekommen deren Größe als Deckel — nichts wird größer als das Kernwort. Der Pruefstand hat die Regel prompt beim Wort genommen und eine Zeile mit 184 gegen 182 gemeldet; Ursache war, dass der Deckel aus einer Probemessung hochgerechnet wurde und Schriften nicht exakt linear skalieren. Wird jetzt nachgefasst, bis er wirklich haelt. BEWEGUNG: Aufziehen (die Woerter wachsen in ihre Größe hinein), NICHT der Schub von oben des Held-Looks — eine Bewegung quer zu den Kanten würde genau das aufweichen, was den Look ausmacht. Gemessen: 94 Prozent zu Beginn, danach exakt dieselbe Endgroesse wie die bewegungslose Variante (326 zu 326 Pixel). OBERFLAECHE: dritter Schalter "Look" (Held/Plakat). Die Auswahl ist datengetrieben, controls.jsx blieb unveraendert — es mussten nur alle acht Kombinationen aus look x bewegung x farbe existieren, sonst haetten sich Schalter nicht bewegt (nachgeprueft: 0 Luecken). Der Schalter "Schreibschrift" heißt jetzt "Farbe" — im Plakat gibt es keine Schreibschrift, dort faerbt er das Kernwort. Zwei neue Vorschaubilder, mit der echten Engine gerendert; je Look ein eigener Vorschausatz, weil ein Sechs-Wort-Satz im Plakat in zwei Gruppen zerfallen waere.
Satzzeichen stehen nicht mehr im Bild — schneiden die Gruppen aber weiter
Geändert 2026-07-30 auf Merts Ansage, samt seiner Warnung dazu ("vergiss aber nicht, dass die Satzzeichen im System eine Hilfe sind, um zu entscheiden, wann eine Zeile endet"). Genau darin liegt die Falle, und es ist eine REIHENFOLGE-Falle: Satzzeichen sind das verlaesslichste Signal für den Schnitt der Caption-Gruppen — verlaesslicher als jede gemessene Gesangspause, weil sie aus dem getippten Text stammen und nicht aus einer Messung. Wer sie einfach beim Einlesen wegwirft, um sie aus dem Bild zu bekommen, nimmt der Gruppierung ihr bestes Werkzeug; dann entscheidet nur noch die Pause, und Sätze laufen wieder ineinander — exakt der von Mert gemeldete Fehler "gon do. Sag mir nicht dass alles okay". Dem einzelnen Standbild sieht man das NICHT an. DESHALB: abgestreift wird ERST in caption_bridge.baue_untertitel, also NACHDEM die Gruppen geschnitten sind (Funktion ohne_satzzeichen, nur an den Wortenden — "dont" und "Nacht-Fahrt" behalten ihr Zeichen, weil es zwischen Buchstaben steht). Faellt ein Wort dadurch ganz weg, faellt seine Zeit mit heraus, sonst bekaeme jedes Wort dahinter den Einsatz seines Nachbarn. Anfang und Standzeit der Caption kommen weiter aus der ungekuerzten Gruppe. ABGESICHERT mit caption_check.pruefe_satzzeichen(), das BEIDE Seiten zugleich prüft: kein Satzzeichen im fertigen ASS, UND ein Punkt trennt weiterhin die Gruppen (geprüft mit gleichmaessigem Takt, also ohne jedes Pausensignal — trennt es dann noch, kann es nur am Satzzeichen liegen). Beide Haelften gegengeprobt: Abstreifen zurueckgebaut → meldet den Punkt im Bild; Satzzeichen-Signal abgeschaltet → meldet die verlorene Trennung. Voller Pruefstand danach ohne Beanstandung, Vorlagen-Abweichung unveraendert 1,0 Prozent. NICHT betroffen ist Vox: das Format stempelt ganze Sätze ins Bild (formate/vox/text.py) und braucht seine Satzzeichen.
Drei Caption-Pruefungen liefen nur zum Schein — jetzt halten sie auch auf
Gefunden und behoben 2026-07-30 bei einer Durchsicht der kompletten Caption-Kette (Auftrag Mert: "schau dir alles Wichtige zu den Captions an"). Der Look selbst war in Ordnung — beide Pruefwerkzeuge melden "alles sauber", die Abweichung von Merts Vorlage liegt bei 1,0 Prozent. Kaputt war die ABSICHERUNG, und zwar dreifach, jedes Mal nach demselben Muster: die Prüfung existiert, aber niemand zieht Konsequenzen. (1) DIE 2-PROZENT-REGEL GAB ES NICHT. CLAUDE.md schreibt vor, dass ein Held-Look über rund 2 Prozent Abweichung als abgedriftet gilt. Im Code stand dazu nichts: caption_vorlage.py druckte die Zahl und endete IMMER mit Erfolg, ausrollen.sh schaute sie nur an. Es haetten 8 Prozent dastehen können und es waere trotzdem ausgerollt worden. Jetzt: SCHWELLE = 0,02 in caption_vorlage.py, Abbruchcode 1 bei Ueberschreitung und ebenso bei abweichender Zeilenzahl; ausrollen.sh bricht darauf ab. Gegengeprobt mit absichtlich verstelltem Zeilenabstand (3,1 Prozent → Abbruch). Nebenbei: der Bericht braucht das Standbild gar nicht (die Vorlage steht als gemessene Zahlenreihe im Code), nur das Vergleichsbild — vorher hiess "kein Standbild" stillschweigend "nicht pruefbar", also faellt die Prüfung im Container nicht mehr aus. (2) EIN STILLER RUECKFALL, DER SICH FÜR LAUT HIELT. Lässt sich eine Zeile nicht entzerren, schiebt caption_engine sie pauschal nach unten und schrieb das in die Liste VERZAHNUNG_NOTAUSGANG — mit dem Kommentar "wird vom Pruefstand ausgelesen". Wurde sie nie, von niemandem, und gedruckt wurde auch nichts. Jetzt: Warnung ins Fehlerprotokoll beim Auftreten UND Beanstandung im Pruefstand (dito für SKALA_FEHLER, die unlesbaren Schrift-Umrechnungen). Gegengeprobt mit unmöglich gemachtem Entzerren — beide Zeilen werden gemeldet, Abbruchcode 1. (3) FÜNF VON SIEBEN TESTSAETZEN LIEFEN NIE. Vorgabe von caption_check.py waren "kurz" und "referenz"; die uebrigen fünf standen zwar in SÄTZE, wurden aber nur von Hand gefahren. Nicht dabei war ausgerechnet "zwei_zeilen" — der Fall, an dem Mert die ineinanderlaufenden Zeilen gefunden hatte. Jetzt laufen alle sieben als Vorgabe: 108 gerenderte Bilder, 2 Minuten 45 statt 33 Sekunden, ohne Beanstandung. MERKSATZ: Eine Prüfung, deren Ergebnis niemand liest, ist Zierde. Wer hier etwas misst, muss es auch durchsetzen — sonst waere der Aufwand besser gar nicht da, denn er taeuscht Sicherheit vor.
Push läuft automatisch — und Ausfaelle sind sichtbar
Ergaenzt 2026-07-30. Die GitHub-Sicherung hing daran, dass jede Session "git push" tippt; bei mehreren parallel arbeitenden Sessions ist "jemand denkt daran" keine Sicherung. githooks/post-commit schiebt jetzt nach jedem Commit im HINTERGRUND hoch (im Vordergrund hängt sonst jeder Commit ein bis zwei Sekunden daran, ohne Netz minutenlang). Eingeschaltet mit "git config core.hooksPath githooks" — der Haken liegt IM Repo, damit alle Sessions denselben haben. Während Rebase/Merge feuert er nicht, sonst würde jeder Zwischenstand einzeln gepusht. GEGEN DEN STILLEN RUECKFALL: Fehlschlaege landen in /tmp/reelforge_push.log, und "./ausrollen.sh stand" zählt die Commits, die nur lokal liegen. Beides gegengeprueft — der automatische Push ist nachweislich gelaufen (Commit 8edd0a4 landete ohne Handarbeit oben), und die Warnung schlaegt nachweislich an. Ein Push, der lautlos ausfaellt, waere schlimmer als keiner: man haelt sich dann für gesichert. Damit ist die Hub-Instanz für diesen Zweck nicht noetig — kein Cron, keine pi-weite Automatik, alles im Repo.
Alte Render-Abbilder gelöscht — 13 von 16 grossen Schichten sind jetzt Muell
Aufgeraeumt 2026-07-30 auf Merts Freigabe. Weil vor der Abbild-Trennung jeder Bau ohne Zwischenspeicher lief, hatte JEDES der 15 Render-Abbilder seinen eigenen kompletten Satz Schichten — 30,5 GB in der Artifact Registry, rund 3 EUR im Monat. Gelöscht: render:v1 bis v12 und render-base:v1. Behalten: render:v13/v14/v15 als Rueckfall und render-base:v2. Vorher geprüft, dass nichts darauf verweist (der Job läuft auf v15, keine Cloud-Run-Fassung nutzt ein render-Abbild). Die ui-Abbilder wurden ABSICHTLICH nicht angefasst: an ihnen haengen die Cloud-Run-Fassungen, und ohne Abbild lässt sich eine Fassung nicht mehr starten — "./ausrollen.sh zurück" waere kaputt. Sie sind außerdem klein. ACHTUNG BEIM MESSEN: die angezeigte Größe bleibt zunächst bei 30,5 GB. Artifact Registry gibt die Schichten erst später frei; von 16 Brocken über 900 MB sind jetzt nur noch 3 in Benutzung, die anderen 13 warten auf Googles Aufraeumlauf. Wer die Ersparnis prüfen will, muss also später nochmal nachsehen.
Sicherung ausserhalb des Pi: privates GitHub-Repo
Eingerichtet 2026-07-30 (github.com/mertoege/reelforge, privat). Vorher lag die komplette Projektgeschichte auf EINER SD-Karte — Syncthing spiegelt nur die Dateien auf Merts PC, nicht die Versionsgeschichte. Ein Kartenschaden haette jede Entscheidung, jeden Zwischenstand und jede Rueckrollmoeglichkeit mitgenommen; das war das größte Einzelrisiko im ganzen Aufbau und hatte mit Hosting nichts zu tun. VOR dem ersten Push geprüft: keine Schluesseldateien getrackt, kein Treffer für Schluesselmuster (sk-ant-, ghp_, AIza…, PRIVATE KEY, AKIA…) in der GESAMTEN Historie, .env nachweislich nicht im Repo. Ab jetzt gehört "git push" zum Ablegen — ein Commit, der nur auf dem Pi liegt, ist keine Sicherung. Bei mehreren Sessions kann ein Push abgelehnt werden (jemand war schneller): dann "git pull --rebase && git push". WICHTIG in der Anleitung festgehalten: klagt der Rebase über ungespeicherte Änderungen, sind das die einer ANDEREN Session — kein stash/checkout/reset, das löscht fremde Arbeit, die es nirgends sonst gibt. NEBENBEI: gh speichert den Zugangsschluessel im Klartext unter ~/.config/gh. Alle Sessions laufen als Nutzer pi und haben damit denselben GitHub-Zugriff — bewusst so, weil jede Session pushen soll.
Nach einer Vorschau hängt der Cloud-Run-Verkehr an einer festen Fassung
Gefunden 2026-07-30 beim Testen der neuen Vorschau-Adressen. "--no-traffic" schaltet den Dienst von "immer die neueste Fassung" auf "feste Fassung" um. Folge: Jedes weitere Ausrollen baut, meldet Erfolg — und Mert sieht trotzdem den alten Stand. Beim Test tatsächlich eingetreten (Verkehr hing auf reelforge-00023, während ui:v24 erzeugt wurde). ABGESICHERT: ausrollen.sh setzt den Verkehr jetzt ausdruecklich auf die neueste Fassung UND liest nach, ob dort auch das erwartete Abbild läuft — verglichen wird die Pruefsumme, nicht der Name (Cloud Run loest "ui:v25" beim Ausrollen zu "ui@sha256:…" auf; ein Namensvergleich meldet immer einen Unterschied, den es nicht gibt). "./ausrollen.sh stand" zeigt an, ob der Verkehr festhaengt, "vorschau-weg <name>" raeumt eine Test-Adresse ab. Gemessen nach dem Umbau: 8:04 → 1:47 für das Render-Abbild (Bauen 255→93 s, Hochladen 229→14 s).
Render-Abbild zweigeteilt — Ausrollen 8 Minuten → 1-2 Minuten
Umgebaut 2026-07-30, nachdem Mert gefragt hat, warum das Ausrollen so lange dauert. GEMESSEN an render:v14: 4:15 Bauen + 3:49 Hochladen = 8 Minuten für zwanzig Zeilen Python. Ursache: Cloud Build startet jeden Bau auf einer frischen Maschine OHNE Zwischenspeicher, und cloudbuild.yaml sagte nirgends "nimm den letzten Stand". Also wurden jedes Mal torch (191 MB) und drei Sprachmodelle (~700 MB) neu geladen und installiert — und weil dadurch jede Schicht einen neuen Fingerabdruck bekam, wurden anschliessend alle 4,9 GB neu hochgeladen, obwohl 99 % davon unveraendert waren. LÖSUNG: Dockerfile.render.base (Unterbau: Pakete + Modelle, ändert sich selten) und Dockerfile.render (duenne Schicht: nur Projektcode). ABSICHERUNG gegen den klassischen stillen Rueckfall dieses Projekts — neuer Code auf altem Unterbau: der Unterbau traegt einen Fingerabdruck aus requirements.txt + Dockerfile.render.base, die duenne Schicht vergleicht ihn beim Bauen und BRICHT AB, wenn er nicht passt. Verworfen: eigener Server statt Cloud Run. Der Render-Dienst läuft nur Minuten am Tag (8 Kerne / 16 GB, ~10 Cent je Video); eine Dauermaschine kostet 15-30 EUR im Monat und müsste von Hand gepflegt werden — genau die Umgebungsdrift, die hier schon dreimal ein schlechteres Video hat durchgehen lassen.
ausrollen.sh: ein Werkzeug, Pflichtpruefungen inklusive, Vorschau-Adressen je Session
Eingefuehrt 2026-07-30. Vorher war Ausrollen eine Handvoll langer gcloud-Zeilen mit von Hand hochgezaehlten Nummern. Jetzt: ./ausrollen.sh alles | vorschau <name> | stand | zurück | unterbau. Die Nummer wird selbst gefunden. VOR jedem Ausrollen laufen zwingend: Formate laden, caption_check.py, caption_vorlage.py, npm run build UND der Selbsttest im echten Browser (headless Chromium, --dump-dom, Suche nach "Alles in Ordnung"). Letzterer ist der wichtigste: ein Fehler in EINER ui.jsx reisst die ganze Seite mit, und "npm run build" merkt das NICHT — der Fehler entsteht erst beim Aufbauen im Browser. Bei mehreren parallel arbeitenden Sessions ist das die einzige Prüfung, die fremde kaputte Arbeit vor dem Ausrollen abfaengt. "vorschau <name>" schaltet einen Stand als getaggte Cloud-Run-Fassung OHNE Verkehr frei — eigene Adresse, gleiches Passwort, Merts Adresse unberuehrt. Damit muss eine Format-Session nicht mehr auf die Reelforge-Session warten, um in der Cloud zu testen.
Kein eigener Git-Zweig je Session — alle teilen sich EINEN Arbeitsordner
Festgehalten 2026-07-30, nachdem genau das als Verbesserung vorgeschlagen wurde und sich beim Nachsehen als schaedlich herausstellte. Ein Git-Zweig hängt am ARBEITSORDNER, nicht an der Session — und alle Sessions arbeiten in /home/pi/reelforge. Ein "git checkout" würde allen anderen mitten in der Arbeit die Dateien austauschen. Alle bleiben auf main. Dass halbfertige Arbeit Mert nicht erreicht, sichern stattdessen drei Dinge: available=False (die Reel-Art taucht in seiner Oberflaeche gar nicht auf), Vorschau-Adressen (Testen ohne Ausrollen) und der Selbsttest als Tuersteher vor dem Ausrollen. Echte Zweige würden pro Session einen eigenen Ordner brauchen (git worktree) — Umbau von tmux-Sessions, Pfaden und Syncthing, nicht nebenbei zu machen.
Schluessel gehoerten nie ins Render-Abbild — .env war mit eingebacken
Gefunden und behoben 2026-07-30 beim Sicherheits-Check vor der GitHub-Sicherung. Die .dockerignore schloss die .env NICHT aus, also hat "COPY . /app" den ElevenLabs-Schluessel in jedes Render-Abbild gebacken (bis einschliesslich render:v14). Benutzt hat ihn dort nie etwas — formate/vox/stimme.py sucht die Datei unter dem festen Pi-Pfad, den es im Container gar nicht gibt. Die Abbilder liegen in einer privaten Artifact Registry, das Risiko war also klein, aber es gehört nicht dorthin. Ab jetzt in .dockerignore ausgeschlossen (zusammen mit .git, .claude und den Entwicklungswerkzeugen). In der Cloud kommen Schluessel aus dem Google-Tresor. Ob der Schluessel getauscht wird, entscheidet Mert.
Caption-Auswahl: ein Vorschaubild und zwei Schalter statt vier Kacheln
Umgebaut 2026-07-29 auf Merts Rueckmeldung ("die Bildvorlage ist noch nicht gut genug"). Nach dem Ausduennen zeigte die Auswahl drei fast identische Kacheln — die Varianten unterscheiden sich nur in Bewegung und Farbe, und Bewegung sieht man im Standbild GAR NICHT. Jetzt: EIN grosses Vorschaubild plus zwei Umschalter (Bewegung: Schub/Ohne · Schreibschrift: Weiss/Sandgelb). Das Bild hängt nur an der Farbe; beim Bewegungs-Schalter steht "im Standbild nicht zu sehen" dabei, statt ein Bild vorzutaeuschen, das nichts zeigt. Damit beide Schalter jede Stellung können, ist die vierte Kombination nachgetragen (held-erscheinen-warm) — ein Schalter mit einer toten Stellung ist kaputt. Welche Eigenschaften als Schalter erscheinen, sagt der Server (caption_bridge.SCHALTER), damit Beschriftung und Verhalten nicht auseinanderlaufen. ZWEITER FEHLER dabei gefunden: das Vorschaubild wurde in der Oberflaeche auf 420x244 beschnitten (object-fit: cover), abgeschnitten wurde ausgerechnet das grosse Schreibschrift-Wort — also genau das, was den Look ausmacht. Den Ausschnitt bestimmt jetzt allein make_caption_previews.py; die Bilder sind doppelt so gross (840 px, scharf auf dem Handy), zeigen sechs Woerter statt vier (damit die Treppe überhaupt sichtbar ist) und haben Vignette plus feines Korn, damit die Kachel nach Videobild aussieht und nicht nach Textfeld.
Nur noch ein Caption-Look: Held. Die anderen vier sind gelöscht
Entschieden 2026-07-29 von Mert ("loesch alle Caption-Stile bis auf den letzten"). Von 14 Eintraegen bleiben 3: Held — Schub, Erscheinen, warm. Gelöscht sind der alte Karaoke-Eintrag "klassisch", Collage, Wort für Wort (3), Mittelweg (3) und Schlicht (3) — und zwar RICHTIG gelöscht, nicht nur aus der Auswahl genommen: caption_engine.py schrumpft von 1722 auf 700 Zeilen (Bewegungskatalog, Rollen, Kaestchen-Anordnung, sechs Kompositionen, drei Stil-Sätze raus), caption_bridge.baue_untertitel hat nur noch einen Zweig, die elf Vorschaubilder unter static/captions sind weg. GRUND fuers echte Löschen statt Ausblenden: unerreichbarer Code ist in genau diesem Projekt schon dreimal die Brutstaette des stillen Rueckfalls gewesen — was niemand mehr auswaehlen kann, prüft auch niemand mehr. RUECKWAERTS: alte Auftraege und alte Browser-Staende tragen die gestrichenen Namen noch. caption_bridge.gueltiger_stil() biegt sie auf held-schub um und SCHREIBT DAS INS RENDERLOG (kein stiller Tausch); die Oberflaeche raeumt einen gespeicherten Stand beim Start auf. Der alte Karaoke-Renderer selbst bleibt im Code und ist über captionStyle="klassisch" weiter erreichbar, nur nicht mehr anklickbar. Wer die geloeschten Stile sucht: Commit ab25ad2 und früher.
Caption-Gruppen folgen Zeilen, nicht der Wortzahl
Behoben 2026-07-29 auf Merts Rueckmeldung (Screenshot: im Bild stand "gon do. Sag mir nicht, dass alles okay" — Ende der einen und Anfang der nächsten Zeile in EINER Gruppe). Ursache: die Gruppierung schnitt stur nach `max_woerter` ab, der Rest einer langen Zeile klebte an der nächsten. Jetzt zwei Durchgaenge: (1) HARTE GRENZEN — Satzende im Songtext oder Gesangspause; hier endet eine Gruppe immer, egal wie kurz sie dadurch wird. (2) Zu lange Gruppen werden an ihrer BESTEN inneren Grenze geteilt (größte Luecke, Satzzeichen-Bonus, Mitte bevorzugt) statt abgeschnitten — aus zehn Woertern werden zweimal fünf, nicht acht plus zwei. Die Pausenschwelle ist nicht mehr fest, sondern wird je Song aus den gemessenen Luecken abgeleitet (Median mal 3 plus 0,12 s, begrenzt auf 0,28 bis 0,55 s) — eine Ballade hat andere Pausen als ein Rap, ein fester Wert passt bei einem von beiden nie. Satzzeichen sind dabei das verlaesslichste Signal, weil sie aus dem getippten Text kommen und nicht aus einer Messung. Neue Dauerpruefung in caption_check.py: keine Gruppe darf über ein Satzende oder eine deutliche Pause hinweglaufen.
Captions: jedes Wort auf seiner eigenen Zeit — und Abschnittsmarken raus
Behoben 2026-07-29 auf Merts Rueckmeldung ("das erste Wort passt, danach kommen ein paar Woerter im Block"). ZWEI Fehler. (1) TIMING: Der Held-Stil setzte ALLE Woerter einer Gruppe auf den Zeitpunkt des ersten Wortes und liess sie nur mit einem festen Versatz von 70 ms je Zeile einlaufen — die auf 90 ms genaue Wortzuordnung war damit wertlos. Dieselbe Sorte Fehler in der Collage (90 ms Aufschlag JE WORT, beim vierten Wort fast eine halbe Sekunde zu spaet) und im Mittelweg (fester Versatz von 0,12 s statt der echten Zeit des Kernworts). Jetzt bekommt jedes Wort seinen eigenen Einsatz aus der Zuordnung. Neue Dauerpruefung in caption_check.py: JEDER Einsatz im fertigen ASS muss auf dem Einsatz eines echten Wortes liegen (60 ms Toleranz) — damit kann sich kein kuenstlicher Versatz mehr einschleichen. (2) ABSCHNITTSMARKEN: "[Hook]", "Verse 2:", "(Chorus)" standen im Video. Sie gingen ungefiltert in die Wortzuordnung — die hat sie im Gesang gesucht, nicht gefunden und dadurch auch die Zeiten der echten Woerter drumherum verzogen. caption_timing.ohne_abschnittsmarken() wirft sie jetzt aus dem Songtext, bevor zugeordnet wird; caption_bridge filtert geklammerte Marken zusätzlich heraus. Bewusst vorsichtig: ein blankes "Bridge" oder "Drop" bleibt stehen, weil es eine echte Songzeile sein kann — gestrichen wird nur, was geklammert ist oder allein auf einer Zeile steht.
Held-Captions bilden Merts Vorlage jetzt nach — messbar, nicht nach Gefuehl
Entschieden 2026-07-29, zweiter Durchgang auf Merts Ansage "so lange arbeiten, bis das Gerenderte exakt wie der Screenshot aussieht". Neues Werkzeug caption_vorlage.py: Die Vorlage (uploads/paste_1785338174295.png) ist Zeile für Zeile ausgemessen; das eigene Ergebnis wird gerendert, ebenfalls vermessen und BEIDES auf denselben Block normiert (Anteile der Blockbreite statt Pixel — die Vorlage ist ein 262x184-Ausschnitt, wir rendern 1080x1920). Verglichen werden Zeilenaufteilung, linke und rechte Kante, Grundlinienabstand und Groessenverhaeltnis. Ausgangswert 21,6 Prozent mittlere Abweichung, jetzt 1,0 Prozent (größte Einzelabweichung 3,5 Prozent). VIER Regeln machen den Unterschied, alle an der Vorlage gemessen: (1) Schriftwechsel je WORT statt je Zeile — Schreibschrift bekommen genau das erste Wort der Gruppe und das Heldenwort, alles andere ist fette Grotesk, auch mitten in derselben Zeile ("And" in Schreibschrift, "if you just do" direkt daneben). (2) Der Groessensprung ist KLEIN: das Heldenwort hat nur die 1,5-fache x-Hoehe, nicht die 2,6-fache wie im ersten Anlauf. Gross wirkt es trotzdem, weil die Schreibschrift lange Ober- und Unterlaengen hat. (3) Die Zeilen ziehen sich nach links ein (erste Zeile volle Blockbreite, jede weitere 10,5 Prozent weiter). (4) Zeilenabstand 1,7 x-Hoehen der neuen Zeile. Block fuellt 78 Prozent der Bildbreite. Woerter je Gruppe von 6 auf 8, weil die Vorlage acht hat.
Schreibschrift Great Vibes ersetzt durch Niconne
Entschieden 2026-07-29. Great Vibes ist NICHT die Schrift aus Merts Vorlage: das Wort "three" hat dort ein Seitenverhaeltnis von 2,45, in Great Vibes 1,56 — sie ist also deutlich schmaler und feiner. Ausgewaehlt wurde aus 14 Schreibschriften von Google Fonts, erst per Formvergleich gegen die Vorlage (Masken uebereinandergelegt), dann per Sichtvergleich des KOMPLETTEN Satzes gerendert. Niconne trifft die Proportionen am besten (2,39 gegen 2,45) und die Form des Schwung-A in "And". Die exakte Schrift der Vorlage ist vermutlich eine CapCut-Hausschrift und nicht frei zu haben. Great Vibes bleibt im Ordner (andere Modi), Niconne ist in Dockerfile.render mit angemeldet und wird dort beim Bauen geprüft. Außerdem: Zahlwoerter bekommen bei der Wahl des Heldenworts einen Zuschlag — in Erklaer-Reels ist die Zahl der Haken ("these THREE things"). Ohne den Zuschlag waere "things" gross gesetzt worden.
Ausgerollt: Oberflaeche ui:v21, Render render:v12 (Gruppen an Zeilengrenzen)
Ausgerollt 2026-07-29. Im ausgerollten Abbild gegengeprueft: Merts Beispielzeile ("like i dont know what this whole pain gon do." + nächste Zeile) wird dort in "like i dont know what" / "this whole pain gon do." / "Sag mir nicht dass alles okay" geteilt — kein Ueberlaufen mehr in die nächste Zeile.
Ausgerollt: Oberflaeche ui:v20, Render render:v11 (Wort-Timing, Marken-Filter)
Ausgerollt 2026-07-29. Im ausgerollten Abbild gegengeprueft (Cloud Build, amd64): Marken-Filter macht aus "[Hook] / Du fehlst mir" ein sauberes "Du fehlst mir", die Timing-Prüfung meldet ok, Pruefstand ohne Beanstandung.
Ausgerollt: Oberflaeche ui:v19, Render render:v10 (Vorlagen-Setzung)
Ausgerollt 2026-07-29. Enthaelt die Held-Setzung nach Merts Vorlage und die Schreibschrift Niconne. Gegengeprueft IM ausgerollten Render-Abbild (Cloud Build, amd64): Vorlagen-Abgleich 1,2 Prozent mittlere Abweichung (auf dem Pi 1,0 — der Unterschied kommt von der Schrift-Rasterung, nicht vom Layout), Pruefstand ohne Beanstandung. Der Schriften-Test in Dockerfile.render bricht ab, falls Niconne im Abbild fehlt — der Bau lief durch, also ist sie da.
Ausgerollt: Oberflaeche ui:v18, Render render:v9 (Captions)
Ausgerollt 2026-07-29. Enthaelt die komplette Caption-Ueberarbeitung (Umrechnung der Schriftgroessen, Zeilensatz im Held-Stil, x-Hoehen statt Punktzahlen, drei behobene Zeitueberlappungen) sowie den bis dahin committeten Vox-Stand (bleibt gesperrt, available=False). Gegengeprueft NICHT nur durch "gebaut, also gut": der Pruefstand caption_check.py lief per Cloud Build IM ausgerollten Render-Abbild auf amd64 — Vorhersage und libass-Ergebnis stimmen dort genauso, keine Beanstandung. Die Vorschaubilder im Oberflaechen-Abbild wurden über ihre Pruefsumme gegen die lokalen Dateien verglichen (identisch).
Captions: libass misst Schriftgroessen anders als das Messwerkzeug — 24 bis 44 Prozent
Gefunden 2026-07-29 (Sitzung Reelforge, Auftrag Mert "Captions müssen perfekt werden"). Die Caption-Engine misst Woerter mit PIL und lässt libass malen. Beide meinen mit "Schriftgroesse 100" etwas ANDERES: PIL das Geviert der Schrift, libass die Summe aus Ober- und Unterlaenge der OS/2-Tabelle (VSFilter-Erbe). Bei DM Sans sind das 1,32 Geviert, bei Great Vibes 1,75 — dieselbe Zahl ergibt also in Schreibschrift eine 24 Prozent kleinere Schrift als in der Grotesk. Folge: JEDE Messung dieser Engine war je nach Schrift 24 bis 44 Prozent zu gross, und beim MISCHEN zweier Schriften stimmte das Groessenverhaeltnis um ein Drittel nicht — genau der Effekt ("der grosse Held sieht aus wie die kleinen Woerter"), gegen den hier zuvor mit Faustkorrekturen angebaut wurde. Behoben mit _ass_skala() in caption_engine.py: Geviert geteilt durch (usWinAscent + usWinDescent), direkt aus der Schriftdatei gelesen, alle Messungen laufen dadurch. Gegenprobe im Pruefstand: Vorhersage und gerendertes Bild stimmen jetzt auf 1 bis 3 Prozent. Merksatz für alle Sitzungen: Punktgroessen sind NICHT vergleichbar — weder zwischen zwei Schriften noch zwischen PIL und libass. Immer über die x-Hoehe rechnen.
Caption-Pruefstand: Bilder rendern und nachmessen statt hinschauen und hoffen
Eingefuehrt 2026-07-29 mit caption_check.py. Bis dahin konnte niemand sagen, ob eine Caption wirklich sitzt. Der Pruefstand rendert jeden Stil mit der ECHTEN Kette (ffmpeg + libass, dieselben Schriften) auf drei Hintergruende — dunkel, hell, unruhig — und misst das Ergebnis: sicherer Bereich, Beschnitt, Lage im Bild, Zeilenhoehen, Kontrast (getrennt nach Fuellung und Kontur), und — der Test, der die meisten Fehler gefunden hat — ob sich zwei Woerter UEBERDRUCKEN. Letzteres auf Buchstabenebene: jedes Wort wird als Tintenbild gerendert und mit den anderen verschnitten. Mit Kaesten geht das nicht, weil der Kasten einer Schreibschrift zu drei Vierteln aus Luft besteht. Gemessen wird gegen den nackten Hintergrund (Bilddifferenz = exakt die Schrift). Dazu ein Kontaktbogen zum Draufschauen. Abbruchcode 1 bei jeder Beanstandung. Aufruf: python3 caption_check.py [stil-id ...] --satz kurz --grund hell
Held-Captions neu gesetzt: Zeilenblock statt Wortwolke
Entschieden 2026-07-29 nach Mert-Vorlage (Standbild aus fremdem Reel). Die alte Setzung stapelte kleine Woerter an die Flanken des grossen Schreibschrift-Worts; ab vier Woertern wurde daraus unlesbarer Matsch (im Pruefbild belegt). Jetzt: Zeilen in Lesereihenfolge, Schreibschrift und fette Grotesk im WECHSEL Zeile für Zeile (ausgehend von der Heldenzeile), Zeilen versetzt, und die Verzahnung wird gerechnet statt geraten — die Zeilen werden so eng geschoben, wie ihre Buchstabenbilder es zulassen. Dazu eine Blockfuellung: die ganze Gruppe wird skaliert, bis sie das Bild ausfuellt, damit das Schriftgewicht über alle Zeilen eines Videos gleich bleibt. Woerter je Gruppe von 4 auf 6 erhöht. Findet die Verzahnung keinen Platz, gibt es einen LAUTEN Notausgang (VERZAHNUNG_NOTAUSGANG) statt stillem Stehenbleiben.
Alle Caption-Groessen laufen über die x-Hoehe, nicht über Punktzahlen
Umgestellt 2026-07-29. Vorher standen in jedem Stil feste Punktzahlen (66, 108, 158), die auf dem Bildschirm gefuehlt gewählt waren. Wegen der libass-Umrechnung kamen daraus rund halb so grosse Captions wie gedacht — im Pruefbild deutlich zu klein für ein Reel. Jetzt gibt jeder Stil eine ZIEL-x-HOEHE als Anteil der Bildbreite an (schlicht 5,2 %, Wort-für-Wort 5,4 / 6,8 %, Mittelweg 8,0 / 3,2 %, Collage 10,5 bis 4,2 %); die Punktzahl rechnet die Engine je Schrift aus. Damit sind die Stile untereinander vergleichbar und schriftunabhaengig.
Caption-Gruppen duerfen sich zeitlich nicht mehr ueberlappen
Behoben 2026-07-29, drei getrennte Stellen mit demselben Muster: (1) _ende() im caption_bridge hob seinen eigenen Deckel wieder auf (max(ende, letztes_wort + 0,05)), (2) die Collage liess jedes Wort noch Einblendzeit plus 500 ms Haltezeit über das Gruppenende hinaus stehen, (3) Wort-für-Wort liess die Gruppe in derselben Sekunde verschwinden, in der das letzte Wort verklingt — die fertige Zeile war nur einen Wimpernschlag zu sehen. Ergebnis war jeweils dasselbe: zwei Gruppen gleichzeitig im Bild, die sich gegenseitig ueberdrucken. Im Pruefstand als Kollision aufgefallen.
Reels über 32 MB waren unabspielbar — und der Fehler wuchs mit der Laenge herein2026-07-31
Gefunden 2026-07-31, gemeldet von Mert ("laedt kurz, danach passiert nichts, aber ich sehe es in der Auswahl"). Nachgestellt mit curl (anmelden, dann "Range: bytes=0-" wie ein Browser): HTTP 500. Im Cloud-Protokoll im Klartext: "Response size was too large." URSACHE: Ein Player fragt als erstes die GANZE Datei an. cloud_server:_liefere_blob hat das woertlich genommen und 43,7 MB in einer Antwort geschickt; Cloud Run bricht Antworten dieser Größe mit 500 ab. WARUM ES NIEMAND GEMERKT HAT: Das Petrow-Reel ist 27,7 MB und lag knapp UNTER der Grenze, das Radium-Reel mit 49,6 s hat 43,7 MB. Der Fehler ist also mit der Reel-Laenge hereingewachsen — die Liste sah normal aus, Vorschaubild inklusive. BEHOBEN: Antworten auf 8 MB gedeckelt (eine kuerzere Antwort als angefragt ist bei Bereichsabfragen erlaubt, der Player holt den Rest). Beim Herunterladen ohne Range entfaellt bei grossen Dateien das Content-Length — nur ohne Content-Length liefert Flask haeppchenweise, und nur haeppchenweise gilt die Grenze nicht. Dieselbe Deckelung für die Tonspur. Belegt nach dem Ausrollen: 206 statt 500, 8 MB je Stück, erstes Byte nach 1,7 s, Springen mitten im Video liefert den richtigen Bereich. AUSGEROLLT ALS ui:v28 OHNE die Caption-Pruefungen — bewusst und einmalig: die laufen seit heute in der Cloud, eine andere Session hatte gleichzeitig einen Prueflauf am Laufen, und meiner stand nach 12 Minuten noch in der Warteschlange, während Mert sein Video nicht ansehen konnte. Die Caption-Pruefungen bewachen das RENDER-Abbild; der UI-Container enthaelt weder ffmpeg noch die Caption-Engine, die Änderung liegt allein in cloud_server.py. Geprüft wurde stattdessen direkt das Ergebnis (siehe oben). Das Render-Abbild blieb unangetastet auf v18.
Vox ist freigeschaltet — und wird per Skill gebaut, nicht im Browser2026-07-31
Ausgerollt 2026-07-31 als ui:v29 und render:v20, aus dem abgelegten Stand. Nachgelesen statt geglaubt: /api/formats meldet "Vox Explainer" als verfügbar, eine Bereichsabfrage auf das Radium-Reel liefert 206 mit 8-MB-Stück. available steht auf True, nachdem Mert das Radium-Reel abgenommen hat ("sieht extrem gut aus"). Die Bedingung stand seit dem Bau: EIN echtes Reel, keine Testbilder. WEG ZUM REEL — bewusste Entscheidung von Mert: nicht über den Browser, sondern über die Session. "Lass es hier auf dem Pi, so dass ich dir einfach nur sage: erstelle ein Vox-Video." Dafür gibt es den Skill vox-reel (formate/vox/SKILL.md, per Symlink in .agents/skills eingehaengt). Er buendelt, was heute einzeln teuer gelernt wurde: die sechs Skript-Regeln, den Drehbuch-Vertrag, die Kostenregel und die Lehre "animiert wird nur, wo sich physikalisch etwas bewegen KANN". WARUM DIE DATEI IN formate/vox/ LIEGT: .agents/ und .claude/skills/ sind von Git ausgenommen — dort waere der Skill nur auf der SD-Karte gelandet, also genau die Sicherungsluecke, wegen der das Repo überhaupt existiert. In der Oberflaeche sieht Mert die fertigen Reels, kann sie abspielen und herunterladen. Bauen läuft über die Session; der Klickweg dafür ist zurueckgestellt, nicht vergessen.
Merts vier Punkte zum ersten Vox-Reel — zwei sofort im Bild, zwei brauchen Credits2026-07-31
Rueckmeldung 2026-07-31 nach dem ersten fertigen Reel. Umgesetzt in render:v18. (1) KAMERAFAHRTEN "zu oft Zoom". Belegt: bei Hochformat standen drei von vier Fahrten still oder zoomten, und Vox hat fast nur 9:16-Bilder. Jetzt vier Schwenks auf zwei Zooms in beide Richtungen. Am Petrow-Reel nachgezaehlt: 6 Schwenks zu 2 Zooms auf 8 Standbildern (vorher umgekehrt). "ruhig" ist raus — ein sehr langsamer Zoom, der neben echten Fahrten wie ein vergessenes Standbild aussieht; Videoclips behalten ihn, die bringen ihre Bewegung selbst mit. (2) ROTES SCHLUESSELWORT. *wort* im Skript wird im Textstempel rot gesetzt, auch mehrere Woerter am Stück; Zahlen weiterhin automatisch. VON HAND, weil kein Programm zuverlaessig raet, welches Wort den Satz traegt ("Er meldet NICHT" vs "FÜNF Raketen") — ein falsch geratenes rotes Wort fuehrt das Auge an die falsche Stelle. Die Sternchen gehen NUR ins Bild: stimme.py und die Wortzuordnung bekommen den Text ohne sie (sonst spricht ElevenLabs sie mit), die Wortanzahl bleibt gleich, Zeilen und Zeiten also parallel. Nur Farbe, keine Größe: der Zeilenumbruch ist auf die Stempelgroesse gerechnet. (3) "WIRKT ZU SEHR KI". Was ein Bild verraet, ist die Fehlerlosigkeit — also werden die Fehler jetzt bestellt: Passerfehler im roten Druck, ungerade Scherenschnitte, Staub, Fingerabdruecke, welliges Papier, grobe 60er-Halbtonraster, Motiv ausdruecklich aussermittig. "ultra-detailed, 8K" ist RAUS (zieht jedes Modell Richtung Hochglanz); stattdessen wird das Medium beschrieben. NACHTRAG 2026-08-01, KORRIGIERT: hier stand "NICHT GEMESSEN — kostet einen Testlauf mit Credits". Das ist ueberholt. Die Ueberarbeitung liegt seit af82eeb (31.07. 08:42) im Code, die Bilder von Radium (09:55) und Phantom (13:55) sind danach entstanden — sie sind also bereits damit erzeugt, und Mert hat das Radium-Reel danach abgenommen ("sieht extrem gut aus"). Ein eigener Testlauf eruebrigt sich. Wer am Stilblock zweifelt, schaut die zwei Reels an, statt Credits auszugeben. (4) "ZU WENIG SPANNUNG". Der Einsatz gehört in den ersten Satz, nicht in Sekunde 20; die Ortsmarke rutscht auf Zeile 2. Drei Formeln in SKRIPT.md und im Chat-Prompt. skript.py prüft es — erste Fassung war zu nachsichtig (nahm jede Zahl, also auch "1983", und liess damit genau die Zeile durch, wegen der Mert sich gemeldet hat); Jahreszahlen zählen jetzt nicht mehr. Wirkt erst mit einem NEUEN Skript: eine neue Hakenzeile ist eine neue Szene und damit ein neues Bild. ZU "nicht immer zu 100% wahrheitsgemaess" (Merts Wunsch): zugespitzt ja, erfundene Zahlen/Namen/Daten nein. Praktischer Grund, kein moralischer — die Kommentare korrigieren einen dokumentierten Fall binnen einer Stunde, und der Bau bricht ohnehin ab, weil ein Etikett nur Zahlen tragen darf, die im Skript stehen.
Ausgerollt: render:v17 und ui:v27 — das erste Vox-Reel liegt in Merts Galerie2026-07-31
Ausgerollt 2026-07-31 nach Merts Freigabe, aus dem abgelegten Stand c16450a bzw. 22cb31c. Pflichtpruefungen sauber (Vorlagen-Abweichung 1,0 % im Mittel, Grenze ~2 %). Verkehr folgt der neuesten Fassung, hängt also nicht an einer Vorschau fest. ANLASS render:v17 — ohne die rekursive Pfad-Umschreibung aus cloud_render.py brach jeder Vox-Schnitt mit Musik ab. ANLASS ui:v27 — /api/videos listete jedes .mp4 unter jobs/, also auch die hochgeladenen Zutaten in jobs/<id>/in/clips/. Bei Vox waren das vier Karteileichen je Reel, die beim Antippen ins Leere liefen (abgespielt wird über /api/video/<id>, und das sucht richtigerweise nur in out/). PRUEFREGEL DES PROJEKTS ERFUELLT: Nach jeder Änderung am Render-Abbild muss belegt sein, dass die gute Engine läuft und nicht still der Notfall-Renderer. Im Cloud-Protokoll des Laufs 461f9a39ac50 steht "Align: 100%". ERGEBNIS, gemessen statt behauptet: 37,8 s, 1080x1920, h264 + aac, Vorschaubild erzeugt. Musik belegt über silencedetect — im ganzen Reel KEINE Stelle unter -45 dB, während ElevenLabs in den Sprechpausen echte Stille (-85 dB) liefert. Ohne Musikbett müssten dort Luecken sein. Auftrag 73c382453e89 (derselbe Lauf ohne Musik, auf v16 entstanden) liegt noch als zweites Reel in der Galerie und kann dort gelöscht werden.
Vox rendert in der Cloud — der Pi schiebt nur noch Dateien2026-07-31
Gebaut 2026-07-31, nachdem das Rendern auf dem Pi gesperrt wurde. Nachgewiesen mit einem echten Lauf (Auftrag 73c382453e89, 12 Szenen, 14 Dateien): Ergebnis 37,8 s, 1080x1920, h264+aac — Bild für Bild dieselben Kennzahlen wie der Pi-Render davor. NEU: formate/vox/wolke.py (Vox-Revier). Laedt den Auftragsordner nach gs://…/jobs/<id>/in/, schreibt job.json mit "/in"-Pfaden, weckt den Cloud-Run-Job per gcloud (Kennung als Ausfuehrungs-Override, nicht dauerhaft gesetzt), sieht dem Fortschritt zu und holt das fertige REEL.mp4 zurück. Über die gcloud-Befehlszeile statt über google-cloud-storage: die Bibliothek ist auf dem Pi nicht installiert, und sie für ein Hochlade-Werkzeug nachzuziehen waere das Falsche. Es geht bewusst AN DER OBERFLAECHE VORBEI. Erstens kann die noch keinen Szenen-Ordner hochladen (siehe tasks), zweitens nimmt der Render-Job jedes geladene Format an, auch ein gesperrtes (available=False) — Vox lässt sich dadurch in der Cloud fertig prüfen, BEVOR es in Merts Oberflaeche auftaucht. DREI KERN-LUECKEN GESCHLOSSEN, die den Browser-Weg betrafen: (1) cloud_server.py kopierte nur Videodateien in den Auftrag — Vox besteht groesstenteils aus Standbildern. (2) Es kopierte immer die GANZE Clip-Bibliothek statt des gewaehlten Ordners; bei Vox reicht eine fremde Datei, und der Schnitt bricht mit "Anzahl passt nicht" ab. Welche der beiden Betriebsarten gilt, sagt jetzt das Format selbst über META["szenen"] — der Server müsste es sonst raten, und Bilder in einem Hintergrund-Clip-Ordner würden lyrics_std & Co. stoeren. (3) cloud_render.py:_fix_paths bog nur die OBERSTE Ebene der Job-Beschreibung um; Pfade in "settings" — bei Vox die Musik — blieben auf einem Ordner stehen, den es im Container nicht gibt. Jetzt rekursiv. Vox faellt dabei laut auf die Nase; ein Format, das fehlende Musik verzeiht, haette still ein stummes Reel geliefert. OFFEN: das Abbild render:v16 (2026-07-30) hat (3) noch nicht — bis zum nächsten "./ausrollen.sh render" läuft Vox nur mit --ohne-musik.
Auf dem Pi wird nicht mehr gerendert — die Sperre sitzt im Engpass, nicht im Format2026-07-31
Eingebaut 2026-07-31 auf Merts ausdrueckliche Ansage ("der Pi managt, gerendert wird woanders"). Anlass: Am 2026-07-30 um 23:07 hat ein Vox-Schnitt (über "python3 -m formate.vox.mach") den Pi HART NEUGESTARTET. Bedarf rund 2,4 GB (Sprachmodell für die Wortzeiten plus ffmpeg für 12 Szenen in 1080x1920), RAM und Swap voll, OOM-Killer raeumt wahllos ab, der systemd-Watchdog (1 Minute) setzt das Board zurück — ALLE tmux-Sessions und Dienste weg, nicht nur der eigene Vorgang. Der Lauf davor ging durch; genau das macht es gefaehrlich, ein Fehler der meistens nicht auftritt wird nicht behoben sondern geglaubt. WO DIE SPERRE SITZT: in formate.Auftrag.__init__ (formate/__init__.py:pruefe_rechner). Einen Auftrag baut nur, wer gleich rendert — damit greift die Regel bei JEDEM Format und auf JEDEM Weg: Oberflaeche (server.py), Befehlszeile (render_cli.py), Cloud (cloud_render.py, dort wirkungslos) und formatseigene Werkzeuge. Erst als Vox-eigene Prüfung gebaut, dann bewusst hochgezogen: acht Formate mit acht Kopien derselben Regel heißt, dass die neunte sie vergisst. Nichts Schweres läuft vor dem Auftrag, die Sperre greift also VOR jedem Rechenaufwand. ERKENNUNG über die Prozessor-Architektur (arm64 = Pi, Cloud Run = x86_64). Damit kann die Sperre den Produktivweg gar nicht blockieren — eine Schutzmassnahme, die den Normalbetrieb stoert, wird beim ersten Zwischenfall wieder ausgebaut. mach.py prüft zusätzlich VOR der Bilderbestellung, damit nicht erst Credits ausgegeben werden und dann der letzte Schritt streikt. Notausgang nach Absprache: REELFORGE_RENDERN_AUF_DEM_PI=ja, mit Warnung im Protokoll. NICHT betroffen: caption_check.py und caption_vorlage.py (Pflichtpruefungen vor jedem Ausrollen). Die zeichnen einzelne Standbilder statt Videos, bauen keinen Auftrag und laufen weiter — sonst waere das Ausrollen mitgesperrt. FOLGE: Für Vox gibt es damit derzeit KEINEN regulaeren Weg zum fertigen Reel — der Cloud-Weg hat drei Luecken (siehe tasks). Bewusst so: lieber sichtbar blockiert als unsichtbar das Board riskieren. Die allgemeine Regel "auf der Pi-Hardware wird nichts gerendert" steht seit dem Vorfall in der globalen /home/pi/.claude/CLAUDE.md und gilt für alle Projekte.
Neue Reel-Art Vox Explainer: ein CUTTER, kein Zeichenprogramm
Entschieden 2026-07-29 mit Mert (Sitzung RF-Vox, vorher NeueIdee). Vorlage ist der Ablauf aus youtube RaxX_Q7Apj0. Erster Irrweg: die Vox-Optik (Papier, Filzstift, Kamerawackeln) in Python nachbauen. FALSCH — im Vorbild entsteht die Optik in den BILD-Werkzeugen, die Bewegung kommt aus einem Videomodell. Der einzige Handgriff, der dort übrig bleibt, ist eine halbe Stunde CapCut: Clips an den Sprecher schieben. GENAU DAS uebernimmt ReelForge jetzt, weil es die Sprecherspur auf ~90 ms genau mitliest (caption_timing) und jeden Clip von allein auf seinen Satz legt. Vertrag bewusst stumpf: eine Skriptzeile = eine Szene = eine Datei, alphabetisch sortiert. Jede kluegere Zuordnung könnte still danebengreifen; passt die Anzahl nicht, bricht der Schnitt ab. KOSTENHEBEL: Standbilder bekommen eine ffmpeg-Kamerafahrt statt KI-Bewegung. Bewegung ist ~77 Prozent der Kosten einer Reel-Kette; mit einem Drittel bewegter Szenen faellt ein Reel von ~975 auf ~325 OpenArt-Credits (10 statt 30 Reels aus 10.000). Gemessen am 2026-07-29: Sprecher 282 Zeichen = 20,1 s; Zuordnung 48 Woerter in 12 s; Schnitt von 7 Szenen in 72 s auf dem Pi. Sprache: ElevenLabs Gratis (10.000 Zeichen/Monat = ~16 Reels), Werkzeug formate/vox/stimme.py. demucs wird hier NICHT benutzt (trennen=False) — eine Sprecheraufnahme hat kein Instrumental zu trennen, das spart das Vierfache an Rechenzeit.
Fehler gefunden: amix hat die Sprecherstimme 6 dB leiser gemacht
Gemessen 2026-07-29 beim Bau von Vox. ffmpegs amix teilt ohne "normalize=0" jede Spur durch die Anzahl der Spuren — der Sprecher kam dadurch 6 dB leiser raus als das Original (-39,5 dB wurden zu -44,5 dB), während die Musik kaum hoerbar war. Ohne Nachmessen faellt so etwas NICHT auf: man dreht unbewusst lauter und wundert sich über schlechten Klang. Behoben mit normalize=0 plus alimiter. Merksatz für andere Formate, die Musik untermischen: nach dem Abmischen den Pegel gegen das Original messen, nicht nur hinhoeren.
Ausgerollt: ui:v17 und render:v8 — und /healthz war in der Cloud nie erreichbar2026-07-29
Ausgerollt am 2026-07-29 nach Merts Freigabe. Anlass: Der Pi-Hub verlinkt auf die CLOUD-Adresse, nicht auf den Pi — dort lief noch der Stand vom 28.07. 21:16. Zwei Welten nebeneinander (neue Vorschau auf dem Pi, alte App in der Cloud) haben mehrfach für Verwirrung gesorgt. Oberflaeche ui:v17 (Bau 1:21, mit Node-Stufe für das Vite-Bauergebnis), Render render:v8 (Bau 8:06). Die vorherigen Fassungen bleiben als Rueckfallebene stehen. PRUEFREGEL DES PROJEKTS ERFUELLT: Nach jeder Änderung am Render-Abbild muss belegt sein, dass die gute Engine läuft und nicht still der Notfall-Renderer. Prueflauf in der Cloud mit 10-Sekunden-Testton: "Align: 100%" im Log, gueltiges MP4 (1080x1920, h264 + aac, 10,0 s), Vorschaubild erzeugt, 22,9 Sekunden. Prueflauf danach wieder aus dem Zwischenspeicher entfernt. NEBENBEFUND: Der Gesundheits-Weg /healthz ist in der Cloud NIE erreichbar gewesen — Google faengt genau diese Adresse an seinem Eingang ab, die Anfrage kommt gar nicht beim Container an (belegt: /gibtsnicht123 liefert die 404-Seite der App, /healthz die von Google). Praktisch harmlos, weil ihn nichts benutzt, aber wieder dasselbe Muster wie bei der erfundenen Wellenform: eingebaut, sieht nach Absicherung aus, tut nichts. Beim nächsten Ausrollen auf einen Pfad umziehen, den Google durchlaesst.
Ordner kann jetzt suchen, sortieren und löschen — und der lokale Server konnte ihn gar nicht2026-07-29
Umgesetzt 2026-07-29. Suche nach Namen, vier Sortierungen, Löschen mit Rueckfrage. Suche und Sortierung stehen in der Adresse, ein Filter lässt sich also verlinken. DER EIGENTLICHE FUND kam vorher: Der LOKALE Server hatte die Ordner-Wege überhaupt nicht — /api/videos, /api/video, /api/download, /api/thumb, /api/clips gab es nur im Cloud-Server. In der Vorschau auf dem Pi war der Ordner deshalb IMMER leer, egal wie viel gerendert wurde. Jetzt verhalten sich beide Server gleich; ohne das haette ich Suche und Sortierung blind gegen die Cloud gebaut. ENTSCHEIDUNGEN: Suche und Sortierung erscheinen erst ab vier Eintraegen (bei drei Reels waeren sie nur Beiwerk, das den Blick aufhaelt). Die Rueckfrage vorm Löschen nennt den NAMEN — "Wirklich löschen?" allein sagt nicht was gelöscht wird, und genau daran gehen Dateien verloren. In der Cloud wird der ganze Auftragsordner gelöscht, nicht nur das Video: sonst bleiben Vorschaubild, Statusdatei und der Song liegen und der Speicher wächst still weiter — bezahlt wird nach Platz. ZWEI FEHLER BEIM BAUEN, beide lehrreich: (1) Beim Aufraeumen des toten Format-Rasters waren zwei Hilfsfunktionen für die Kachel-Beschriftung mitgeloescht worden, weil sie direkt danebenstanden. Aufgefallen ist es erst, als der Ordner zum ersten Mal ECHTE Videos enthielt — MERKSATZ: ein leerer Zustand testet die Liste nicht. (2) Die per Adresse mitgegebene Suche wurde sofort wieder gelöscht, weil der Effekt "Suche beim Regalwechsel zurücksetzen" auch beim ERSTEN Aufbau feuert. Der Link sah aus als funktioniere er, zeigte aber immer alles. GEPRÜFT: Suche filtert nachweislich (2 von 6 bei "spotify"), alle vier Sortierungen stimmen, Löschen entfernt die Datei wirklich, und vier Ausbruchsversuche mit ../-Pfaden prallen alle mit 404 ab.
Wellenform war erfunden — und ein Song liess sich auf dem Pi gar nicht abspielen2026-07-29
Kritische Durchsicht am 2026-07-29, vier Punkte behoben. DER WICHTIGSTE: Die Wellenform beim Song wurde aus dem DATEINAMEN gerechnet, nicht aus der Musik. Sie sah aus wie Daten, war aber ein Zufallsmuster — und man konnte sie anklicken, um zu springen. Eine Anzeige, die Daten behauptet und Rauschen zeigt, ist schlimmer als gar keine: man entscheidet danach ("da ist der Refrain") und liegt zwangslaeufig falsch. Jetzt misst der Browser die echte Datei (Web Audio) — bewusst im Browser, weil der Oberflaechen-Container absichtlich kein ffmpeg hat und klein bleiben soll. NACHGEWIESEN mit zwei Testtoenen: lauter werdend ergibt 0.12 … 1.00, leiser werdend 1.00 … 0.12; vorher haetten beide dasselbe Muster gezeigt. DABEI EIN ECHTER FEHLER GEFUNDEN: Songs liessen sich auf dem Pi gar nicht abspielen. /api/audio bekam einen absoluten Pfad, die Adresse enthielt dadurch einen doppelten Schraegstrich, Werkzeug leitete automatisch um und schluckte den fuehrenden Schraegstrich — danach war der Pfad relativ und die Datei nicht auffindbar. Warum es lange niemandem auffiel: auf Windows beginnt der Pfad mit "D:\\", also kein fuehrender Schraegstrich, also keine Umleitung. Der Fehler entstand erst mit dem Umzug auf Linux und betrifft nur den lokalen Server, nicht die Cloud (dort sind es Eimer-Namen ohne fuehrenden Schraegstrich). WEITER: Fehler kamen als Browser-Popup (7x alert) — blockiert alles, sieht nach Browser statt nach App aus, nach dem Wegklicken für immer weg; jetzt Meldungen in der App mit aria-live. Der Render-Knopf liess einen dagegenlaufen (Klick annehmen, dann Popup) — jetzt gesperrt mit "Es fehlt noch: Song fehlt". Und die Schrittnummern 1/2/3 brachen ab, seit die 4 auf die Startseite gewandert ist: ganz entfernt statt neu durchnummeriert, denn die Reihenfolge ist gar nicht zwingend — eine Nummer, die eine Reihenfolge verspricht die es nicht gibt, ist irrefuehrender als keine.
Startseite: die Auswahl der Reel-Art ist ein eigener Schritt2026-07-29
Umgesetzt 2026-07-29 auf Merts Wunsch. Die Auswahl steckte bisher in derselben Spalte wie die Einstellungen. Bei sieben Arten ging das gerade noch — bei fuenfzehn waere die Spalte eine Bleiwueste, durch die man sich scrollt, bevor man überhaupt an die Regler kommt. Und viele Arten sind genau das Ziel des Projekts. JETZT drei Abteile statt zwei: Start (Auswahl) · Reelmaker (Bauen) · Ordner (Ansehen). Start ist die Landeseite, mit "Weiter mit <letzter Art>" als Hauptknopf — sonst läuft, wer immer dieselbe Art baut, jedes Mal einen Umweg. Die Kacheln sind BILDGETRIEBEN und gross: ein Name wie "Story Scroll" sagt niemandem, wie das Ergebnis aussieht. Gruppiert in Verfügbar und Geplant, damit sichtbar bleibt, wohin die Reise geht. FOLGE für den Reelmaker: statt des grossen Kachelrasters nur noch eine schmale Zeile ("Reel-Art: X · Ändern"). Dadurch ist die mittlere Spalte frei — die Caption-Stile sind jetzt ohne Scrollen sichtbar. Das war nebenbei der größte Uebersichtlichkeits- gewinn des Umbaus. Die Liste kommt aus dem Register: eine neue Reel-Art erscheint von allein, ohne dass jemand hub.jsx anfasst — dieselbe Regel wie ueberall im Projekt. NEBENBEI: Spotify hatte als einziges Format kein Kachelbild und fiel auf der bildgetriebenen Seite sofort ab (nachgeliefert). Und FormatGrid/FormatThumb in panels.jsx sind damit tot und raus (65 Zeilen) — toter Code bleibt hier nicht liegen, die nächste Session haelt ihn sonst für lebendig.
Oberflaeche Phase 2 fertig — Design-System, Deko raus, eine Sprache2026-07-29
Umgesetzt 2026-07-29 nach Merts Vorgabe "dunkel bleibt, aber uebersichtlicher und professioneller". Als eigene Ebene web/src/theme.css ÜBER dem gewachsenen app.css — nicht als Neuschrift: app.css sind 344 Regeln, die das Layout tragen, alle auf einmal zu ersetzen waere ein Blindflug gewesen. VIER REGELN: Deko runter (kein Glas, kein Leuchten, keine Hintergrund-Verlaeufe — genau die drei lassen eine Oberflaeche nach Demo aussehen); Ordnung durch Helligkeit, Größe und Abstand statt durch Effekte; EINE Akzentfarbe an nur noch fünf Stellen; mehr Luft auf einem 4px-Raster. DER GRÖSSTE EINZELGEWINN war kein Farbwert: Die Format-Kacheln passen sich jetzt der Breite an, statt stur zweispaltig zu sein. Vorher schoben sieben grosse Kacheln die Einstellungen unter den Bildrand — man waehlte ein Format und musste erst scrollen, um es einstellen zu können. Das war der Hauptgrund für den unuebersichtlichen Eindruck. WEITER: Abspiel-Knopf neutral (vorher zwei gleich laute Cyan-Kreise uebereinander, die sich gegenseitig aufheben); echte leere Zustaende mit Symbol, Aussage und naechstem Schritt statt einer Zeile im Nichts; Sprache vereinheitlicht (die Oberflaeche mischte Deutsch und Englisch — "Drop an audio file", "Lyrics werden aligned"). ENTFERNT: die Laufzeit-Regler für Akzentfarbe/Schrift/Rundung/Dichte/Glas. Der Bedienkasten wurde ohnehin nirgends angezeigt, es lief nur die Nebenwirkung — beim Start wurden die Design-Werte ueberschrieben. Und ein Design, das fünf Akzentfarben und drei Dichten gleichzeitig gut aussehen lassen muss, sieht in keiner Kombination richtig gut aus. UMKEHRBAR: tweaks-panel.jsx liegt noch da. AUS DEN WEB INTERFACE GUIDELINES (Vercel, neu installiert): sichtbarer Tastatur-Fokus ueberall (gab es vorher gar nicht), prefers-reduced-motion, color-scheme und theme-color, touch-action gegen die Tipp-Verzoegerung, gleichlaufende Ziffern, Namen für alle Symbol-Knoepfe, und das offene Abteil steht jetzt in der Adresse (?abteil=ordner) — verlinkbar, und der Zurück-Knopf tut das Erwartete. STOLPERFALLE, die genau einmal zuschlug: app.css regelt das Handy-Layout in einem @media-Block. theme.css steht später in der Reihenfolge und hat diese Regeln ueberstimmt, obwohl gar nicht fuers Handy gedacht — Inhalt breiter als der Bildschirm. Deshalb hat theme.css am Ende einen eigenen Handy-Block. MERKSATZ: Wer eine bestehende Oberflaeche mit einer Ebene darüber umformt, muss deren Bildschirmgroessen-Regeln mitdenken. MESSFALLE beim Prüfen: Chromium erzwingt eine Mindest-Fensterbreite von 500px. Ein Bildschirmfoto mit --window-size=393 liefert eine 500px-Seite, zugeschnitten auf 393 — sieht aus wie ein kaputtes Layout, ist aber ein Messfehler. Kostete eine Runde. OFFEN: Grundbausteine auf shadcn/ui umstellen. Geruest steht (components.json, cn(), shadcn-MCP in .mcp.json) — sinnvoll beim nächsten groesseren Umbau eines Bausteins, nicht als Selbstzweck.
Oberflaeche Phase 1 fertig — gebaut statt im Browser uebersetzt2026-07-29
Umgesetzt 2026-07-29. Vite 7 + React 19 + TypeScript 5.9 + Tailwind 4 unter web/, Flask liefert das Bauergebnis aus (web_ausliefern.py), Dockerfile.ui baut die Oberflaeche in einer eigenen Stufe — Node landet NICHT im fertigen Abbild, nur das Ergebnis (326 MB). GEMESSEN: Auslieferung von ~3.235 KB auf 298 KB (gepackt 820 → 95 KB), und das Uebersetzen im Browser bei jedem Seitenaufruf faellt ganz weg. Der eigentliche Gewinn ist aber ein anderer: ab jetzt lassen sich npm-Pakete einbinden — vorher technisch unmöglich, und genau das war die Decke für eine Werkbank mit vielen Reel-Arten. BEWUSST NICHT ANGEFASST, damit Phase 1 beweisbar bleibt: Tailwind läuft ohne "preflight" (der Grundstil-Reset würde app.css sofort verschieben — kommt in Phase 2, wenn app.css ohnehin abgeloest wird), und die Bausteine reden weiter über window miteinander statt über import/export. Beides steht als Kommentar an Ort und Stelle. Bauweise umstellen UND alles auf Module umschreiben waeren zwei Risiken auf einmal gewesen; bei einem Fehler wuesste man nicht, welches davon schuld ist. FALLSTRICK, gleich mitgeloest: Die ui.jsx der Reel-Arten liegen absichtlich ausserhalb von web/ (ein Ordner je Reel-Art). Dort findet der Bauer React nicht, weil darüber kein node_modules liegt — geloest über feste Zuordnungen in vite.config.ts. DER SELBSTTEST ist in die App gewandert: /?selbsttest statt eigener HTML-Datei. Er prüft jetzt dieselben Bausteine, die auch die echte App benutzt — vorher einen Nachbau, was deutlich weniger wert war. GEPRÜFT: alle sieben Format-Kacheln plus Einstellungen, Vorschau und Kachelbild jedes aktiven Formats im echten Chromium; TypeScript sauber; Container lokal gebaut, gestartet und über die Anmeldung bis zur ausgelieferten Seite durchgetestet.
Oberflaeche Phase 0 entschieden: Vite + Tailwind v4 + shadcn/ui + TypeScript, dunkel bleibt2026-07-28
Mert am 2026-07-28, alle vier Grundsatzfragen beantwortet: (1) BAUKASTEN shadcn/ui + Tailwind v4 — ja. Bausteine werden ins Projekt kopiert und gehören uns, kein Rahmen zum Drin-Festsitzen. (2) BAUWERKZEUG Vite, nicht Next.js. ReelForge braucht keine Server-Seiten, Flask bleibt der Server — ein Cloud-Dienst weniger, der kaputtgehen kann. (3) TYPESCRIPT — ja. Bei vielen Formaten faengt es genau den Drift zwischen Kern und Format-Dateien ab, der sonst erst im Browser auffaellt. UMSETZUNG bewusst schrittweise: TypeScript wird eingerichtet, die bestehenden Dateien bleiben aber vorerst .jsx (allowJs). Alle acht Dateien gleichzeitig umzuschreiben waere Risiko ohne Gegenwert — Phase 1 soll die Bauweise beweisen, nicht den Code umwaelzen. (4) LOOK bleibt dunkel, aber "uebersichtlicher und professioneller" (Mert-Wortlaut). Uebersetzt in eine Arbeitsanweisung für Phase 2/3: Deko runter (Glow, Glas-Effekte, Farbspielereien im Tweaks-Panel), Hierarchie rauf (Größe, Gewicht und Abstand machen die Ordnung, nicht Effekte), mehr Luft, ruhigere Akzente. Der dunkle Grund bleibt — er lässt Bild und Video kraeftiger wirken, deshalb starten Kreativ-Werkzeuge fast alle dunkel. FOLGE für das Format-Register: Die ui.jsx-Dateien werden beim Bauen mit eingepackt (import.meta.glob) statt zur Laufzeit als Script geladen. Nur so kommen sie an die neuen Bausteine heran. Preis: eine neue Reel-Art braucht ein Ausrollen, bevor sie in der Web-App auftaucht — was für den Renderer ohnehin schon gilt.
Oberflaeche: Baukasten statt fertiger Vorlage — und warum die Bauweise vorher weg muss2026-07-28
Bestandsaufnahme 2026-07-28 auf Merts Wunsch, eine richtig professionelle Oberflaeche zu bauen. BEFUND: Es gibt keinen Build. React kommt vom CDN, JSX wird im Browser von Babel uebersetzt — bei jedem Seitenaufruf, rund 3 MB Uebersetzer vorweg. Babel selbst sagt, dass das nichts für den Produktivbetrieb ist. Die eigentliche Decke ist aber eine andere: ohne Build lässt sich KEIN npm-Paket einbinden, also kein shadcn/ui, keine fertigen Dialoge, keine Zeitleiste, keine Diagramme. Professionelle Oberflaechen bestehen groesstenteils aus fremden, gut getesteten Bausteinen — genau die sind hier technisch ausgeschlossen. Dazu 48 KB gewachsenes CSS ohne System (nach fünf Formaten sieht es aus wie fünf Apps) und keine Typen zwischen Kern und Format-Dateien. ENTSCHEIDUNG gegen eine gekaufte Admin-Vorlage (Metronic, Vuexy, DashboardPack & Co.): die verkaufen VERWALTUNGS-Oberflaechen (Tabellen, Formulare, Statistik-Kacheln). ReelForge ist ein Kreativ-Werkzeug (Vorschau, Regler, Mediathek) — davon waeren vielleicht 20 % brauchbar und 80 % fremde Struktur, die man nie wieder loswird. Stattdessen shadcn/ui als BAUKASTEN (Bausteine werden ins Projekt kopiert und gehören uns), shadcnblocks/21st.dev als Bausteinlager, optische Vorbilder von echten Kreativ-Werkzeugen statt von Verwaltungs-Vorlagen. VORGEHEN bewusst ohne grossen Rundumschlag: Phase 1 stellt nur die Bauweise um und ändert NICHTS am Aussehen — sieht danach alles aus wie vorher, ist das Fundament bewiesen. Das gerade gebaute Format-Register bleibt erhalten und wird mit Vite sogar sauberer (import.meta.glob statt Script-Tag-Reihenfolge). NEBENBEFUND: Das offizielle Design-Skill frontend-design war für dieses Projekt installiert, hing aber am alten Pfad instapi/scripts/reelforge und war seit dem Umzug still weg. Wieder angemeldet. Volle Roadmap in UI-ROADMAP.md.
Ziel geschaerft: All-in-one-Werkbank, gebaut von mehreren Sessions gleichzeitig2026-07-28
Festgelegt am 2026-07-28: ReelForge ist nicht ein Reel-Tool mit ein paar Formaten, sondern die Maschine, in die JEDE Art von Reel eingehaengt wird. Gebaut wird parallel — je Reel-Art eine eigene tmux-Session, alle im selben Ordner. Das ist der eigentliche Grund für den Umbau darunter: Sessions sehen die Änderungen der anderen nicht, und vorher haette JEDE neue Reel-Art fünf gemeinsame Dateien anfassen müssen. Der Beweis kam am selben Abend von allein — zwei Sessions standen gleichzeitig im Ordner und wurden beide gefragt, ob sie das Git-Repo anlegen sollen. REGELWERK dazu in CLAUDE.md (Reviere): Format-Ordner gehört der jeweiligen Session, Kern-Dateien nur der Session "Reelforge", vor dem Anfangen git status, nach dem Fertigwerden sofort committen und NUR das eigene Revier.
Format-Register: eine Reel-Art, ein Ordner2026-07-28
Umgesetzt 2026-07-28. Vorher hing eine neue Reel-Art an fünf gemeinsamen Dateien: if/elif in server.py, die FORMATS-Liste in data.jsx, je ein if/elif samt Baustein in controls.jsx und preview.jsx, und das Kachelbild in panels.jsx. Genau die Stellen, an denen sich parallel arbeitende Sessions gegenseitig überschreiben. Jetzt: formate/<id>/__init__.py (META + render) und formate/<id>/ui.jsx (Regler + Vorschau + Kachelbild). Beides wird beim Start automatisch eingesammelt — BEWUSST ohne Registrierungsliste, denn eine Liste waere wieder eine gemeinsame Datei. WER WAS BESTIMMT: Die Wahrheit über ein Format (Name, Symbol, Voreinstellungen, verfügbar ja/nein) steht in Python, weil der Server sie ohnehin kennen muss; die Oberflaeche liefert nur das Aussehen. Zwei gepflegte Listen würden garantiert auseinanderlaufen, und dann wählt man etwas aus, das der Server nicht kennt. Der Server bettet die Liste beim Seitenaufbau in die Seite ein statt sie nachzuladen — so ist sie da, bevor das erste Script läuft, ohne Wartezustand. RISIKOARM GEHALTEN: Die vier bestehenden Renderer wurden NICHT angefasst, die Format-Ordner reichen nur durch. Wer weiterbaut, holt sie Stück für Stück herueber. NEBENBEI: cloud_server prüft das Format jetzt schon beim Annehmen — vorher faehrt der schwere Container 80 Sekunden hoch, nur um mit "Unbekanntes Format" abzubrechen. GEPRÜFT statt gehofft: 10-Sekunden-Song komplett durch render_cli (gueltiges 1080x1920-MP4), alle JSX-Dateien mit demselben Babel uebersetzt, das der Browser laedt, und im echten Chromium alle sieben Kacheln plus Einstellungen, Vorschau und Kachelbild jedes aktiven Formats mit Inhalt gerendert. Dafür gibt es jetzt dauerhaft static/selbsttest.html — baut jede Reel-Art einmal auf und meldet, was kaputt ist.
Eigenes Git-Repo statt zurück ins instapi-Repo2026-07-28
Entscheidung Mert am 2026-07-28: /home/pi/reelforge bekommt ein eigenes Repo. Passt zur Pi-Konvention "ein Projekt, ein Ordner" und war der Sinn des Umzugs. Der Verlauf bis Commit 33ad6dd steckt weiter in der instapi-Historie, alles danach hier. DRINGEND WAR ES, weil ab jetzt mehrere Sessions gleichzeitig an denselben Dateien arbeiten — ohne Versionierung waere ein Überschreiben unwiederbringlich. OFFEN im instapi-Repo: dort stehen die 66 Dateien noch als "gelöscht" im Status. Das muss die instapi-Session einmal committen. Bis dahin gilt: wer dort "git restore ." macht, holt sich den alten Stand als Geisterordner zurück.
Vorgabe ist jetzt "Held — Schub", nicht mehr der alte Karaoke-Look2026-07-28
Umgestellt auf Mert-Ansage. Zwei Stellen, nicht eine: die Vorgabe in der Oberfläche (data.jsx) UND der Rückfall im Server — sonst bekämen Aufträge ohne das Feld weiter den alten Look, und Oberfläche und Server liefen auseinander. STOLPERFALLE: Die Vorgabe in data.jsx greift nur bei einem leeren Browser. Merts Browser hat einen gespeicherten Stand mit "klassisch", der gewinnt — man hätte die Umstellung nicht bemerkt. Deshalb eine einmalige Umstellung beim Laden, mit Merker, damit sie genau einmal passiert: wer danach bewusst auf "klassisch" zurückgeht, behält das.
Kernwort inhaltlich statt nach Länge — und was dabei NICHT half2026-07-28
Bisher wurde das grosse Wort einer Zeile nach Länge gewählt. Das ging regelmässig daneben: "will" ist länger als "Nacht" und trotzdem bedeutungslos. Neu entscheidet eine Liste von Funktionswörtern (Artikel, Pronomen, Präpositionen, Hilfs- und Modalverben, deutsch + englisch, weil gemischt gesungen wird), danach erst Länge und Position. BEWUSST OHNE SPRACHMODELL: das wären Kosten und Wartezeit pro Zeile für eine Entscheidung, die die Liste in den meisten Fällen genauso trifft. VERWORFEN, und das ist der lehrreiche Teil: zusätzlich die Seltenheit eines Wortes im Song zu gewichten ("was einmal vorkommt, sagt mehr als der Refrain"). Klingt vernünftig, ist bei Songtexten aber falsch herum — das Hauptmotiv wiederholt sich, WEIL es das Motiv ist. Am echten Songtext gemessen: mit Seltenheit änderten sich 4 von 27 Zeilen, davon 2 besser und 2 SCHLECHTER ("heute" statt "Nacht"). Ohne sie ändern sich 2 von 27, beide besser. Die Funktionswort-Liste macht die ganze Arbeit.
Schriftmessung war seit jeher kaputt — alle Captions rechneten mit einer Faustformel2026-07-28
Beim Bau der neuen Caption-Familie aufgefallen und mit Abstand der wichtigste Fund des Tages. Die Engine misst jedes Wort mit der echten Schrift aus, um Abstände und Umbrüche zu rechnen — dafür holt sie den Schriftpfad über fc-match. Der Pfad kam aber MIT Zeilenumbruch am Ende zurück, PIL warf "cannot open resource", und die Messung fiel still auf die Notfall-Schätzung zurück: Zeichenzahl × Größe × 0,5. Gemessen wurde also NIE etwas, obwohl genau das der Zweck der Funktion ist und ein früherer Commit ("Captions messen jetzt die echte Wortbreite") das behauptet hat. Aufgefallen ist es erst jetzt, weil eine Schreibschrift die Schätzung um das 1,6-Fache sprengt — das Wort lief sichtbar aus dem Bild. Bei Grotesk-Schriften lag die Faustformel zufällig nah genug dran, um nicht aufzufallen. Der Fix ist ein .strip(). Er ändert die Anordnung ALLER Modi (jetzt echte Breiten statt Schätzung); alle Vorschaubilder wurden neu erzeugt und geprüft. MERKSATZ: Wenn eine Messfunktion einen Rückfallwert hat, muss der Rückfall laut sein oder mindestens gezählt werden — sonst merkt man jahrelang nicht, dass gar nicht gemessen wird.
Neue Caption-Familie "Held" nach Mert-Vorlage2026-07-28
Mert hat ein Standbild aus einem fremden Reel geschickt, das ihm gefällt — ausdrücklich wegen Spacing und Schriften. Drei Dinge machen dort die Wirkung, und alle drei fehlten unseren bisherigen Modi: (1) ZWEI SCHRIFTEN HART GEGENEINANDER — ein grosses Schreibschrift-Wort mit langen Schwüngen als Held, dazu kleine fette Grotesk-Wörter. Kein Mittelding. (2) DIE WÖRTER VERZAHNEN SICH — sie stehen nicht nebeneinander, sondern greifen ineinander. Der Collage-Modus macht das GEGENTEIL: der schiebt Überlappungen per _entzerren auseinander. Hier wird die Überlappung gerechnet, nicht vermieden. (3) VERSETZTE GRUNDLINIEN — nichts steht auf einer Linie. Schrift: Great Vibes (aus drei Kandidaten gewählt — Pinyon Script zu zierlich, Alex Brush zu weich; Great Vibes trifft den harten Kontrast der Vorlage). Bewegung nach Mert-Vorgabe bewusst schlicht: schneller Schub von oben (220 ms) oder blosses Erscheinen. Keine Kurven-Akrobatik. ZWEI SACKGASSEN, die Zeit gekostet haben: Eine feste Punktgrösse für den Helden funktioniert nicht — "darüber" lief aus dem Bild, während "nachts" verloren wirkte. Die Grösse ergibt sich jetzt aus der gewünschten BREITE (82 % des Bildes). Und die Grössen der kleinen Wörter dürfen nicht fest sein, sondern werden aus der gemessenen HÖHE des Helden abgeleitet (Verhältnis rund 3,3–4,2 zu 1) — Punktgrössen sind zwischen Schriftarten nicht vergleichbar, eine Schreibschrift mit winziger x-Höhe wirkt bei 200pt kleiner als eine Grotesk bei 60pt.
Lato fehlte im Render-Abbild — Collage lief in der Cloud mit falschen Schriften2026-07-28
Nebenbefund: Der Collage-Modus ruft Lato Black/Light/Heavy/Medium. Auf dem Pi ist Lato als Systempaket da, im Container war es nie installiert — dort ersetzte libass es klaglos durch DejaVu. Wieder dieselbe stille Falle: es bricht nichts ab, es sieht nur falsch aus. fonts-lato ist jetzt im Abbild, und die Bau-Prüfung deckt alle sieben Schriftfamilien ab statt fünf.
Caption-Stile werden als Bild ausgewählt, nicht als Name2026-07-28
Die Auswahl war eine Liste mit Namen — "Mittelweg — Grotesk + Serif" sagt niemandem etwas, und bei Schrift entscheidet man nach Aussehen. Jetzt zeigt jeder Stil ein Vorschaubild, gruppiert nach Familie (Klassisch, Wort für Wort, Mittelweg, Schlicht, Collage). WICHTIG: Die Bilder sind mit der ECHTEN Engine und den echten Schriften gerendert (make_caption_previews.py), nicht per CSS nachgebaut. Eine Vorschau, die nur ungefähr passt, wäre schlimmer als keine — man wählt danach aus und bekäme etwas anderes. Erzeugt werden sie einmalig im Render-Container und als JPG mitcommittet (11 Stück, zusammen ~100 KB); zur Laufzeit rechnet nichts. Ändert sich ein Stil in caption_engine.py, muss das Skript neu laufen — sonst zeigt die Vorschau den alten Stand. Zwei Details, die den Ausschluss machen: (1) Alle Bilder bekommen DENSELBEN Ausschnitt, automatisch aus dem Textbereich aller Stile bestimmt — bei individuellem Zuschnitt sähe ein enger geschnittener Stil größer aus, als er ist. (2) Bei "Wort für Wort" verschwindet die ganze Gruppe, sobald das letzte Wort endet; mit kurzen Wortdauern war das Bild schon wieder leer, bevor alle Wörter standen (die ersten Vorschauen waren schwarz).
Caption-Timing: erst die Stimme trennen, dann den Text drauflegen2026-07-28
Bisher wurde der Songtext direkt auf den fertigen MIX gelegt — also auf Gesang PLUS Schlagzeug, Bass und Synths. Das Modell sucht Sprache in einem Klangbrei, entsprechend ungenau sitzen die Wörter. Neu trennt stable-ts erst die Gesangsspur heraus (demucs) und ordnet den Text auf der sauberen Stimme zu. GEMESSEN am 2026-07-28 an einem echten Song, mit zwei voneinander unabhängigen Maßstäben (beide aus der isolierten Gesangsspur gewonnen, also nicht im Kreis gemessen — der Maßstab ist reine Signalmessung, kein zweiter Spracherkenner): Versatz der Zeilenanfänge zum Gesangs-Einsatz 230 → 90 ms; Anteil der Einsätze näher als 150 ms 18 → 64 %; Versatz zum nächsten Silben-Einsatz 69 → 49 ms; Wörter, die in einer Gesangspause anfangen 2 → 0 %. VERWORFEN, jeweils mit Messung statt Bauchgefühl: Sprach-Erkennung (vad=True) macht es SCHLECHTER, sobald die Stimme getrennt ist (90 → 180 ms); Modell "medium" ist minimal besser (90 → 70 ms), kostet aber das 2,7-Fache; refine() (Nachschärfen) braucht das 6-Fache und ändert messbar nichts. PREIS: Die Trennung kostet Rechenzeit — auf dem Pi wuchs ein 45-Sekunden-Song von 46 auf 193 Sekunden. In der Cloud faellt das kaum ins Gewicht: derselbe Song braucht dort mit Trennung 64 Sekunden (8 Kerne statt 4, und die richtige Prozessor-Sorte). Abschaltbar über die Einstellung "stimmeTrennen". STOLPERFALLE beim Einbauen: demucs muss auf Version 4.0.1 festgenagelt werden. Neuere Ausgaben ziehen "sphn" nach, das für den Pi (arm64) kein fertiges Paket hat und einen Rust-Compiler verlangt. Außerdem liest die Trennung über soundfile ein, nicht über torchaudio — das verlangt inzwischen TorchCodec und fällt sonst mit ImportError aus. Nebenbei: Wörter, für die die Zuordnung keine Zeit liefert, fielen bisher stillschweigend raus (im Video fehlte mitten im Satz ein Wort). Sie werden jetzt zwischen ihre Nachbarn eingepasst.
Oberfläche in zwei Abteile geteilt — Reelmaker und Ordner2026-07-28
Vorher war alles eine einzige lange Seite: Song, Einstellungen, Vorschau UND die Liste aller fertigen Videos untereinander. Am Handy hiess das scrollen, bis man beim Gesuchten war. Jetzt zwei Abteile, umschaltbar in der Kopfzeile. "Ordner" zeigt zwei Regale: fertige Reels und die Hintergrund-Clip-Bibliothek, beide als Kachelraster mit Vorschaubild statt als Namensliste — bei Videos erkennt man am Bild in einer Sekunde, was man sucht, am Dateinamen ("Mila_3_Uhr_Naechte_lyrics_std_20260728_104255") nie. Damit die Kacheln nicht für jedes Bild Videodaten laden müssen, legt der Render-Job jetzt ein Standbild (thumb.jpg) neben das fertige Video; die Kachel greift darauf zu. Ältere Reels ohne Standbild behelfen sich weiter mit dem Video selbst (#t=1.5 holt genau ein Bild). Neuer Weg /api/thumb/<id>, dazu /api/clip/<name>, damit sich Hintergrund-Clips überhaupt ansehen lassen. Am Handy sind Abspiel- und Ladeknopf DAUERHAFT sichtbar (ohne Maus gibt es kein "hover") und das Abspiel-Symbol sitzt unten links statt mittig — mittig läge es genau auf der Caption des Reels. Geprüft im echten Browser auf 1440x900, 393x852 und 375x667: Umschalten funktioniert, kein Querscrollen, keine Fehler in der Konsole. NACHGEBESSERT am selben Tag auf Mert-Rückmeldung: Ein Klick auf eine Kachel sprang zuerst ins Abteil "Reelmaker", wo das Video in der dritten Spalte läuft — am Handy sitzt die weit unten, man musste also erst dorthin scrollen. Jetzt spielt das Video an Ort und Stelle im Ordner, in einer Ebene darüber (schließen per Escape, Klick daneben oder Knopf). Nachgemessen im Browser: der Spieler liegt auf allen drei Grössen komplett im Bild, ohne dass die Seite scrollt.
Caption-Engine angeschlossen — sie lief vorher gar nicht2026-07-28
Befund am 2026-07-28: caption_engine.py (vier Commits, sechs Kompositionen, Wort-für-Wort-Timing) wurde von KEINER Datei importiert. Die Arbeit war fertig, aber jeder Render brachte weiter den alten Karaoke-Look — ohne Fehlermeldung, weil nichts sie je aufgerufen hat. Angeschlossen über ein neues Zwischenstück caption_bridge.py, das die Whisper-Wörter in die vier Eingabeformen der Engine übersetzt. Zwei Regeln, die dort fallen: (1) Gruppen brechen an ATEMPAUSEN im Gesang statt stur alle drei Wörter — der alte Schnitt zerriss Sätze mitten im Satz; (2) das Kernwort ist das LÄNGSTE der Gruppe, bei Gleichstand das spätere. Ohne Bedeutungsanalyse ist Wortlänge der beste billige Näherungswert: Füllwörter sind kurz, Bedeutungsträger lang, die Pointe steht meist am Ende. Auswählbar sind 10 Stile im Format lyrics_std (Wort-für-Wort in 3 Varianten, Mittelweg in 3, Schlicht in 3, Collage). "Klassisch" bleibt der alte Look. BEWUSST OHNE stillen Rückfall: wer einen Stil wählt, bekommt ihn oder einen Fehler — genau die stille Falle, die hier schon zweimal zugeschlagen hat. Zwei Dinge fehlten für den Betrieb und sind jetzt drin: pysubs2 (schreibt die ASS-Dateien) stand in keiner Paketliste, und keine der Projektschriften war im Render-Abbild angemeldet — libass hätte sie klaglos durch DejaVu ersetzt, und die Engine misst jedes Wort vorher per fc-match aus, um die Abstände zu rechnen. Der Container prüft die fünf Schriften jetzt beim Bauen und bricht ab, wenn eine fehlt. Nebenbei korrigiert: die Variante "hart" versprach Versalien, schrieb aber in der Schreibweise des Songtexts.
Clip-Erzeugung: OpenArt PixVerse V6, fest auf 540p2026-07-27
Hintergrund-Clips entstehen über OpenArt (Merts Advanced-Abo, 12.000 Credits/Monat) per MCP-Anschluss — kein zweites Abo, keine API-Schluessel. Modell: PixVerse V6, text2video, 9:16, 5 s, ohne Ton. Es ist mit Abstand das guenstigste in OpenArts Liste (das nächste kostet 125 statt 70). FESTGELEGT am 2026-07-27: Aufloesung 540p statt 720p — 50 statt 70 Credits, also 29 % guenstiger. Ein Reel aus fünf Szenen kostet damit 250 Credits (~0,70 $), aus dem Monatskontingent werden 40 statt 29 Reels. Begruendung, warum die niedrige Aufloesung hier NICHT stoert: keine Gesichter im Bild (bewusst vermieden, dort scheitern die Modelle), kein Text im Clip (Captions werden später in voller Aufloesung eingebrannt), dunkles koerniges Material verzeiht Weichheit, und Instagram komprimiert ohnehin stark. Bei einzelnen Schluesselszenen kann man weiterhin auf 720p hochgehen. ACHTUNG: PixVerse lässt keinen festen Zufallswert setzen — derselbe Prompt liefert jedes Mal eine andere Aufnahme, ein sauberer A/B-Vergleich ist damit unmöglich.
Reels statt ganzer Musikvideos2026-07-27
Klarstellung 2026-07-27: Es werden KEINE kompletten Musikvideos gebaut, sondern kurze Reels vom besten Teil eines Songs (Hook), rund 15–25 Sekunden aus 3–5 Szenen. Das ändert die Dramaturgie: nicht Erzaehlbogen, sondern eine Kernszene mit zwei bis drei hinfuehrenden — weniger Schnitte, laengere Einstellungen, die staerkste Einstellung ans Ende. Kosten je Reel bei 540p rund 250 Credits.
Szenen aus dem Songtext statt abstrakter Tapete2026-07-27
Richtungsentscheidung 2026-07-27 nach mehreren verworfenen Anlaeufen: Der Hintergrund ist nicht mehr eine abstrakte Dauerschleife, sondern JEDE Zeile bekommt ihre eigene Szene, die zeigt was gesungen wird. Zwei Lesarten getestet — poetisch (keine Menschen, Raeume erzaehlen) und konkret (ein Mensch, aber NIE ein Gesicht: von hinten, im Gegenlicht, nur Haende). Mert hat die konkrete Fassung gewählt. Zwei Regeln, die den Ausschlag gaben: (1) nie ein Gesicht — dort scheitern die Modelle am zuverlaessigsten, die Vermeidung wirkt wie Regie statt wie Einschraenkung; (2) in JEDEM Prompt dieselbe Bildsprache (gleiche Filmemulation, gleiches Korn, kuehle Schatten mit warmen Lichtern) — ohne das sehen fünf Clips aus wie fünf verschiedene Filme statt wie ein Stück.
Videos ansehen und herunterladen2026-07-27
Früher lagen fertige Reels auf Merts PC in ~/Videos/Reelforge — in der Cloud gibt es keinen Ordner zum Aufmachen. Neu: (1) Kachel "Meine Videos" listet ALLE bisher gerenderten Reels (neueste oben, mit Datum und Größe), je Zeile ein Knopf zum Ansehen und einer zum Herunterladen. (2) Nach dem Rendern läuft das fertige Video direkt im Handy-Rahmen der Vorschau, mit "Zurück zur Vorschau". (3) Die beiden toten Knoepfe der Desktop-Zeit sind ersetzt — "Datei oeffnen" rief den Windows-Explorer, "Export" hatte gar keine Funktion. Server: /api/videos (Liste), /api/video/<id> (ansehen), /api/download/<id> (herunterladen), alle drei mit Bereichsabfragen, sonst lässt sich im Video nicht springen und Safari spielt es gar nicht erst ab. WICHTIG fuers Testen: der Chromium auf dem Pi kann H.264/AAC NICHT abspielen (freie Version ohne Lizenz-Codecs) und meldet dann "DEMUXER_ERROR_NO_SUPPORTED_STREAMS" — das ist KEIN Fehler der App. Byte-Genauigkeit stattdessen per curl gegen das Original prüfen.
Oberflaeche handytauglich gemacht2026-07-27
Die Oberflaeche war ein 3-Spalten-Raster in einer fixierten Buehne — auf dem Telefon unbedienbar. Geloest rein per CSS-Block (@media max-width 900px), die React-Bausteine blieben unangetastet und die Desktop-Ansicht ändert sich um kein Pixel: Spalten untereinander, die SEITE scrollt statt drei Kaestchen, Fenster-Knoepfe und Brotkrumen ausgeblendet, Eingabefelder auf 16px (darunter zoomt iOS beim Antippen rein und zerreisst das Layout), Tippziele mindestens 44px, Rand für die iPhone-Leiste unten. Render-Knopf steht jetzt FEST am unteren Bildschirmrand statt am Ende der Scrollstrecke. STOLPERFALLE dabei: position:fixed wirkte nicht, weil .col.preview einen transform (Einblend-Animation) UND einen backdrop-filter hat — beides macht die Spalte zum Bezugsrahmen, die Leiste landete bei Pixel 3218 statt 754. Der backdrop-filter kommt aus "body.glassy .col.preview", also aus einer staerkeren Regel als ".col.preview" — beide Selektoren müssen genannt werden. Geprüft per echtem Browser-Screenshot auf 393x852 und 375x667: kein Querscrollen, Leiste sichtbar.
Zugang per Passwort statt Google-Login (Google-Login gescheitert)2026-07-27
KORRIGIERT den Eintrag darunter: Der geplante Google-Login (IAP) lässt sich bei diesem Konto nicht in Betrieb nehmen. IAP antwortete dauerhaft mit 502 und der Meldung "Empty Google Account OAuth client ID(s)/secret(s)" — Google erzeugt den noetigen Anmelde-Schluessel bei privaten Gmail-Projekten ohne Organisation nicht automatisch, und die Schnittstelle, über die man ihn früher anlegen konnte (IAP OAuth Admin API), hat Google im Maerz 2026 abgeschaltet. Von Hand anlegbar waere er noch, ist aber Klick-Fummelei mit Henne-Ei-Problem bei der Weiterleitungs-Adresse. Entscheidung mit Mert am 2026-07-27: stattdessen ein Passwort vor der App. Umsetzung in cloud_server.py — Passwort und Sitzungsschluessel liegen im Google-Tresor (Secret Manager) und werden dem Dienst als Umgebungsvariable hereingereicht, NICHT im Code. Cookie nur über HTTPS, 30 Tage gueltig, Bremse nach 8 Fehlversuchen (5 Min Sperre) gegen Durchprobieren. Umstieg auf Google-Login bleibt jederzeit möglich.
Oeffentliche Web-App statt Tailscale-only2026-07-27
Zuschnitt geändert (2026-07-27, Mert-Wunsch): ReelForge soll auch auf Geraeten ohne Tailscale laufen. Daher liegt jetzt AUCH die Oberflaeche in der Cloud (Cloud-Run-Dienst "reelforge", Adresse https://reelforge-1085372544136.europe-west1.run.app), gesichert per Google-Login (IAP, seit 2026 ohne teuren Lastverteiler möglich, kostenlos). Nur Merts Konto ist freigeschaltet. WICHTIG: Oberflaeche und Render laufen in ZWEI getrennten Containern — Dockerfile.ui ist schlank (~250 MB, Bau 45 s, startet sofort), Dockerfile.render bleibt der schwere Brocken (4,9 GB, Kaltstart ~80 s), der nur zum Rendern hochfaehrt. Ein gemeinsamer Container haette bedeutet: 80 Sekunden Wartezeit, nur um die Oberflaeche zu oeffnen.
Cloud-Aufbau steht — gemessene Zahlen2026-07-27
Projekt reelforge-503709 (Region europe-west1/Belgien, EU-Datenhaltung). Bausteine: Zwischenspeicher gs://reelforge-503709-jobs (Ordner je Auftrag unter jobs/<id>/in bzw. /out, gemeinsame Clip-Bibliothek unter clips/), Render-Job "reelforge-render" (8 Kerne, 16 GB, existiert nur während er läuft), Oberflaechen-Dienst "reelforge". Zwei eng geschnittene Zugangskonten: reelforge-render darf NUR an den Zwischenspeicher, reelforge-ui darf an den Zwischenspeicher und den Render-Job wecken. Ausgabenlimit 5 EUR mit Mail-Warnung bei 50/90/100 %. GEMESSEN am 15-s-Testvideo: Cloud 48 s gegen Pi 105 s (Silbenzuordnung 21 s gegen 74 s), plus ~85 s Kaltstart beim ersten Render. Die alten Windows-Dialoge (Ordner-/Dateiauswahl per PowerShell, Explorer oeffnen) sind ersatzlos raus — im Browser sinnlos; Clips kommen jetzt aus der Bibliothek.
Umzug in die Cloud — Pi ist der Kopf, Cloud der Muskel2026-07-27
Entscheidung 2026-07-27: ReelForge verlaesst den Windows-PC. Neue Aufteilung — der Pi haelt Oberflaeche, Warteschlange und Dateien (Last nahe null) und uebergibt das fertige Video direkt an instapi; das schwere Rechnen läuft als Cloud-Job, der nur während des Renderns existiert (Google Cloud Run, Freikontingent 180.000 Prozessor-Sekunden/Monat ≈ 90 Musikvideos). Grund gegen den Pi als Render-Maschine: gemessene Hitzekurve unter Dauerlast — nach 2,5 Min reisst er die Hitzegrenze, nach knapp 4 Min drosselt er den Takt (2400 → 2256 MHz), Luefter da schon bei 10.100 U/min am Anschlag. Für ein 60-s-Reel (≈2 Min Arbeit) reicht der Pi, für geplante 3-Minuten-Videos mit KI-Clips (≈6 Min Volllast) nicht. Wichtig: die KI-Videos entstehen weiterhin bei Kling/Seedance/Higgsfield, nicht lokal — es kommt ein fertiges MP4 an. Deshalb wird KEINE Grafikkarte gebraucht, nur mehr CPU-Zeit.
Render-Kern von der Oberflaeche getrennt2026-07-27
Neu: render_cli.py rendert ohne Flask und ohne Fenster — nimmt dieselbe Job-JSON, die die Oberflaeche schon heute an /api/render/start schickt, schreibt Fortschritt in eine Statusdatei. Die bestehenden Renderer wurden dafür NICHT angefasst (server.py ist dank __main__-Schutz importierbar, ohne den Server zu starten) — bewusst risikoarm gehalten. Ausgabe-/Upload-Ordner jetzt per Umgebungsvariable (REELFORGE_OUTPUT_DIR / REELFORGE_UPLOAD_DIR) umstellbar, damit derselbe Code lokal und im Container läuft. Dazu Dockerfile.render: ffmpeg, Schriften, Whisper-Modelle fest eingebacken (spart ~40 s Anlauf pro Render), torch bewusst als CPU-Variante (sonst ~2,5 GB Treiber-Ballast). Auf dem Pi mit 15-s-Testsong geprüft: gueltiges 1080x1920-MP4, Umlaute korrekt, 9 Sekunden.
STILLE FALLE: ReelForge rendert bei fehlenden Paketen einfach schlechter weiter2026-07-27
Beim Containerbau zweimal reingefallen — unbedingt merken. Die Renderer fangen ImportError UND SystemExit ab und fallen dann auf _render_simple_caption_video zurück (schwarzer Hintergrund, keine Silbenzuordnung). Es kommt ein gueltiges MP4 raus, es bricht NICHTS ab — man merkt nur an der Optik, dass die gute Engine gar nicht lief. Zwei konkrete Ursachen gefunden und behoben: (1) torchaudio von PyPI passt nicht zur CPU-Variante von torch → laedt nicht → stable-ts tot; beide müssen aus derselben Quelle (download.pytorch.org/whl/cpu) kommen. (2) lyrics_video_ui.py importiert tkinter beim Laden (war mal ein Fenster-Programm, 68 Verwendungen) → ohne Paket "tk" im Abbild scheitert der Import. PRUEFREGEL nach jeder Änderung am Abbild: Import von lyrics_video_ui testen und auf "Align: 100%" im Renderlog achten — fehlt das, lief nur der Notfall-Renderer.
ReelForge liegt jetzt im Git-Repo2026-07-27
Der komplette Ordner war unversioniert — einzige Sicherung war Syncthing (und der PC ist meist aus). Am 2026-07-27 als Commit ins instapi-Repo aufgenommen: Backend, React-UI, alle Renderer, Schriften, Sounds. Arbeitsdateien (uploads/, _previews/, .stfolder/) bleiben per .gitignore draussen. Änderungen kuenftig mitcommitten.
Formatzahl korrigiert: 4 statt 3 aktiv2026-07-27
Manifest zaehlte 3 aktive Formate, im Code sind es 4 — spotify (spotify_video.py, _render_spotify in server.py) war nie eingetragen. Aktiv: lyrics_std, story_scroll, whatsapp_chat, spotify. Gesperrt bleiben pinterest, nachtfahrt, blackreveal.
WhatsApp-Chat: Feinschliff Animation + echte Sounds2026-06-22
Runde 2 Feedback (2026-06-22): (1) Drei Punkte "..." werden jetzt einzeln getippt (Auslassungszeichen … → drei echte Punkt-Tasten). (2) Tastatur slided smooth hoch (ease-out, 0,3s) statt schlagartig. (3) "tippt…" im Header während sie schreibt. (4) Echte WhatsApp-Sounds eingebaut: Empfangston (original WhatsApp-Notification), Sende-Swoosh, Tipp-Klicks pro Taste — Dateien in reelforge/sounds/, gemischt via build_chat_audio() zeitgenau zu den Szenen-Events. (5) Szene endet jetzt mit echtem SENDE-Moment (Antwort wird zur Bubble + Sende-Sound). _render_chat_frames gibt (n, switch, events) zurück. Profilbild = KI-Gesicht via Soul 2.0.
WhatsApp-Chat realistischer (Optik + Animation)2026-06-22
Format whatsapp_chat ueberarbeitet (2026-06-22): (1) echte Roboto-Schrift FEST mitgeliefert in reelforge/fonts/ (vorher Fallback auf DejaVu/Arial → Hauptgrund für unechten Look), inkl. NotoColorEmoji für Emojis. (2) Neuer 2025-Bubble-Stil: voll abgerundete Pillen OHNE Tail/Spitze, Schrift von +30% auf echtes Mass runter, Zeitstempel inline am Textende statt eigener Zeile. (3) Animation fluessig: FPS 30→60, Tipp-Punkte mit weichem 1,4-s-Puls, Nachricht blendet weich aus den Punkten ein. Farben waren schon exakt korrekt. Visuell auf dem Pi per Einzelframe+Clip geprüft. (4) Pixelvergleich gegen echten Android-Dark-Screenshot: Eingabeleiste verkleinert, Blasen-Padding enger, Timestamp baseline-buendig, Datums-Chip echter, Header-Trennlinie. (5) Bestehender Chat-VERLAUF von Anfang an sichtbar (nicht erste Nachricht). (6) Tippen menschlich: variable Geschw., Mini-Denkpausen, gelegentl. Tippfehler+Backspace-Korrektur (_simulate_typing). (7) Echte Android-Icons via Material-Icons-Font (Zurueck/Video/Anruf/ Menue/Senden/Mic/Klammer/Kamera/Smiley/Doppelhaken/WLAN/Signal/Akku) statt handgezeichnet. (8) Rundes Profilbild im Header (avatar_path; Default = KI-generiertes aesthetic Foto via Higgsfield Soul 2.0, passend zur deutschen Teen-Zielgruppe — kein GPT Image 2). (9) Langer Mehrtage-Verlauf mit Datums-Trennern (MO/DO/GESTERN/HEUTE), Chat UNTEN verankert wie echt. (10) Emoji-Fix: NotoColorEmoji ist Bitmap-Font (nur 109px) → in 109 rendern + skalieren. Bewusst NICHT geändert: exakte Hex-Farben (sind schon WhatsApp-genau). OFFEN (Stufe 2): Laenge auf 25s, Stille-Intro-Hook mit Chat-Sounds, Song-Drop.
Auto-Sync Pi<->PC via Syncthing statt scp2026-06-22
Schluss mit manuellem scp: Syncthing haelt den reelforge-Ordner zwischen Pi und Merts Windows-PC automatisch gleich (Zwei-Wege, sekundenschnell). Pi-Seite am 2026-06-22 eingerichtet: syncthing 1.29.5 als System-Dienst syncthing@pi (läuft nach Reboot weiter), Ordner-ID "reelforge" → /home/pi/instapi/scripts/reelforge, __pycache__ via .stignore ignoriert. Pi-Geraete-ID: JVZPKT3-LSRNH3I-R2CTGZD-XFZSLOY-32QCDTA-CPY4SZU-VWNRBKB-5BHO2Q3. Offen: Mert muss Syncthing auf dem PC installieren + koppeln (siehe tasks).
Doppelten Ordner reelforge_pc_stand aufgeloest2026-06-22
Unter /home/pi lag eine veraltete Kopie (Stand 29.05. 16:09) als reelforge_pc_stand — die landete nach einem Fehler faelschlich auf Merts PC, weniger Formate (kein Spotify), ältere UI. Am 2026-06-22 geprüft: jede gemeinsame Datei ist im echten Ordner neuer. Nur der Song-Upload + fontconfig waren unikat → in den echten Ordner kopiert. Alte Kopie geparkt als /home/pi/reelforge_pc_stand_ALT_kann_weg. EINZIGER gueltiger Pfad ist jetzt /home/pi/instapi/scripts/reelforge.
Renderer-Module ins Paket kopiert2026-05-29
lyrics_video_ui.py und whatsapp_video.py lagen früher ausserhalb des reelforge-Ordners und wurden vom scp -r nie mitkopiert — auf Windows lief deshalb nur der ASS-Fallback. Seit 2026-05-29 liegen Kopien direkt im Ordner. Jetzt werden die echten Renderer mitgeliefert.
pywebview statt reinem Browser2026-05-29
React-UI läuft als natives Programmfenster (pywebview), nicht im Browser-Tab. Grund: polierte Look mit Glow-Effekten und Live-Animation ist nur so möglich. Tkinter-Backup (reelforge_ui.py) bleibt als Notfall-Variante ohne pywebview.
Windows-FFmpeg-Fixes eingepflegt2026-05-29
server.py enthaelt 6 Fixes für Windows: Fontconfig-Patch, Pfad-Escaping, Umlaut-Sanitizing, Render in %TEMP%, ASS statt drawtext, subprocess ohne text=True. Lokal auf dem Pi getestet — Windows-Test steht noch aus.