added lite version
This commit is contained in:
File diff suppressed because one or more lines are too long
1
server/public/assets/master-x_buNpFo.js
Normal file
1
server/public/assets/master-x_buNpFo.js
Normal file
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Radio Master</title>
|
||||
|
||||
|
||||
<script type="module" crossorigin src="/assets/master-x_buNpFo.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/debug-B-FoNBZ5.js">
|
||||
|
||||
@@ -90,15 +90,20 @@ router.post('/:id/play/end', requireUser, (req, res) => {
|
||||
});
|
||||
|
||||
router.post('/:id/resolve', requireUser, async (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
const streams = getStreamsForStation(id);
|
||||
if (!streams.length) return res.status(404).json({ error: 'no streams' });
|
||||
const preferred = req.body?.streamId
|
||||
? streams.find((s) => s.id === Number(req.body.streamId))
|
||||
: streams[0];
|
||||
if (!preferred) return res.status(404).json({ error: 'stream not found' });
|
||||
const resolved = await resolveStream({ url: preferred.url, format: preferred.format });
|
||||
res.json({ stream: preferred, resolved });
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
const streams = getStreamsForStation(id);
|
||||
if (!streams.length) return res.status(404).json({ error: 'no streams' });
|
||||
const preferred = req.body?.streamId
|
||||
? streams.find((s) => s.id === Number(req.body.streamId))
|
||||
: streams[0];
|
||||
if (!preferred) return res.status(404).json({ error: 'stream not found' });
|
||||
const resolved = await resolveStream({ url: preferred.url, format: preferred.format });
|
||||
res.json({ stream: preferred, resolved });
|
||||
} catch (err) {
|
||||
if (err?.name === 'AbortError' || res.headersSent) return;
|
||||
res.status(500).json({ error: 'resolve failed' });
|
||||
}
|
||||
});
|
||||
|
||||
// Same-origin streaming proxy. Adds the CORS headers Icecast/SHOUTcast servers
|
||||
@@ -116,8 +121,14 @@ router.get('/:id/proxy', requireUser, async (req, res) => {
|
||||
const resolved = await resolveStream({ url: preferred.url, format: preferred.format });
|
||||
if (resolved.format === 'hls') return res.status(415).json({ error: 'hls not proxied' });
|
||||
|
||||
// Use an AbortController only for the fetch-connection phase. Once the
|
||||
// upstream connection is established the listener is removed and streaming
|
||||
// teardown switches to reader.cancel(). Keeping the controller active after
|
||||
// fetch() resolves causes undici to emit an unhandled AbortError on the
|
||||
// response-body stream when the client disconnects.
|
||||
const controller = new AbortController();
|
||||
req.on('close', () => { try { controller.abort(); } catch { } });
|
||||
const abortFetch = () => { try { controller.abort(); } catch { } };
|
||||
req.once('close', abortFetch);
|
||||
|
||||
let upstream;
|
||||
try {
|
||||
@@ -127,10 +138,14 @@ router.get('/:id/proxy', requireUser, async (req, res) => {
|
||||
headers: { 'User-Agent': 'oradio-kiosk/1.0', 'Icy-MetaData': '0' }
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.name === 'AbortError') { res.end(); return; }
|
||||
req.off('close', abortFetch);
|
||||
if (err?.name === 'AbortError') { res.end(); return; }
|
||||
if (!res.headersSent) res.status(502).json({ error: `upstream: ${err.message || err}` });
|
||||
return;
|
||||
}
|
||||
// Fetch resolved — detach the abort listener so controller.abort() is
|
||||
// never called on an already-resolved fetch.
|
||||
req.off('close', abortFetch);
|
||||
if (!upstream.ok || !upstream.body) {
|
||||
return res.status(502).json({ error: `upstream HTTP ${upstream.status}` });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user