Podcast workflow: episodes start manually via Start new episode (name, season, ep #); scripts no longer create entries; v2.17.0
Publish Container / build (push) Successful in 16m42s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-12 08:33:23 -04:00
1 parent 3a69e03e60
commit 326838e5bc
4 files changed
+55 -38

No files matched your search

+20 -22
View File
@@ -4,7 +4,7 @@ import { writeFile } from '../atomic-write.js'
import { requireAdminAuth } from '../auth.js'
import { state } from '../state.js'
import { DATA_DIR } from '../config.js'
import { scriptKey, scriptEpisodeNumber } from '../episode-script-keys.js'
import { scriptKey } from '../episode-script-keys.js'
import { fetchAllEpisodes } from './episodes.js'
const WORKFLOW_FILE = path.join(DATA_DIR, 'podcast-workflow.json')
@@ -46,7 +46,7 @@ function laneFor(season, wf) {
}
const normTitle = t => String(t ?? '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim()
const plannedKey = p => p.key ?? scriptKey(p.season, p.episodeNumber)
const plannedKey = p => scriptKey(p.season, p.episodeNumber)
function toIsoDate(pubDate) {
const d = new Date(pubDate)
@@ -67,18 +67,10 @@ export function register(app) {
const byKey = new Map()
const upsert = (key, patch) => byKey.set(key, { ...(byKey.get(key) ?? {}), key, ...patch })
for (const [key, entry] of Object.entries(state.episodeScripts ?? {})) {
upsert(key, {
season: entry.season ?? '',
episodeNumber: scriptEpisodeNumber(key, entry),
title: entry.title ?? '',
hasScript: true,
})
}
for (const p of wf.planned) {
const key = plannedKey(p)
const prev = byKey.get(key)
upsert(key, { season: p.season, episodeNumber: p.episodeNumber, title: prev?.title || p.title || '', planned: true, plannedLane: p.lane })
upsert(key, { season: p.season, episodeNumber: p.episodeNumber, title: prev?.title || p.title || '', planned: true })
}
for (const item of feed) {
const hasNum = /^\d+$/.test(item.episode)
@@ -104,7 +96,7 @@ export function register(app) {
const items = [...byKey.values()].map(e => ({
...e,
lane: wf.laneOverrides[e.key] ?? e.plannedLane ?? laneFor(e.season, wf),
lane: wf.laneOverrides[e.key] ?? laneFor(e.season, wf),
laneOverride: wf.laneOverrides[e.key] ?? '',
released: Boolean(e.released),
state: { ...emptyState(), ...(wf.items[e.key] ?? {}) },
@@ -113,22 +105,28 @@ export function register(app) {
res.json({ items, feedOk, laneSeasons: { A: laneSeasonOf('A', wf), B: laneSeasonOf('B', wf) } })
})
// Start the workflow for a new episode. Name, season and episode number are all required
// so the episode lands in the right lane and matches the feed once it is released.
app.post('/api/admin/podcast-workflow/planned', requireAdminAuth, async (req, res) => {
const season = /^\d+$/.test(String(req.body?.season ?? '')) ? String(req.body.season) : ''
const season = String(req.body?.season ?? '').trim()
const episodeNumber = String(req.body?.episodeNumber ?? '').trim()
const title = typeof req.body?.title === 'string' ? req.body.title.trim().slice(0, 160) : ''
if (!/^\d+$/.test(episodeNumber)) {
res.status(400).json({ message: 'Episode number must be a whole number.' })
if (!title) {
res.status(400).json({ message: 'Episode name is required.' })
return
}
const lane = ['A', 'B'].includes(req.body?.lane) ? req.body.lane : 'other'
const wf = await load()
// Without a season number the key must still be unique per lane.
const key = season ? scriptKey(season, episodeNumber) : `${lane}-p${episodeNumber}`
if (!wf.planned.some(p => plannedKey(p) === key)) {
wf.planned.push({ key, season, episodeNumber, title, lane })
await save(wf)
if (!/^\d+$/.test(season) || !/^\d+$/.test(episodeNumber)) {
res.status(400).json({ message: 'Season and episode number must be whole numbers.' })
return
}
const wf = await load()
const key = scriptKey(season, episodeNumber)
if (wf.planned.some(p => plannedKey(p) === key)) {
res.status(409).json({ message: `Season ${season} episode ${episodeNumber} has already been started.` })
return
}
wf.planned.push({ season, episodeNumber, title })
await save(wf)
res.json({ ok: true, key })
})