Add player functionality with HLS support and API integration
- Implemented a new Player class in player.js to handle audio playback, including HLS support using hls.js. - Created a shared API module in api.js for making HTTP requests with proper error handling. - Added DOM utility functions in dom.js for creating and clearing elements. - Introduced WebSocket connection handling in ws.js for real-time updates. - Developed a comprehensive CSS stylesheet for styling the application, including a high-contrast theme.
This commit is contained in:
65
server/sources/radiobrowser.js
Normal file
65
server/sources/radiobrowser.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// Thin wrapper around the Radio-Browser community API.
|
||||
// Docs: https://api.radio-browser.info/
|
||||
|
||||
const SERVERS = [
|
||||
'https://de1.api.radio-browser.info',
|
||||
'https://nl1.api.radio-browser.info',
|
||||
'https://at1.api.radio-browser.info'
|
||||
];
|
||||
|
||||
let activeServer = SERVERS[0];
|
||||
|
||||
async function rb(path, params) {
|
||||
const url = new URL(path, activeServer);
|
||||
if (params) for (const [k, v] of Object.entries(params)) {
|
||||
if (v != null) url.searchParams.set(k, String(v));
|
||||
}
|
||||
const res = await fetch(url, { headers: { 'User-Agent': 'OnlineRadioExplorer/0.1' } });
|
||||
if (!res.ok) throw new Error(`Radio-Browser ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function search({ name, country, tag, limit = 30 }) {
|
||||
const list = await rb('/json/stations/search', {
|
||||
name, country, tag, limit, hidebroken: true, order: 'votes', reverse: true
|
||||
});
|
||||
return list.map(toCanonical);
|
||||
}
|
||||
|
||||
export async function byUuid(uuid) {
|
||||
const list = await rb('/json/stations/byuuid', { uuids: uuid });
|
||||
return list[0] ? toCanonical(list[0]) : null;
|
||||
}
|
||||
|
||||
function detectFormat(codec, url) {
|
||||
const c = (codec || '').toLowerCase();
|
||||
if (c.includes('mp3')) return 'mp3';
|
||||
if (c.includes('aac')) return 'aac';
|
||||
if (c.includes('ogg') || c.includes('vorbis') || c.includes('opus')) return 'ogg';
|
||||
if (url?.endsWith('.m3u8')) return 'hls';
|
||||
if (url?.endsWith('.m3u')) return 'm3u';
|
||||
if (url?.endsWith('.pls')) return 'pls';
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function toCanonical(s) {
|
||||
return {
|
||||
uuid: s.stationuuid || undefined,
|
||||
name: s.name,
|
||||
slug: `rb-${s.stationuuid}`,
|
||||
homepage: s.homepage || null,
|
||||
country: s.countrycode || s.country || null,
|
||||
genres: (s.tags || '').split(',').map((t) => t.trim()).filter(Boolean),
|
||||
description: null,
|
||||
image_url: s.favicon || null,
|
||||
source: 'radiobrowser',
|
||||
source_ref: s.stationuuid,
|
||||
streams: [{
|
||||
url: s.url_resolved || s.url,
|
||||
format: detectFormat(s.codec, s.url_resolved || s.url),
|
||||
bitrate: s.bitrate || null,
|
||||
label: s.codec ? `${s.codec} ${s.bitrate || ''}`.trim() : null,
|
||||
priority: 0
|
||||
}]
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user