Wie ich aus Telegram-Fotos automatisch fertige Reels baue
Ich mag Reels. Ich mag sie nicht bearbeiten. Lange jedenfalls nicht mehr, seit ich mir einen Workflow gebaut habe, der das für mich übernimmt.
Das Prinzip ist denkbar simpel: Ich schicke Fotos (oder ein Video) mit einer Caption an meinen Telegram-Bot, tippe /start und kurze Zeit später kommt ein fertiges 9:16-Reel zurück. Mit animiertem Text-Intro auf dem ersten Bild und weichen Crossfade-Übergängen zwischen den Bildern.
Das Ganze läuft vollautomatisch über n8n, Remotion und FFmpeg.
Was der Workflow genau macht
Ich kann drei Arten von Medien schicken:
- Ein einzelnes Foto bekommt ein animiertes Text-Intro via Remotion und landet als ein Segment im fertigen Reel.
- Ein Foto-Album (bis zu 10 Bilder) bekommt ebenfalls ein Remotion-Intro auf dem ersten Bild, danach werden alle Bilder mit FFmpeg und xfade-Übergängen zusammengesetzt.
- Ein einzelnes Video wird auf 1080×1920 skaliert und gecroppt.
Das Besondere: Kein Polling, kein Cronjob. Der Workflow ist rein event-getrieben. Er wartet auf den Telegram-Trigger und macht dann seinen Job.
Der technische Ablauf im Überblick
Wenn ich Fotos schicke, werden sie mit ihrer Caption in einem DataTable-Buffer zwischengespeichert. Der Buffer wartet, bis ich /start schicke. Erst dann holt sich der Workflow alle Medien für diese Gruppe, erstellt ein Arbeitsverzeichnis und startet das Rendering.
Für das erste Bild läuft das so: Bild wird als Base64 an meinen Remotion-Service geschickt, der rendert daraus ein seg_0.mp4 mit animiertem Text. Alle weiteren Bilder werden direkt mit FFmpeg als Standbild auf 4 Sekunden gestreckt. Am Ende werden alle Segmente mit einem 0,5-Sekunden-Crossfade zusammengemischt, der Offset liegt bei je 3,5 Sekunden pro Bild.
Das fertige Reel kommt dann direkt per Telegram zurück, das Arbeitsverzeichnis und die Buffer-Einträge werden aufgeräumt.
Remotion und das Text-Intro
Der Remotion-Service läuft als separater Container und ist per http://remotion-render:3000 erreichbar. Ich schicke ihm das Bild als Base64, den Caption-Text, die gewünschte Dauer und den MIME-Type. Zurück kommt ein fertiges MP4 mit Slide-Up und Fade-In-Animation, Inter Medium in 72px, weiß mit Schatten und einem dunklen Overlay mit 35% Deckkraft.
Das sieht gut aus und ich muss nichts daran machen.
Was ich auf dem Weg gelernt habe
Ein paar Dinge haben beim Bauen mehr Zeit gekostet als gedacht:
SplitInBatches ist nicht selbsterklärend. Output 0 ist „done“, Output 1 ist „loop“. Wenn man das vertauscht, wundert man sich eine Weile warum der Loop nie endet.
DataTable update gibt 0 Items zurück. Das heißt, wenn ich nach dem Markieren weitermachen will, brauche ich ein Fan-out-Pattern. Der „Mark Processed“ Zweig ist ein dead-end, der zweite Zweig läuft weiter.
Der Telegram GetFile-Node funktioniert nicht. Er injiziert automatisch download: true, was zu einem „invalid file_id“-Fehler führt. Die Lösung: Direkt per HTTP Request an die Telegram-API gehen.
require('fs') geht im Task Runner nicht. Wer Base64 braucht, macht das über die Shell: base64 -w 0 datei.jpg.
Remotion erzeugt einen Audio-Stream. Das heißt, alle FFmpeg-Kommandos brauchen -an, sonst meckert FFmpeg beim Merge. Außerdem hat Remotion eine andere Timebase (1/90000) als FFmpeg, weshalb das erste Segment nochmal re-encodiert werden muss bevor es in den Merge geht.
Warum ich das alles dokumentiere
Weil ich in drei Monaten wieder drin sitze und nicht mehr weiß, warum irgendwas so gebaut ist wie es ist. Und weil vielleicht jemand anderes das gleiche Problem hat und sich die Stunden sparen kann die ich reingesteckt habe.
Falls du Fragen hast oder einen ähnlichen Workflow baust, schreib mir gerne.
