A podcast RSS feed generator backed by a Spring Boot 4.0 application. It reads MP3 files from a local directory, extracts metadata from their ID3 tags, and produces an RSS 2.0 feed with iTunes/Apple Podcasts modules — auto-regenerated when files change.
docker compose upThis builds the image, starts the service on port 8080, and binds the artwork/, episodes/, and info/ directories.
docker build -t rss-feed-generator .
docker run -p 8080:8080 \
-v $(pwd)/artwork:/app/artwork \
-v $(pwd)/episodes:/app/episodes \
-v $(pwd)/info:/app/info \
-e RSS_ARTWORK_DIR=/app/artwork \
-e RSS_EPISODES_DIR=/app/episodes \
-e RSS_INFO_DIR=/app/info \
rss-feed-generator./gradlew bootRunAt runtime, the app reads from three directories relative to the working directory:
.
├── episodes/ # MP3 episode files (.mp3 by default)
├── artwork/ # Cover art files (.jpeg by default)
│ └── cover.jpeg # Used as the podcast show art
└── info/
└── show.json # Podcast metadata (see below)
{
"title": "My Podcast",
"description": "A description of your show",
"site": "https://example.com",
"link": "http://localhost:8080",
"image": "cover.jpeg",
"language": "en-us"
}| Field | Required | Description |
|---|---|---|
title |
Yes | Podcast name |
description |
Yes | Show description |
link |
Yes | Base URL for serving RSS, episodes, and artwork |
image |
No | Artwork filename in the artwork/ directory |
site |
No | External website URL |
language |
Yes | RSS feed language code (e.g. en-us) |
All settings can be configured via Spring properties in application.properties or via environment-prefixed variables (RSS_).
| Property | Env Var | Default | Description |
|---|---|---|---|
rss.artwork-dir |
RSS_ARTWORK_DIR |
artwork |
Path to artwork directory |
rss.episodes-dir |
RSS_EPISODES_DIR |
episodes |
Path to episodes directory |
rss.info-dir |
RSS_INFO_DIR |
info |
Path to info/ directory containing show.json |
rss.rss-file-name |
RSS_RSS_FILE_NAME |
rss.xml |
Name of the generated RSS XML file |
rss.show-file-name |
RSS_SHOW_FILE_NAME |
show.json |
Podcast metadata file name |
rss.episode-file-extension |
RSS_EPISODE_FILE_EXTENSION |
.mp3 |
File extension to recognize as episodes |
rss.artwork-file-extension |
RSS_ARTWORK_FILE_EXTENSION |
.jpeg |
File extension for artwork files |
rss.extract-artwork |
RSS_EXTRACT_ARTWORK |
false |
Extract embedded cover art from MP3 ID3 tags |
rss.file-watch |
RSS_FILE_WATCH |
true |
Watch episodes directory for file changes and rebuild RSS automatically |
rss.run-on-startup |
RSS_RUN_ON_STARTUP |
false |
Generate RSS feed on application startup |
rss.language |
RSS_LANGUAGE |
en-us |
Default language code |
rss.error-log-file-prefix |
RSS_ERROR_LOG_FILE |
parse-errors- |
Prefix for error log files |
rss.failure-limit |
RSS_FAILURE_LIMIT |
5 |
Max RSS generation failures before stopping retries |
rss.file-delay-seconds |
RSS_FILE_DELAY_SECONDS |
30 |
Seconds to wait after detecting a file change before regenerating RSS. Prevents parsing incomplete writes. Set to 0 for immediate parsing on any file change. |
With Docker Compose, override the defaults in compose.yaml:
environment:
RSS_ARTWORK_DIR: /app/artwork
RSS_EPISODES_DIR: /app/episodes
RSS_INFO_DIR: /app/info
RSS_FILE_WATCH: "true"
RSS_RUN_ON_STARTUP: "true"Or when running with docker run, pass --env RSS_RUN_ON_STARTUP=true.
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check — returns "Up and running" |
/parse |
GET | Trigger a full RSS feed regeneration |
/rss |
GET | Serve the generated RSS feed |
/episodes/{file} |
GET | Serve an episode MP3 file |
/artwork/{file} |
GET | Serve an artwork image file |
# Check health
curl http://localhost:8080/health
# Trigger a re-parse and RSS rebuild
curl http://localhost:8080/parse
# Download the feed
curl http://localhost:8080/rss
# Serve an episode
curl http://localhost:8080/episodes/01%20-%20Ep.%201.mp3
# Serve artwork
curl http://localhost:8080/artwork/cover.jpeg./gradlew bootRun./gradlew bootJar
java -jar build/libs/rss-generator-0.0.1-SNAPSHOT.jarTests automatically run spotlessCheck first:
./gradlew testThis project uses Spotless with Google Java Format. Run lints independently:
./gradlew spotlessCheck
./gradlew spotlessApplyThe app has two mechanisms for RSS regeneration:
- File watcher (default: enabled) — monitors the episodes directory using Java's
WatchServiceand rebuilds the RSS feed when files are created, deleted, or modified. - Scheduled poll (cron: every minute) — checks for new file changes and rebuilds if detected. Also triggers a one-time parse on startup if
rss.run-on-startupistrue.
By default, the app waits 30 seconds after detecting a file change before regenerating the RSS feed. This prevents parsing partially-written files, which can happen when transferring large MP3 files into the episodes directory. The cooldown applies to all RSS regeneration triggers — including startup parses.
Override the cooldown with:
- Environment variable:
RSS_FILE_DELAY_SECONDS=0(or any number of seconds) - Property:
rss.file-delay-seconds=0 - Docker Compose: add
RSS_FILE_DELAY_SECONDSto the environment section
Set to 0 to disable the cooldown and parse immediately on file change.