Publish a new episode of the Weathering podcast. Handles the full release pipeline: MP3 metadata & chapters via ffmpeg, upload to Cloudflare R2, generate episode markdown for the dynamical.org 11ty site, validate the podcast RSS feed, and draft social posts. Use this skill whenever the user says 'release an episode', 'publish a podcast', 'new episode', 'podcast release', or anything about shipping/publishing a Weathering episode. Also trigger when user mentions podcast metadata, chapter markers, or podcast feed validation in the context of Weathering.
Publishes a new episode of the Weathering podcast through a multi-step pipeline. Each step pauses for user confirmation before proceeding to the next.
The user needs to provide (or you need to locate):
weathering_logo.png)Tools that should be available on the system:
ffmpeg (for MP3 metadata/chapters)aws CLI (for R2 upload)git (for committing to the dynamical.org repo)op (1Password CLI — R2 credentials are in the Dynamical vault)Work through these steps in order. Use a TodoList to track progress. Pause after each step to confirm with the user before proceeding.
Ask the user for:
content/podcast/ and incrementing)https://weathering.dynamical.org/weathering_logo.png)Fetch the Google Doc by appending /export?format=html to the doc URL (follow redirects). Use HTML format, not TXT — the plain text export strips all hyperlinks. Parse out:
HH:MM:SS or MM:SS formatShow the parsed content to the user for confirmation before proceeding.
Use ffmpeg to embed ID3v2 metadata into the MP3:
# Write chapter metadata file (ffmetadata format)
# See scripts/write_chapters.py for generating the ffmetadata file from parsed chapters
ffmpeg -i input.mp3 -i chapters.ffmetadata \
-map 0:a -map_metadata 1 \
-metadata title="EPISODE_TITLE" \
-metadata artist="Marshall, Marta, and Alden" \
-metadata album="Weathering" \
-metadata track="EPISODE_NUMBER" \
-metadata genre="Podcast" \
-metadata date="PUBLISH_DATE" \
-c copy \
output.mp3
If cover art is provided, also embed it:
ffmpeg -i output.mp3 -i cover.png \
-map 0:a -map 1:v \
-c:a copy -c:v png \
-metadata:s:v title="Album cover" \
-metadata:s:v comment="Cover (front)" \
-disposition:v attached_pic \
final.mp3
Write the ffmetadata file directly (it's a simple INI-like format — see the ffmpeg docs). Each [CHAPTER] block needs TIMEBASE=1/1000, START/END in milliseconds, and title. Use ffprobe to get the total duration for the final chapter's END time.
After tagging, verify with:
ffprobe -v quiet -print_format json -show_chapters -show_format output.mp3
Show the metadata summary to the user for confirmation.
Upload the final MP3 to the Cloudflare R2 bucket. Use op run to inject credentials from the Dynamical vault:
op run --env-file=<(cat <<'EOF'
AWS_ACCESS_KEY_ID=op://Dynamical/Cloudflare R2 Weathering/AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=op://Dynamical/Cloudflare R2 Weathering/AWS_SECRET_ACCESS_KEY
EOF
) -- aws s3 cp final.mp3 s3://weathering/EPISODE_NUMBER.mp3 \
--endpoint-url https://a037e2d3d5f9c6ecfc95350360f2ab8d.r2.cloudflarestorage.com \
--content-type audio/mpeg
The public URL will be: https://weathering.dynamical.org/EPISODE_NUMBER.mp3
Verify the upload by checking the URL is accessible (curl -I).
Create the episode markdown file at content/podcast/EPISODE_NUMBER.md in the dynamical.org repo. Follow the exact frontmatter format of existing episodes:
---