Narrated shorts that post themselves
Pick a niche and get a voiced, captioned Italian short, sent to TikTok drafts and Instagram.
- Python
- Flask
- OpenRouter
- Edge TTS
- faster-whisper
- FFmpeg
- TikTok API
- Postiz
What it makes
Real videos produced by the system.
- 13narrated videos from the in-house engine, 24 to 70 seconds each
- 6 + 1TikTok drafts uploaded and Instagram post created end to end in a test run
- 33videos generated from the panel in its first eight days, across both engines
One niche in, a narrated short out
Pick a niche: it writes the script, voices it in Italian, times every word, covers the voice with stock footage and sends the MP4 to TikTok drafts and Instagram.
Tap a block to see what it does.
- Input
- AI
- Logic
- Output
Input
Control panel
Flask on port 5000: pick a niche, type a topic or leave it empty, run one or N in a row; jobs run in background threads, polled every 1.5 s.
- Flask
- JavaScript
Hands to Script writer
AI
Script writer
Free 550B model on OpenRouter; the niche profile sets hook and structure; first line is the hook, 6–8 sentences; rewritten longer if the voice is under 30 s.
- OpenRouter
Gets from Control panelHands to Italian voice, Stock footage
AI
Italian voice
Edge TTS with one of four Italian neural voices, picked at random per video; the length of the voice track sets the length of the video.
- Edge TTS
Gets from Script writerHands to Word-timed captions, Final render
Logic
Stock footage
6 English search terms from the LLM plus fixed niche terms; portrait clips from the Pexels and Pixabay APIs, up to 5 s each, until the voice is covered.
- Pexels API
- Pixabay API
Gets from Script writerHands to Final render
AI
Word-timed captions
faster-whisper (small, int8, CPU) transcribes the voice with word timestamps; words are grouped in pairs into an ASS file, Arial Black 70 with outline.
- faster-whisper
- ASS
Gets from Italian voiceHands to Final render
Logic
Final render
Crop to 1080×1920, join clips, burn captions, mix the voice over a synthesised chord bed (no licensed music), limiter, H.264 + AAC.
- FFmpeg
Gets from Italian voice, Stock footage, Word-timed captionsHands to TikTok draft, Instagram post
Output
TikTok draft
My own Content Posting API client, scopes user.info.basic + video.upload; OAuth via a Cloudflare tunnel, token refreshed at ~23 h; the video waits in the inbox for approval.
- TikTok API
- Cloudflare Tunnel
Gets from Final renderHands to Phone push
Output
Instagram post
Self-hosted Postiz in Docker: find the channel, upload, post now. Its public API sits under /api/public/v1, not the path in the hosted docs.
- Postiz
- Docker
Gets from Final render
Output
Phone push
Polls TikTok's status endpoint (up to 8 × 15 s) and pushes via ntfy only when the draft is really in the inbox; otherwise it sends a warning.
- ntfy
Gets from TikTok draft
The problem
A narrated page (horoscopes, money habits, curiosities) needs a new 30–70 second video most days: a script, a voice, matching footage, captions timed to the words, then an upload to each platform. The open-source generator I had tried ran as a separate app in its own environment, and posting was still manual.
What I built
- I wrote a prompt profile per niche (hook style, structure, banned topics such as investment tips) and an LLM step that writes the Italian script, proposes a topic when the field is empty and rewrites it longer if the voice runs under 30 seconds.
- I voiced scripts with Edge TTS and timed the captions by transcribing the audio with faster-whisper, so two-word captions appear exactly when they are spoken; they are burned in as styled ASS subtitles.
- I sourced footage from the Pexels and Pixabay APIs with LLM-written search terms plus fixed per-niche ones; astrology uses only its own visual pool, so a career horoscope never cuts to an office.
- I put both video engines behind a Flask panel with background jobs, one lock per engine, batch runs and a history log, opened from a desktop icon with no console window.
- I publish from the same job: TikTok drafts through my own Content Posting API client, Instagram through a self-hosted Postiz, and a phone push via ntfy once TikTok confirms the draft.
The hard part
TikTok access. Postiz's connector asked for six scopes, including video.publish, which needs an app audit, so TikTok refused even my own account. I wrote a minimal OAuth client with only user.info.basic and video.upload, behind a Cloudflare tunnel because TikTok rejects localhost; it uploads to the inbox as a draft and polls the status before notifying.
Result
The engine produced 13 narrated videos, and the panel 33 videos across both engines in its first eight days. In an end-to-end test the app uploaded 6 TikTok drafts (3 of them tests) and created 1 Instagram post through Postiz.
My role
Solo project. I chose the niches, wrote the prompt rules and the review-before-posting flow, set up the TikTok developer app, Docker, Postiz and the tunnel, and tested every step; Claude Code wrote most of the Python and the panel.