Transcripts: new and existing episodes are transcribed in the background (published transcript first, else whisper), with a status line in Health
build-container / build (push) Successful in 3m29s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-23 11:46:15 -04:00
1 parent e7bd24bbaa
commit 1c089c4a10
6 files changed
+64 -8

No files matched your search

+23 -3
View File
@@ -113,13 +113,33 @@ async function handle(req, res) {
}
}
/** For transcripts: where an episode came from (its feed, its id in that feed, its file). Anyone may ask about the shows on their own list only. */
/** For transcripts: where an episode came from (its feed, its id in that feed, its file). With a person, only the shows on their own list; without one (the server's own background work), any. */
async function feedInfo(me, itemId) {
if (!BASE || !/^[\w-]+$/.test(String(itemId))) return null;
const f = (o, k) => { if (!o) return undefined; const hit = Object.keys(o).find(x => x.toLowerCase() === k.toLowerCase()); return hit === undefined ? undefined : o[hit]; }; // (Podgrab's field names, whatever their capitals)
const it = await (await podgrab('/podcastitems/' + itemId)).json().catch(() => null); if (!it) return null;
const pid = f(it, 'PodcastID') || f(it, 'podcastId'); if (!pid || !(await mySubs(me)).includes(pid)) return null;
const pid = f(it, 'PodcastID') || f(it, 'podcastId'); if (!pid || (me && !(await mySubs(me)).includes(pid))) return null;
const sh = await (await podgrab('/podcasts/' + pid)).json().catch(() => null);
return { guid: f(it, 'GUID') || f(it, 'guid') || '', fileUrl: f(it, 'FileURL') || f(it, 'fileUrl') || '', feedUrl: (sh && (f(sh, 'URL') || f(sh, 'url'))) || '' };
}
module.exports = { handle, enabled, feedInfo };
/** The newest `n` episodes of every show in Podgrab, newest first overall: [{ id, title, show, guid, fileUrl, feedUrl }]. */
async function recentEpisodes(n) {
if (!BASE) return [];
const f = (o, k) => { const hit = Object.keys(o || {}).find(x => x.toLowerCase() === k.toLowerCase()); return hit === undefined ? undefined : o[hit]; };
const shows = await (await podgrab('/podcasts')).json().catch(() => []), out = [];
for (const sh of Array.isArray(shows) ? shows : []) {
const sid = idOf(sh); if (!sid) continue;
const items = await (await podgrab('/podcasts/' + sid + '/items')).json().catch(() => []);
for (const it of (Array.isArray(items) ? items : []).sort((x, y) => String(f(y, 'PubDate')).localeCompare(String(f(x, 'PubDate')))).slice(0, n))
out.push({ id: String(idOf(it)), title: f(it, 'Title') || '', show: f(sh, 'Title') || '', date: String(f(it, 'PubDate') || ''), guid: f(it, 'GUID') || '', fileUrl: f(it, 'FileURL') || '', feedUrl: f(sh, 'URL') || '' });
}
return out.sort((x, y) => y.date.localeCompare(x.date));
}
/** The audio of one episode, straight from Podgrab (for the server's own background work). */
async function episodeAudio(itemId) {
if (!BASE || !/^[\w-]+$/.test(String(itemId))) throw new Error('Bad episode');
const r = await podgrab('/podcastitems/' + itemId + '/file'), loc = r.headers.get('location');
if (r.status >= 300 && r.status < 400 && loc && /^https?:\/\//i.test(loc)) return fetch(loc, { signal: AbortSignal.timeout(2 * 3600e3) }); // (Podgrab has no copy of its own: the original)
return r;
}
module.exports = { handle, enabled, feedInfo, recentEpisodes, episodeAudio };