Hold episode highlights until the episode is in the Spotify feed; auto-fill study lesson audio from the feed by season + episode; v2.19.0
Publish Container / build (push) Successful in 16m16s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-12 11:47:22 -04:00
1 parent 69d445013a
commit 77bce2ae0c
11 files changed
+127 -17

No files matched your search

+8 -9
View File
@@ -11,7 +11,7 @@ const WORKFLOW_FILE = path.join(DATA_DIR, 'podcast-workflow.json')
const STEPS = ['readScript', 'record', 'edit', 'upload']
async function load() {
export async function loadPodcastWorkflow() {
try {
const data = JSON.parse(await readFile(WORKFLOW_FILE, 'utf8'))
return { planned: Array.isArray(data.planned) ? data.planned : [], items: data.items ?? {}, laneOverrides: data.laneOverrides ?? {}, laneSeasons: data.laneSeasons ?? {} }
@@ -30,10 +30,9 @@ function emptyState() {
}
// A lane's season comes from the workflow page's own setting, falling back to Current Series.
function laneSeasonOf(lane, wf) {
export function laneSeasonOf(lane, wf, content = state.cachedSiteContent ?? {}) {
const saved = wf.laneSeasons?.[lane]
if (/^\d+$/.test(String(saved ?? ''))) return String(saved)
const content = state.cachedSiteContent ?? {}
const fallback = lane === 'A' ? content.seriesSeason : content.secondSeries?.season
return fallback ? String(fallback) : ''
}
@@ -55,7 +54,7 @@ function toIsoDate(pubDate) {
export function register(app) {
app.get('/api/admin/podcast-workflow', requireAdminAuth, async (_req, res) => {
const wf = await load()
const wf = await loadPodcastWorkflow()
let feed = []
let feedOk = true
try {
@@ -119,7 +118,7 @@ export function register(app) {
res.status(400).json({ message: 'Season and episode number must be whole numbers.' })
return
}
const wf = await load()
const wf = await loadPodcastWorkflow()
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.` })
@@ -131,7 +130,7 @@ export function register(app) {
})
app.delete('/api/admin/podcast-workflow/planned/:key', requireAdminAuth, async (req, res) => {
const wf = await load()
const wf = await loadPodcastWorkflow()
wf.planned = wf.planned.filter(p => plannedKey(p) !== req.params.key)
delete wf.items[req.params.key]
delete wf.laneOverrides[req.params.key]
@@ -141,7 +140,7 @@ export function register(app) {
// Set the season number each lane looks for ('' goes back to the Current Series value).
app.put('/api/admin/podcast-workflow-lane-seasons', requireAdminAuth, async (req, res) => {
const wf = await load()
const wf = await loadPodcastWorkflow()
for (const lane of ['A', 'B']) {
const v = req.body?.[lane]
if (v === undefined) continue
@@ -161,7 +160,7 @@ export function register(app) {
res.status(400).json({ message: 'Lane must be A, B, other or auto.' })
return
}
const wf = await load()
const wf = await loadPodcastWorkflow()
if (lane === 'auto') delete wf.laneOverrides[req.params.key]
else wf.laneOverrides[req.params.key] = lane
await save(wf)
@@ -170,7 +169,7 @@ export function register(app) {
app.put('/api/admin/podcast-workflow/:key', requireAdminAuth, async (req, res) => {
const { key } = req.params
const wf = await load()
const wf = await loadPodcastWorkflow()
const prev = { ...emptyState(), ...(wf.items[key] ?? {}) }
const next = { ...prev, steps: { ...prev.steps } }
const body = req.body ?? {}