Bui Van Dat

How I Automated a Twice-Daily News Video With Python, ffmpeg, and Local Text-to-Speech

A pipeline that reads 22 sources, has an AI editor pick and fact-check stories, then renders a narrated video with karaoke captions at no per-video cost.

4 min read
In this article
  1. What one edition contains
  2. Stage 1: collect
  3. Stage 2: pick and write with an AI editor
  4. Stage 3: render the visuals with a headless browser
  5. Stage 4: give it a voice
  6. Stage 5: karaoke captions
  7. Stage 6: assemble with ffmpeg
  8. Stage 7: publish
  9. Cost
  10. What I learned

The DatPilot Facebook page publishes a technology news digest every morning and every evening: a short narrated video, plus an album of image cards. Nobody edits them by hand. This post explains how the pipeline works and the decisions that made it cheap and reliable.

What one edition contains

Each edition is a Top 10 with a fixed mix of topics:

  • 3 stories on AI
  • 2 on technology
  • 2 on stock markets, one Vietnamese and one US
  • 2 on crypto
  • 1 on the web and making money online

The output is a vertical video of about 90 seconds, a cover image, a market table, and 10 story cards. The morning edition goes out at 07:30 and the evening one at 20:00, Vietnam time.

Stage 1: collect

The pipeline reads 22 sources: official company blogs, international outlets, and Vietnamese news sites. Most have RSS feeds. A few do not, so for those it reads the links on their news page directly.

Market numbers come from public data endpoints for stock indexes and cryptocurrencies. The sentence that reads those numbers aloud is assembled by code, not written by a model, because a model will occasionally “round” a number into something wrong.

Stage 2: pick and write with an AI editor

A language model acts as the editor. It receives the candidate stories and chooses the 10 that fit the topic mix, preferring fresh international stories that local media have not covered yet. It then writes a short Vietnamese summary for each.

Then comes the important part: a second pass compares every summary against its source article. If a summary claims something the source does not say, it gets rewritten or dropped. An edition takes about three model calls in total.

Outside the scheduled editions, a breaking news check runs every 15 minutes. It only fires for major announcements from the companies themselves, at most twice a day, and never too close to a scheduled edition.

Stage 3: render the visuals with a headless browser

Every card and every video scene is an HTML page. This is the trick that keeps design work fast: layouts, fonts, and colors are just CSS. A headless Chrome instance opens each page and captures it.

For the video, each scene has a short CSS animation, about 1.3 seconds of text sliding in and numbers counting up. A small script steps through the animation and captures it frame by frame as JPEG images. Rendering all scenes for one edition takes about 80 seconds.

Stage 4: give it a voice

The narration uses a Vietnamese text-to-speech model running locally on the CPU. No paid voice API, no per-minute cost. It runs in its own Python environment because it depends on a specific older version of its inference runtime.

Choosing the voice took a few rounds. The first cloud voices I tried sounded flat and did not stress words naturally, which matters a lot in a tonal language like Vietnamese. The local model with a male news voice sounded more natural. If it fails, the pipeline falls back to a free cloud voice so the edition still ships.

Narrating a full edition of about 99 seconds takes around 4 minutes on the CPU. That is slow, but the schedule is fixed and nobody is waiting.

Stage 5: karaoke captions

Most people watch short videos on mute, so captions are not optional. The captions highlight each word as it is spoken, karaoke style.

The fallback cloud voice reports timestamps for each word. The local model does not, so the timing is estimated from syllables: Vietnamese is written as separate syllables, which makes this estimate surprisingly good. The captions are written as an ASS subtitle file and burned into the video by ffmpeg using libass, with the font files shipped alongside the script.

Stage 6: assemble with ffmpeg

ffmpeg does the rest:

  • joins the scenes with 0.4-second transitions: slide, wipe, and fade to black
  • adds a short whoosh sound on each transition, generated from filtered noise instead of a stock audio file
  • mixes in the narration
  • burns in the captions
  • encodes a vertical video ready for upload

The full build, from text to finished video, takes about 5 minutes per edition. If a video runs longer than 90 seconds, it is uploaded as a regular video instead of a short.

Stage 7: publish

The finished video, cover, and cards are posted to the page through the official API. Source links go in a comment, not in the caption, so every story can be traced back to where it came from.

Cost

The marginal cost of each edition is effectively zero: open-source tools, a local voice model, a server I already run, and model calls covered by a subscription I already pay for. The real cost was building it, which took about two days of focused work.

What I learned

  • HTML is the best design tool for generated media. Changing a layout means editing CSS, not re-exporting assets.
  • Let code handle numbers. The model writes sentences. Code writes prices and percentages.
  • Always cross-check the summaries. The second pass catches errors the first one makes confidently.
  • Plan a fallback for every external dependency. The voice, the sources, and the upload can all fail. The edition should still ship.
  • Test the boring edge cases. A user-agent string that was too detailed got rate-limited by a market data endpoint. A plain one worked fine.

This project is a good example of what I mean by AI agents doing the work while code handles the facts. The model is the editor. Everything else is ordinary, testable software.

  • Python
  • ffmpeg
  • text-to-speech
  • automation
  • AI news