New
Apps 575Makers 575This week 575
Promote your app−30%
Automated Video Publishing Pipeline screenshot 1
1views in
the last 30 days

Automated Video Publishing Pipeline

An end-to-end n8n automation that turns one raw video into finished, platform-specific...

Built with Claude Code

by nikita-automation · from DEGermany · added Oct 29, 2026
no ratings0 comments
Open app Code

Automated Video Publishing Pipeline

Kurz auf Deutsch: n8n-Automatisierung, die aus einem Rohvideo fertige Posts für LinkedIn, Instagram und YouTube macht: Übersetzung ins Deutsche mit HeyGen, Schnitt und Untertitel mit Submagic, Texte von Claude, Veröffentlichung über Blotato nach einer Freigabe in Telegram. Mit ehrlicher Kostenanalyse, warum die Pipeline wieder abgeschaltet wurde. Betriebsdetails auf Deutsch weiter unten.

An end-to-end n8n automation that turns one raw video into finished, platform-specific social-media posts. A Telegram bot receives a raw clip → HeyGen translates it into German with voice preservation and lip-sync → Submagic edits it (animated captions, zooms, silence removal) → Claude writes captions tailored to each platform → a Telegram preview with buttons → publishing to LinkedIn, two Instagram accounts, a Facebook page, TikTok and YouTube Shorts via Blotato. State is tracked in Google Sheets.

The only manual work: record the video, send it to the bot, tap one approval button.

Built as a personal project for a public "200 Days of Automation" challenge.

Status: built, shipped, measured — and then deliberately switched off. The pipeline ran end to end and published real posts. It was retired because the numbers didn't work: the automated path cost ~8× the manual one, almost entirely on a single step. See Cost analysis below. The stack stays deployed and can be reactivated at any time.

Stack: n8n (self-hosted, managed via REST API) · Python · Telegram Bot API · HeyGen · Submagic · Anthropic Claude API · Blotato · Google Sheets & Drive · Webhooks · OAuth2

Architecture

