Podcast: built-in screen for Podgrab (shows, episodes, playback, search)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7f38ac5366
commit
675df5710b
13 files changed
+177
-19
No files matched your search
@@ -0,0 +1,32 @@
|
||||
// Podcast proxy: only the allowed Podgrab calls get through, the Podgrab password is added server-side, audio ranges and redirects work.
|
||||
const http = require('http'), assert = require('assert');
|
||||
const seen = [];
|
||||
const fake = http.createServer((req, res) => {
|
||||
let b = ''; req.on('data', c => b += c); req.on('end', () => {
|
||||
seen.push({ m: req.method, u: req.url, auth: req.headers.authorization, range: req.headers.range, b });
|
||||
if (req.url === '/podcasts') { res.writeHead(200, { 'content-type': 'application/json' }); return res.end('[{"ID":"p1","Title":"Show"}]'); }
|
||||
if (req.url === '/podcastitems/e1/file') { res.writeHead(206, { 'content-type': 'audio/mpeg', 'content-range': 'bytes 0-3/10', 'content-length': '4' }); return res.end('abcd'); }
|
||||
if (req.url === '/podcastitems/e2/file') { res.writeHead(302, { location: 'https://cdn.example.com/a.mp3' }); return res.end(); }
|
||||
res.writeHead(200, { 'content-type': 'application/json' }); res.end('{}');
|
||||
});
|
||||
}).listen(0, async () => {
|
||||
process.env.PODCAST_URL = 'http://127.0.0.1:' + fake.address().port + '/'; process.env.PODGRAB_PASSWORD = 'secret';
|
||||
const podcast = require('../podcast');
|
||||
const hub = http.createServer((req, res) => podcast.handle(req, res)).listen(0, async () => {
|
||||
const base = 'http://127.0.0.1:' + hub.address().port, get = (p, o = {}) => fetch(base + '/api/podcast' + p, { redirect: 'manual', ...o });
|
||||
try {
|
||||
let r = await get('/podcasts'); assert.equal(r.status, 200); assert.equal((await r.json())[0].Title, 'Show');
|
||||
assert.equal(seen[0].auth, 'Basic ' + Buffer.from('podgrab:secret').toString('base64'), 'password added by the server');
|
||||
r = await get('/podcastitems/e1/file', { headers: { range: 'bytes=0-3' } }); assert.equal(r.status, 206); assert.equal(await r.text(), 'abcd'); assert.equal(seen.at(-1).range, 'bytes=0-3');
|
||||
r = await get('/podcastitems/e2/file'); assert.equal(r.status, 302); assert.equal(r.headers.get('location'), 'https://cdn.example.com/a.mp3');
|
||||
r = await get('/podcasts', { method: 'POST', body: '{"url":"https://x.test/feed"}' }); assert.equal(r.status, 200); assert.equal(seen.at(-1).b, '{"url":"https://x.test/feed"}');
|
||||
const n = seen.length;
|
||||
for (const [m, p] of [['GET', '/settings'], ['POST', '/settings'], ['GET', '/opml'], ['DELETE', '/tags/1'], ['GET', '/backups'], ['GET', '/podcasts/../settings'], ['DELETE', '/podcastitems/e1']]) {
|
||||
r = await get(p, { method: m }); assert.equal(r.status, 404, m + ' ' + p);
|
||||
}
|
||||
assert.equal(seen.length, n, 'blocked calls never reach Podgrab');
|
||||
console.log('podcast ok');
|
||||
} catch (e) { console.error(e); process.exitCode = 1; }
|
||||
hub.close(); fake.close();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user