Pete's personal blog - static site generator
- Python 44.7%
- CSS 36.6%
- HTML 17.1%
- Dockerfile 1.6%
|
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 20s
Kokoro returns each TTS chunk as a fully-formed MP3 stream, complete with its own ID3v2 header and sometimes an 8 kbps "priming" frame at the start. Raw byte-concatenation (audio_data += chunk_audio) left all those ID3 headers embedded mid-stream and the 8 kbps priming frame as the very first audio frame of the final file. Without a Xing header, mutagen.mp3 and many podcast clients estimate duration from the first frames bitrate. When chunk 0 starts with the 8 kbps priming frame instead of the real ~112 kbps speech bitrate, the estimate is ~14x too long. Result: a 5.5 min post displays as 1h 8min in the UI, and itunes:duration in the RSS feed is wrong too. Fix: replace raw concat with remux_chunks_to_clean_mp3() — writes each chunk to a temp file, runs ffmpeg with the concat demuxer and libmp3lame at 128 kbps with -write_xing 1. The decode + re-encode round-trip discards mid-stream headers and the priming frame; the Xing header carries an accurate total-frame count so every reader agrees on duration. Also re-muxed the three existing broken files in static/audio/: * 2026-05-08-same-chemistry-different-beast.mp3 (was 5164s → 438s) * 2026-05-13-same-words-different-dictionary.mp3 (was 4673s → 400s) * 2026-06-01-reconstituting-to-facts.mp3 (was 4123s → 330s) ffprobe and mutagen now agree on duration for all three. |
||
|---|---|---|
| .forgejo/workflows | ||
| posts | ||
| static | ||
| templates | ||
| .gitignore | ||
| build.py | ||
| docker-compose.yml | ||
| Dockerfile | ||
| generate_audio.py | ||
| nginx.conf | ||
| README.md | ||
| requirements.txt | ||
pete.lostsource.net
Personal blog by Pete — AI systems architect.
Static site generator: Python (Jinja2 + Markdown + Pygments) → nginx.
Architecture
- Posts: Markdown with YAML frontmatter in
posts/ - Build:
python build.pyrenders tooutput/ - Deploy: CI builds Docker image (multi-stage: python build → nginx serve)
- Security: Static HTML only. No user input. No JS.
security@filemiddleware.
Writing a Post
Create posts/YYYY-MM-DD-slug.md:
---
title: "Post Title"
date: 2026-05-03
tags: [tag1, tag2]
summary: "One-line description"
---
Post content in markdown...
Push to main. CI handles the rest.
Local Build
pip install -r requirements.txt
python build.py
# Output in output/
∞