flowchart TD
    U([User]) -- video / button tap --> D[D · Telegram dispatcher<br/>the bot's single webhook]
    D -- message --> A[A · Intake &amp; translate]
    D -- callback --> C[C · Buttons]

    A -- video to raw/ --> DR[(Google Drive)]
    A -- row, status --> SH[(Google Sheets)]
    A -- translate --> HG{{HeyGen API}}
    A -- edit + webhook --> SM{{Submagic API}}

    SM -- "done" webhook --> B[B · Captions &amp; preview]
    B -- final to final/ --> DR
    B -- generate captions --> CL{{Anthropic Claude}}
    B -- preview + buttons --> U

    C -- "Publish now" --> P[P · Publishing engine]
    C -- "Queue" --> SH
    F[F · Scheduler<br/>daily 18:00] -- clip whose date is due --> P
    P -- 6 posts --> BL{{Blotato API}}
    BL --> SN([LinkedIn · Instagram×2 · Facebook · TikTok · YouTube])

    A -. error .-> E[E · Error handler]
    B -. error .-> E
    P -. error .-> E
    E -- ⚠️ alert --> U
    E -- append --> SH

Row lifecycle: received → translating → editing → awaiting_approval → (scheduled) → publishing → done / done_partial / rejected / error

Engineering highlights

  • Modular design — seven focused workflows instead of one monolith: a single Telegram dispatcher (a bot may hold only one webhook), a reusable publishing engine shared by the "publish now" button and the scheduler, and a dedicated error workflow.
  • Idempotency — every side-effecting step is guarded by the row status in Google Sheets. A repeated Submagic webhook is a no-op; a double tap on "Publish" is rejected; the scheduler publishes exactly one clip per day.
  • Reliability — automatic retries (3× with backoff) on every network node; independent publishing per platform (onError: continue), so one failing network doesn't block the others → done / done_partial. 40-minute polling timeout on the HeyGen translation.
  • Scheduling — a "Queue" button assigns the next free date; a daily cron publishes whatever is due, so five clips shot in one day roll out one per day with no further taps.
  • Structured AI output — Claude is asked for strict JSON (LinkedIn / Instagram / YouTube copy + title); the response is parsed with a markdown-fence cleanup and one retry on invalid JSON.
  • Infrastructure-as-code — the whole stack is deployed and updated through the n8n Public API by idempotent Python scripts (scripts/); re-running updates in place instead of creating duplicates.
  • Security — no secrets in the repo (see below).

Cost analysis: why this pipeline is switched off

Measured on a real 1:24 clip, not estimated from price pages:

Item Per clip Per month (1 clip/day)
HeyGen video translation (API, lip-sync) $2.79 ~$84
Submagic captions (Pro $39/mo covers 40 clips) ~$0.98 $39
Claude captions (2 calls, ~3k tokens) $0.023 $0.70
Blotato multi-platform publishing — ~$30
Total ≈ $3.80 ≈ $115

The finding that killed it: HeyGen bills its API from a USD wallet that is entirely separate from the web subscription. The same translation of the same file costs:

  • $2.00 / min through the API — $0.0333/sec, pay-as-you-go
  • $0.24 / min through the web UI — 5 credits/min, and the Creator plan gives 600 credits for $29/mo

That is an 8× premium for automating three clicks. No amount of workflow engineering closes that gap, because the gap isn't in the workflow — it's in the vendor's pricing model. At one clip a day the automation was burning roughly $85/month to save about 60 seconds of manual work per day.

Decision: translate in the HeyGen web UI on a subscription, edit and schedule in Opus Clip, publish from there. The n8n stack stays deployed and deactivated rather than deleted.

What would make it viable again, roughly in order of impact:

  1. Record in German directly — removes the $2.79 line entirely and cuts two vendors out of the chain. The dubbing step exists only because the source is in another language.
  2. Drop lip-sync — audio-only dubbing runs $0.10–0.50/min across the market, and works fine for a screen-recording format where the face isn't the shot.
  3. Swap the captioning vendor — ZapCap's API is $0.10/min pay-as-you-go; Submagic's API is Business-tier only and works out to ~$0.69/min.
  4. Self-host the whole chain — WhisperX → Claude → a cloned voice → LatentSync on a rented GPU lands around $0.10–0.25 per clip, at the cost of owning a much larger surface area.

Takeaway: the engineering was never the bottleneck. Unit economics should have been measured on clip one, not clip six — and a vendor's API price is a separate product from its subscription price, not a discount on it.

Repository structure

File Purpose
workflows/D-telegram-dispatcher.json The bot's single Telegram trigger. Routes messages → workflow A, button callbacks → workflow C
workflows/A-intake-translate.json Accepts a video (up to 1.9 GB via a self-hosted Bot API server, or a Drive link), uploads to Drive raw/, writes the Sheets row, runs the HeyGen translation with polling, saves to translated/, creates the Submagic project
workflows/B-edit-texts-approval.json Submagic webhook → Drive final/ → Claude captions (strict JSON + 1 retry) → Sheets → Telegram preview with buttons
workflows/C-callbacks-publish.json Button handling: publish (now, via engine P), queue (next free date), regen (new caption variant), reject
workflows/P-publish-engine.json Publishing engine (Blotato media upload + 6 independent posts). Called by the "Publish now" button and by scheduler F
workflows/F-scheduler.json Daily at 18:00 (Europe/Berlin) publishes one scheduled clip whose date is due
workflows/E-error-handler.json Error workflow: Telegram alert + append to the log sheet
scripts/deploy.py Idempotent import/update of every workflow via the n8n Public API
scripts/setup_credentials.py Creates n8n credentials from .env
scripts/setup_google.py One-shot creation of the Google Sheet (with headers) and Drive folders
.env.example Environment-variable template

Security

  • No secrets in the repository. Every key (Telegram, HeyGen, Submagic, Anthropic, Blotato, n8n API) lives only in .env, which is excluded via .gitignore. Workflow JSON never hardcodes a key — nodes reference {{ $env.X }} or an n8n credential, and the credentials themselves are not stored in the files.
  • Start from the template: cp .env.example .env, then fill in your own values.
  • scripts/creds.json (internal n8n credential IDs) is also git-ignored — it is generated locally on the first run of setup_credentials.py.
  • In n8n the keys sit in the encrypted credential store; idempotency and double-publish protection are enforced by the row status in Google Sheets.

License

MIT — see LICENSE.


Betrieb und Details (auf Deutsch)

Umsetzung in n8n: Ein Telegram-Bot nimmt das Rohvideo auf Russisch entgegen → HeyGen übersetzt es ins Deutsche (mit Stimmerhalt und Lippensynchronisation) → Submagic schneidet → Claude schreibt die Texte für jede Plattform → Vorschau in Telegram mit Buttons → Veröffentlichung über Blotato. Buchführung in Google Sheets. Handarbeit: Video aufnehmen, an den Bot schicken, einen Button tippen.

Status: funktioniert, ist aber abgeschaltet. Die Pipeline hat den kompletten Durchlauf geschafft und echte Posts veröffentlicht. Abgeschaltet wurde sie nicht wegen eines Fehlers, sondern aus wirtschaftlichen Gründen: Der automatische Weg war etwa 8-mal teurer als der manuelle, und fast die gesamte Differenz entfällt auf einen einzigen Schritt. Details mit Zahlen im Abschnitt Cost analysis. Der Stack bleibt deployt und lässt sich durch Aktivieren der Workflows wieder einschalten.

Kurz gesagt: Die HeyGen-API wird aus einem separaten Dollar-Guthaben bezahlt und hat mit dem Web-Abo nichts zu tun. Dieselbe Übersetzung derselben Datei kostet über die API 2,00 $/Min., im Webinterface mit dem Creator-Tarif (29 $/Monat für 600 Credits, 5 Credits/Min.) 0,24 $/Min. Ein Video kostete in der automatischen Pipeline ≈3,80 $, davon 2,79 $ für HeyGen. Aktueller Ablauf: Übersetzung im HeyGen-Webinterface per Abo, Schnitt und Posting in Opus Clip.

Google Sheets

Eine Tabelle mit zwei Blättern.

Blatt pipeline, erste Zeile sind die Spaltenköpfe (genau diese Namen):

day_number | date_received | raw_url | translated_url | final_url | transcript_de | caption_linkedin | caption_instagram | caption_instagram_2 | caption_youtube | title_youtube | status | scheduled_date | submagic_project_id | post_urls | error_log

caption_instagram_2 ist ein optionaler eigener Text für ein zweites Instagram-Konto (leer heißt: caption_instagram wird verwendet).

Blatt log (für Fehler): timestamp | workflow | node | error.

Veröffentlichungs-Warteschlange: Der Button „🗓 In die Warteschlange“ setzt das Video auf das nächste freie Datum (scheduled_date, eine Veröffentlichung pro Tag). Der Planer F veröffentlicht täglich um 18:00 das Video, dessen Datum erreicht ist. Wird nach 18:00 für „heute“ eingereiht, erscheint das Video beim nächsten Lauf (morgen). Der Button „✅ Jetzt veröffentlichen“ veröffentlicht sofort.

Skripte (scripts/)

Idempotentes Deployment über die n8n Public API (ein erneuter Lauf aktualisiert, statt Duplikate anzulegen):

  • deploy.py: Import und Aktualisierung aller Workflows, Zuordnung der Credentials, Setzen der IDs von Sub-Workflows und Error Workflow, Inline-Einsetzen nicht geheimer $env-Werte.
  • setup_credentials.py: legt die Credentials in n8n aus .env an (Telegram sowie Header-Auth für HeyGen/Submagic/Anthropic/Blotato).
  • setup_google.py: legt einmalig die Google-Tabelle (pipeline + log) und die Drive-Ordner (raw/translated/final) an und schreibt ihre IDs in .env.

Deployment

1. Umgebungsvariablen

.env.example nach .env kopieren, ausfüllen und n8n zur Verfügung stellen (selbst gehostet: über die Prozessumgebung bzw. docker-compose). Die Nodes lesen Werte über {{ $env.X }}, die Skripte in scripts/ direkt aus .env.

n8n Cloud: Dort ist $env nicht verfügbar. $env.X durch n8n Variables ($vars.X, Pro-Plan) ersetzen oder die Werte direkt in die Nodes eintragen.

2. Credentials in n8n

  • Telegram API: Bot-Token von @BotFather → alle Telegram-Nodes.
  • Google Drive OAuth2 und Google Sheets OAuth2 → alle Drive- und Sheets-Nodes.

Die API-Schlüssel für HeyGen/Submagic/Anthropic/Blotato gehen als Header aus $env in die HTTP-Nodes (oder in Header-Auth-Credentials; setup_credentials.py erledigt das automatisch).

3. Workflows importieren

python3 scripts/deploy.py importiert bzw. aktualisiert alle Workflows und setzt die Verknüpfungen. Alternativ manuell (Workflows → Import from File). Nach dem Import:

  1. In D zeigen beide Nodes „Запустить …“ (Starten) auf A und C (das Skript setzt die IDs selbst).
  2. Error Workflow für A–F ist „E — Обработчик ошибок“ (Fehlerbehandlung), ebenfalls vom Skript gesetzt.
  3. In B die Production-URL der Webhook-Node (.../webhook/submagic-done) → SUBMAGIC_WEBHOOK_URL.
  4. D, B, A, C, P aktivieren (F erst, wenn Blotato verbunden ist).

4. API-Verträge prüfen

Vor dem ersten echten Lauf je eine Anfrage schicken und die Antwortfelder abgleichen:

  • HeyGen: POST /v2/video_translate → data.video_translate_id; GET /v2/video_translate/{id} → data.status (success/failed), data.url. output_language ist der Sprachname (German), Liste: GET /v2/video_translate/target_languages.
  • Submagic: POST /v1/projects (x-api-key), Felder title, language, videoUrl, templateName, webhookUrl, Schnitt-Flags magicZooms/removeBadTakes. In GET /v1/projects/{id} stehen downloadUrl und words[] (Transkript wortweise; ohne silence zusammengefügt).
  • Blotato: POST /v2/media (blotato-api-key, Body {url}) → {url}; POST /v2/posts mit {post: {accountId, target: {targetType}, content: {platform, text, mediaUrls}}}. Für YouTube gehören title und privacyStatus in target.
  • Claude: Modell claude-sonnet-4-6, POST /v1/messages.

5. Schrittweise einschalten

  1. Annahme und Buchführung: D + A; Drive-Ordner raw/ und Zeile in Sheets prüfen.
  2. HeyGen: Übersetzung und translated/, Status editing.
  3. Submagic: Untertitel-Vorlage vorher im Interface anlegen (Name → SUBMAGIC_TEMPLATE); Webhook B und final/ prüfen.
  4. Claude: Texte in Sheets und Vorschau in Telegram.
  5. Buttons: ✏️ (neue Variante) und ❌ (rejected).
  6. Blotato: zuerst Test- oder private Konten, für YouTube privacyStatus: "private" in der Node „Пост: YouTube“. Danach die echten Konten.

Nichtfunktionale Anforderungen: wie sie erfüllt sind

  • Idempotenz: B prüft status = editing (Wiederholung → NoOp); C und P prüfen vor der Veröffentlichung status = awaiting_approval.
  • Retries: Netzwerk-HTTP-Nodes mit 3 Versuchen und 5 s Pause. Veröffentlichungen mit onError: continue, damit ein Fehler auf einer Plattform die anderen nicht blockiert.
  • HeyGen-Timeout: 40 × 60 s, danach Fehler an E.
  • Schlüssel: nur in $env bzw. Credentials.

Was im Echtbetrieb kaputtging und wie es behoben wurde

Alles Folgende kam bei echten Videos ans Licht, nicht in Tests. Ich lasse es bewusst stehen, es ist der nützlichste Teil des Projekts.

  • 20-MB-Limit der Telegram Bot API. Gelöst mit einem eigenen Bot-API-Server in Docker auf dem VPS (TELEGRAM_LOCAL=1, benanntes Volume). Im Local-Modus liefert getFile einen Pfad im Container, deshalb sieht die Kette in A so aus: HTTP getFile → Code (Pfad auf dem Host) → Execute Command cp nach /root/.n8n-files → Lesen von der Platte. Das Limit liegt jetzt bei 1,9 GB. Ein direkter Bind-Mount ist nicht möglich, weil Docker per Snap installiert ist.
  • Drive-Links uc?export=download brechen bei Dateien über ~25 MB: Sie liefern HTTP 303 und null Bytes. curl -L folgt der Weiterleitung (deshalb liefen manuelle Tests durch), Blotato auch, der Uploader von Submagic aber nicht. Überall ersetzt durch drive.usercontent.google.com/download?export=download&confirm=t&id=…, das sofort 200 und die Datei liefert.
  • Telegram sendVideo per URL lädt einen fremden Link mit 57 MB nicht (wrong type of the web page content). In B kam eine Code-Node „Прикрепить видео“ (Video anhängen) dazu, die Vorschau geht als Binärdatei raus (binaryData: true), Limit wieder 1,9 GB über den lokalen Server.
  • Automatische Wiederholungen bei kostenpflichtigen POSTs verursachen stille Mehrkosten. retryOnFail stand auf den Nodes, die Aufträge bei HeyGen und Submagic anlegen: Ein Netzwerkfehler nach Annahme des Auftrags hätte 2–3 Abbuchungen hintereinander ausgelöst. Bei beiden POSTs entfernt, bei den Abfrage- und Download-GETs belassen.
  • Google OAuth lief alle 7 Tage ab, solange der Consent Screen im Testing-Modus war. Abhilfe: Consent Screen in Production veröffentlichen.
  • Die Tagesnummer wurde als max(day_number) + 1 berechnet und lief der Realität davon (Tag 55 wurde als 5 eingetragen). Jetzt wird sie aus dem Transkript gelesen (Zahl neben dem Wort Tag), der Zähler ist nur noch Fallback.
  • Die Blotato-Verträge waren unvollständig. Instagram antwortet mit 422 bei mehr als 5 Hashtags (Absicherung: deterministisches Kürzen im Code, nicht auf das Modell verlassen); Facebook braucht in target eine pageId statt accountId (aus GET /v2/users/me/accounts/<id>/subaccounts); TikTok verlangt isBrandedContent, isYourBrand, isAiGenerated.
  • Der Error Workflow war deaktiviert und lief kein einziges Mal, deshalb blieben einige Ausfälle unbemerkt. Er muss genauso aktiviert werden wie die anderen.
  • Die Antwort von Blotato enthält die endgültigen Post-URLs nicht sofort (die Veröffentlichung ist asynchron), und GET /v2/post-submissions/<id> liefert später 404. Die Veröffentlichung bestätigt man zuverlässiger direkt in den Netzwerken.

Eine Frage blieb offen: HeyGen hat für einen Auftrag mehrmals abgebucht (drei Zeilen in der Abrechnung für eine video_translate_id), und die n8n-Daten erklären das nicht.


Autor: Nikita Rozumnyi · KI-Automatisierung für Unternehmen · nikita-automation.de · LinkedIn · alle Projekte

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#content · #publishing · #automation