When I started auto-publishing YouTube videos from GitHub Actions, the default thumbnails were whatever frame YouTube chose to freeze on. Usually a half-rendered slide or a moment of black. They looked unprofessional enough that I fixed it before worrying about anything else.
The result is thumbnail.sh — 51 lines of bash that run as step 4a in my publish pipeline, generate a 1280×720 JPEG from the finished mp4, and hand it to upload.py for thumbnails.set . Here's how it works and where it's still rough.
What the pipeline looks like before the thumbnail step
My full pipeline is orchestrated by main.sh :
TTS — tts.sh generates voice.wav from a script using edge-tts
Visuals — visuals.sh writes slide_*.txt files, one per sentence
Background — bg.sh pulls a Pexels stock video or falls back to a solid color
Compose — compose.sh assembles everything into output.mp4 using ffmpeg
Thumbnail — thumbnail.sh reads output.mp4 , writes thumbnail.jpg ← new
Upload — upload.py uploads the mp4 and optionally calls…