Podcast: resume where you left off, synced between devices (shared progress store with audiobooks)
build-container / build (push) Successful in 9m26s
build-container / build (push) Successful in 9m26s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aa2133ed9a
commit
b3494b9720
5 files changed
+61
-16
No files matched your search
+9
-1
@@ -1,5 +1,6 @@
|
||||
// Podcasts from Podgrab (https://github.com/akhilrex/podgrab). The hub talks to Podgrab's own web API on the person's behalf, so the
|
||||
// Podgrab password (PODGRAB_PASSWORD, user "podgrab") stays on the server and the browser only ever talks to the hub.
|
||||
const prog = require('./progress')('podcast-progress.json'); // where each person got to in each episode
|
||||
const BASE = (process.env.PODCAST_URL || 'https://podcast.necloud.us/').replace(/\/$/, '');
|
||||
const AUTH = process.env.PODGRAB_PASSWORD ? 'Basic ' + Buffer.from('podgrab:' + process.env.PODGRAB_PASSWORD).toString('base64') : '';
|
||||
|
||||
@@ -15,7 +16,6 @@ const json = (res, code, o) => { res.writeHead(code, { 'content-type': 'applicat
|
||||
const enabled = () => !!BASE;
|
||||
|
||||
async function handle(req, res) {
|
||||
if (!BASE) return json(res, 503, { error: 'Podcasts are not set up on this server' });
|
||||
const u = new URL(req.url, 'http://x'), p = u.pathname.slice('/api/podcast'.length) || '/';
|
||||
if (p === '/discover') { // find new shows in Apple's free podcast directory (the search words go to Apple; nothing else does)
|
||||
const q = (u.searchParams.get('q') || '').trim().slice(0, 100);
|
||||
@@ -27,6 +27,14 @@ async function handle(req, res) {
|
||||
return json(res, 200, out);
|
||||
} catch { return json(res, 502, { error: 'The podcast directory did not answer' }); }
|
||||
}
|
||||
if (p === '/progress') {
|
||||
if (req.method === 'GET') return json(res, 200, prog.load());
|
||||
if (req.method !== 'POST') return json(res, 405, { error: 'Method not allowed' });
|
||||
const chunks = []; let n = 0; for await (const c of req) { n += c.length; if (n > 1e6) return json(res, 413, { error: 'Too large' }); chunks.push(c); }
|
||||
let body; try { body = JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}'); } catch { return json(res, 400, { error: 'Bad request' }); }
|
||||
return json(res, 200, prog.merge(body));
|
||||
}
|
||||
if (!BASE) return json(res, 503, { error: 'Podcasts are not set up on this server' });
|
||||
if (p === '/status') return json(res, 200, { ok: true, host: new URL(BASE).host });
|
||||
if (!ALLOWED.some(([m, re]) => m === req.method && re.test(p))) return json(res, 404, { error: 'Not found' });
|
||||
const hasBody = req.method === 'POST' || req.method === 'PATCH';
|
||||
|
||||
Reference in new issue
Block a user