Introducing the Brett Codes Show & coding a simple podcast site generator with Ruby

I set up a podcast feed for my rambling monologues that have been up until this point only available as YouTube videos. I'm calling the podcast The Brett Codes Show, and it has its own website at https://show.brettcodes.com/ with all of the episodes. The RSS feed is available at https://show.brettcodes.com/feed.xml

the brett codes show in white text on a black background

Since my rambles are lightly edited videos of me talking into the camera, they're a perfect fit for listening to without video. I think about them as a solo podcast, and uploading to YouTube was simple for getting started since I already had a channel. But I've got a variety of concerns about YouTube, from supporting Google by using their platform to the addictive nature of the algorithm to the consolidation of content on one major tech platform. I love podcasts and how they're able to be hosted independently of any given platform, just like the web. Podcasts are basically links to MP3 files connected together in an RSS XML feed. There are some YouTube videos I make where they rely heavily on visuals and don't work audio-only, like coding tutorials. So those won't make their way into the Brett Codes Show feed.

I'm glad to be able to offer an alternative way to access my rambles for those who don't want to use YouTube, would prefer to not have ads that YouTube ads, or want to use their favorite podcasting app to listen. I personally download MP3 files and put them on my MP3 player, but I'm a bit of a low tech weirdo.

While there is no shortage of podcast hosting services out there, some of which are free, I wanted to have full control over the feed and the associated website. I also didn't want to be burdened with a recurring monthly subscription until the sun explodes, which is how long I intend to keep the show accessible. I figured it'd be simple enough to extract the audio from my videos, host them on a content delivery network somewhere, and make a simple static site generator to browse the episodes. So I went ahead and rolled my own podcast hosting setup!

First, I take the videos I want to add to the feed and extract the audio using FFmpeg with this little Ruby script I have in my $PATH as extractmp3:

require "shellwords"

abort("Error: you must provide at least one file") if ARGV.empty?

ARGV.each do |f|
  out = "#{File.basename(f, '.*')}.mp3"
  system("ffmpeg -i #{Shellwords.shellescape(f)} -ac 1 -q:a 4 -map a #{Shellwords.shellescape(out)}")
end

The resulting MP3s are mildly optimized and reduced from stereo to mono. I upload those to a Bunny.net storage zone, which is then used in the feed and static site generator. I coded a simple Ruby program that uses YAML to describe the show and its episodes. I add a new episodes/XX-SLUG.yml file, fill out the details, and run the command to generate the site and push it up to Bunny.net. The episode YAML files look like this:

title: How I'd learn to code if I started over in the AI era
description: Yadda yadda
file: https://show.brettcodes.com/media/Brett%20Codes%20-%20How%20I'd%20learn%20to%20code%20if%20I%20started%20over%20in%20the%20AI%20era.mp3
duration: "00:45:47"
bytes: 32287149
published_at: Wed, 2 Sep 2026 08:00:00 GMT
explicit: false

It took me about an hour to code the static site generator with Ruby 4. I decided to not use any dependencies to keep it simple. It works well! The basic gist of how it works is that there are some template files for the XML feed and HTML web pages that use Ruby's built in ERB syntax. The generator loads up the show details from show.yml and the episodes from episodes/*.yml, populates the feed XML and HTML web page templates with the data, and writes it all to the _build directory. That folder then gets pushed to Bunny.net's storage zone.

▶ Watch a video of me coding the feed and site generator from scratch.

Here's a simplified sketch of how the static feed generator works, which is where I started and then expanded the RSS XML generation to also generate HTML pages for all the episodes:

require "erb"
require "yaml"
require "fileutils"

OUTDIR = "_build"
EPS_DIR = "episodes"

FileUtils.rm_rf(OUTDIR)
FileUtils.mkdir_p(OUTDIR)
FileUtils.mkdir_p(File.join(OUTDIR, EPS_DIR))

show = YAML.load_file("show.yml")
episodes = Dir.glob("#{EPS_DIR}/*.yml").sort.map do |f|
  ep = YAML.load_file(f)
  ep["path"] = f.gsub(".yml", ".html")
  ep["link"] = "#{show['link']}/#{ep['path']}"
  ep
end

generate_feed(show, episodes)

