Files
radio-explorer/server/streams/resolver.js
Marco Mooren 00246389bc Add API documentation and underground station importer
- Introduced a new HTML documentation page for the oradio API, including a JavaScript file to handle dynamic content and API requests.
- Added a CSS file for styling the documentation page.
- Implemented an underground station importer script that fetches data from Radio-Browser and writes it to a JSON file.
- Created a stats module to compute and manage vote and play statistics for radio stations.
- Added a polyfill for modulepreload to ensure compatibility with older browsers.
2026-05-11 02:06:48 +02:00

41 lines
1.5 KiB
JavaScript

// Resolve playlist files (.pls / .m3u) to a direct stream URL.
// HLS (.m3u8) is left as-is so hls.js can fetch it.
export function detectFormatFromUrl(url) {
const u = url.toLowerCase().split('?')[0];
if (u.endsWith('.m3u8')) return 'hls';
if (u.endsWith('.m3u')) return 'm3u';
if (u.endsWith('.pls')) return 'pls';
if (u.endsWith('.aac')) return 'aac';
if (u.endsWith('.mp3')) return 'mp3';
if (u.endsWith('.ogg') || u.endsWith('.opus')) return 'ogg';
return 'unknown';
}
function parsePls(text) {
const m = text.match(/^File\d+\s*=\s*(.+)$/im);
return m ? m[1].trim() : null;
}
function parseM3u(text) {
const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
return lines.find((l) => !l.startsWith('#')) || null;
}
export async function resolveStream({ url, format }) {
const fmt = format && format !== 'unknown' ? format : detectFormatFromUrl(url);
if (fmt === 'pls' || fmt === 'm3u') {
try {
const res = await fetch(url, { redirect: 'follow' });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
const direct = fmt === 'pls' ? parsePls(text) : parseM3u(text);
if (!direct) throw new Error('No direct URL found in playlist');
return { url: direct, format: detectFormatFromUrl(direct) };
} catch (err) {
return { url, format: fmt, error: String(err.message || err) };
}
}
return { url, format: fmt };
}