Automated radio morning show production — research, script, render audio, and publish to playout system
Produces a daily 4-hour radio morning show (Mon–Fri, 5–9 AM ET) through an automated pipeline: research trending topics, write hour-by-hour scripts, render voice segments via ElevenLabs, pull scheduled songs, assemble the final show, and publish to the station playout system via PlayoutONE's AutoImporter.
⚠️ CRITICAL — Read Before Publishing The publish pipeline uses the AutoImporter flow. Never directly INSERT or UPDATE the Playlists table. See Publishing Rules below.
/show:fullRun the complete pipeline end-to-end for a given date.
/show:full # produce tomorrow's show
/show:full 2026-04-07 # produce show for specific date
/show:full --hours 5,6 # produce only hours 1 and 2
/show:researchGather trending topics, news, weather, and date-relevant content for show material.
/show:research # research for tomorrow's show
/show:research 2026-04-07 # research for specific date
/show:writeGenerate hour-by-hour show scripts from research output.
/show:write # write scripts for tomorrow
/show:write --hour 7 # write script for a single hour
/show:write --rewrite 6 # rewrite hour 2 script with new direction
/show:renderRender voice segments from approved scripts using ElevenLabs TTS.
/show:render # render all segments for tomorrow
/show:render --hour 5 # render segments for hour 1 only
/show:render --segment intro # render a specific segment
/show:produceAssemble rendered voice segments with songs into final hour blocks. Normalizes to broadcast loudness (-16 LUFS).
/show:produce # produce all hours
/show:produce --hour 8 # produce hour 4 only
/show:previewGenerate a 128kbps preview and send via Telegram for approval before publishing.
/show:preview # preview all hours
/show:preview --hour 5 # preview hour 1 only
/show:publishPush final audio files to the station playout system via AutoImporter.
/show:publish # publish all hours for tomorrow
/show:publish --hour 6 # publish hour 2 only
/show:publish --dry-run # validate without executing
/show:statusShow current production status — which pipeline stages are complete, any errors or warnings.
/show:status # status for tomorrow's show
/show:status 2026-04-07 # status for specific date
/show:verifyQuery PlayoutONE to confirm show rows are correctly installed in the Playlists table.
/show:verify # verify tomorrow's show
/show:verify 2026-04-07 # verify specific date
/show:archiveArchive completed show scripts, audio, and logs.
/show:archive # archive today's completed show
/show:archive 2026-03-30 # archive a specific date
research → write → [approve] → render → pull songs → produce → [preview] → publish → verify
| Stage | Input | Output | Script |
|---|---|---|---|
| research | date, APIs | research notes (markdown) | research-date.sh |
| write | research notes | hour scripts (markdown) | write-scripts.sh |
| approve | scripts | human approval / edits | — |
| render | approved scripts | voice segments (mp3) | render-voice.sh |
| pull songs | playlist/schedule | song files from station library | pull-songs.sh |
| produce | voice segments + songs | final hour blocks (mp3) | produce-hour.sh |
| preview | final hours | Telegram preview | preview.sh |
| publish | final hours | registered in Audio table, DPL dropped to AutoImporter | publish.sh |
| verify | — | confirmation Playlists rows exist | publish.sh (built-in) |
Bracketed stages ([approve], [preview]) are optional human checkpoints.
These rules were established after the 2026-03-30 broadcast failure. Follow them exactly.
F:\PlayoutONE\Audio\ as UID-named files (e.g. 90005.mp3)Audio table with correct TrimOut and Extro markers.dpl file per hour (14-column Music1 format)F:\PlayoutONE\Import\Music Logs\ — AutoImporter handles the restC:\PlayoutONE\data\playlists\ — that path is ignored by AutoImporter| System | When | Effect |
|---|---|---|
| Music1 | Runs periodically, generates 24 DPLs | Overwrites ALL hours if imported after our DPL |
| Playlist scheduler | Hourly cron | Replaces entries for upcoming hours with genre rotation |
| AutoImporter | Continuous, watches import folder | First-import-wins — won't overwrite existing entries |
Solution: Publish AFTER all automated systems have run. Ideal window: 30-60 min before first show hour (e.g., 4:00-4:30 AM for a 5 AM show). The DELETE+clear+DPL sequence ensures our entries win.
| Show Hour | Air Time | UID |
|---|---|---|
| Hour 1 | 5 AM | 90005 |
| Hour 2 | 6 AM | 90006 |
| Hour 3 | 7 AM | 90007 |
| Hour 4 | 8 AM | 90008 |
Every Audio table row for show content must have:
TrimIn = 0
TrimOut = [actual_length_ms]
Extro = [actual_length_ms] - 3000 (3-second crossfade buffer)
Intro = 0
publish.sh handles this automatically. If registering manually, do not skip this step.
Keep individual show blocks to ≤15 minutes per file. Larger files risk PlayoutONE buffering failures. Split 60-minute hours into four 15-minute segments if needed. The publish script supports multiple segments per hour.
The show is hosted by Dr. Johnny Fever — grumpy Gen X radio veteran, deep music knowledge, dry sarcasm, genuine warmth underneath. Full persona reference in references/dr-johnny-fever.md.
skills/morning-show/
├── SKILL.md # This file
├── config.yaml # Voice, station, audio settings
├── scripts/
│ ├── build-show.sh # Master orchestrator
│ ├── research-date.sh # Fetch news, weather, music history
│ ├── write-scripts.sh # LLM script generation
│ ├── render-voice.sh # ElevenLabs TTS rendering
│ ├── pull-songs.sh # Download songs from station
│ ├── produce-hour.sh # Assemble + normalize hour blocks
│ ├── preview.sh # Compress + send Telegram preview
│ └── publish.sh # Upload, register, drop DPL, verify
├── templates/
│ ├── monday.md # Day-specific show template
│ ├── tuesday.md
│ ├── wednesday.md
│ ├── thursday.md
│ ├── friday.md
│ └── segments/
│ ├── open.md
│ ├── weather.md
│ ├── music-history.md
│ ├── rant.md
│ ├── quick-hits.md
│ ├── promos.md
│ └── close.md
└── references/
├── dr-johnny-fever.md # Persona reference
├── playoutone-schema.md # DB schema + mutation rules
├── production-notes.md # Technical lessons learned
└── energy-arcs.md # Hour-by-hour energy mapping
After reading this SKILL.md, always read the .planning/ files before executing any task:
.planning/SPEC.md — Full technical specification, acceptance criteria, data flow, external dependencies, edge cases.planning/PLAN.md — Wave-by-wave build plan, exact task definitions with accept criteria and test cases.planning/STATE.md — Current build state, completed tasks, decisions log, test results, next steps.planning/TRACES.md — Execution traces, failures, review issuesThese files are the ground truth for what exists, what's pending, and how to build it correctly.
# 1. Produce and publish next Monday's show (run Sunday evening)
./scripts/build-show.sh --date 2026-04-06
# 2. Publish only (if audio already produced)
./scripts/publish.sh \
--date 2026-04-06 \
--audio-dir ./shows/2026-04-06/ \
--config config.yaml
# 3. Verify installation
./scripts/publish.sh --date 2026-04-06 --dry-run
# 4. Check station confirmed the schedule
ssh p1-wpfq-srvs "sqlcmd -S localhost -d PlayoutONE_Standard -E -Q \
\"SELECT Name,UID,Title,SourceFile,MissingAudio FROM Playlists \
WHERE Name LIKE '20260406%' AND UID LIKE '9000%'\""
| Date | Incident | Resolution | Reference |
|---|---|---|---|
| 2026-03-30 | Morning show failed to air — 2h 15min dead air | publish.sh rewritten to use AutoImporter flow | docs/INCIDENT-2026-03-30-MORNING-SHOW.md |
TrimOut/Extro audio marker requirements to publish flowF:\PlayoutONE\Import\Music Logs\ (not C:\ path)/show:verify command