It cleans the OUTDIR, which is the _build folder. Then it loads the show details and episodes from YAML files. I chose YAML because it's simple, flexible, and much less verbose than JSON. Lately I've used TOML for different projects, but since Ruby has built-in YAML parsing, I went with that. I really appreciate the YAML.load_file function that takes a path to the file and parses the YAML.

The #generate_feed function is similarly simple:

def generate_feed(show, episodes)
  erb = ERB.new(File.read("feed.xml.erb"))
  feed = erb.result_with_hash({ show: show, episodes: episodes })
  File.write(File.join(OUTDIR, "feed.xml"), feed)
end

ERB is the templating language that's widely used through Ruby and it's built into the standard library. You instantiate a new instance of ERB with the template string (which we read from feed.xml.erb) and then there's a nifty method #result_with_hash that outputs the rendered string with those passed in variables as a hash. That then gets written to disk in the _build folder (OUTDIR). View feed.xml.erb on Codeberg if you're curious.

feed.xml.erb takes the show and outputs the data and then loops through the episodes, building the RSS XML feed for the show. Adding HTML rendering was basically taking that same exact concept but using an HTML ERB template for the home page and each episode page and rendering that inside a shared layout.

I've been coding with basically only Rust for the past month, and I initially thought I'd reach for Rust for this little podcast publishing CLI, but Ruby is really great for quickly sketching something out and getting it working, especially with the robust standard library. With Rust, I'd need to pull in a templating language and YAML parser. The Rust program would no doubt be faster and more resilient, but this Ruby program works more than well enough for now. I could imagine rebuilding this in Rust or Go or something and having a compiled binary that doesn't require the Ruby runtime, but I'm not sure I want to maintain that. Also, there's something satisfying about coding a program for my specific needs and not having it become a whole thing to meet others' needs.

I use Bunny.net for hosting various websites and files, and I find the costs to be extremely reasonable. Often less than US$1/mo to host most of my websites and files that I serve online. The CDN is US$0.01/GB and storage is US$0.03/GB. So if I use 100 GB per month of CDN bandwidth serving the MP3 files, that'd cost me US$1. 100 GB of bandwidth would be about 5,000 episode downloads throughout the month, which seems unlikely for a niche, rambling podcast about software development. And if there was for some reason a spike in downloads, it wouldn't be a huge cost. The storage costs are negligible since the audio files are all pretty small. I don't have to manage a server or keep anything up to date.

All of the links to files and pages are through domains I control, so if for some reason I ever want to host it elsewhere, all I have to do is point the domain to the new host and ensure existing paths work (or set up redirects). This is why it's ideal to own the domain from top to bottom, when possible.

Full control, minimal costs, portable, and coded for longevity? That's perfect for my needs.

Here's the source code for the generator, if it'd be of any use to you for hosting your own podcast feed and site: https://codeberg.org/brettchalupa/brettcodesshow I tried to code it so it could be easily reused for other podcasts. It's essentially just 66 lines of Ruby and some mark-up.

Deploying the site does rely on Bunny.net's bunny CLI, since it does a lot of the heavy lifting for auth and uploading files. It'd be easy enough to swap out for something else, like S3 uploads or FTP.

One missing piece is any sort of analytics for which episodes are downloaded. I'm not particularly interested in analytics right now, but I could set up Bunny.net's logs to be permanently stored in a separate storage zone and then analyze the requests to get concrete download counts for an episode in a given time period. Maybe that's something I'll code in the future if I find myself curious about those numbers.

I was pleasantly surprised with how easy it was to build a static site generator from scratch with Ruby and not have to pull in Jekyll. The inclusion of ERB templating and YAML parsing in Ruby basically meant all my needs were met. Plus I can use a lot of this code again in the future if I ever set up any other podcasts. Coding this project was a lot of fun, and I'm enjoying improving it bit by bit. I tend to learn a lot more when I don't use dependencies as well, which is a nice bonus.

If you're interested in listening along, I'd be thrilled if you checked out the show and subscribed in your podcatcher of choice. Here are the links to the Brett Codes Show:


Thank you for reading! If you found this post helpful or enjoyed episodes of the Brett Codes show, consider buying me a coffee to support my projects.

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论