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.
This commit is contained in:
@@ -11,58 +11,58 @@ const TIMEOUT = 8000;
|
||||
const UA = 'Mozilla/5.0 OnlineRadioExplorer/0.1';
|
||||
|
||||
export function probeStream(rawUrl) {
|
||||
return new Promise((resolve) => {
|
||||
let url;
|
||||
try { url = new URL(rawUrl); } catch { return resolve('err-badurl'); }
|
||||
return new Promise((resolve) => {
|
||||
let url;
|
||||
try { url = new URL(rawUrl); } catch { return resolve('err-badurl'); }
|
||||
|
||||
const isTls = url.protocol === 'https:';
|
||||
const port = Number(url.port) || (isTls ? 443 : 80);
|
||||
const path = (url.pathname || '/') + (url.search || '');
|
||||
const host = url.hostname;
|
||||
const isTls = url.protocol === 'https:';
|
||||
const port = Number(url.port) || (isTls ? 443 : 80);
|
||||
const path = (url.pathname || '/') + (url.search || '');
|
||||
const host = url.hostname;
|
||||
|
||||
const opts = { host, port, servername: host };
|
||||
const connect = isTls ? tls.connect : net.connect;
|
||||
const sock = connect(opts);
|
||||
const opts = { host, port, servername: host };
|
||||
const connect = isTls ? tls.connect : net.connect;
|
||||
const sock = connect(opts);
|
||||
|
||||
let settled = false;
|
||||
const finish = (status) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
try { sock.destroy(); } catch {}
|
||||
resolve(status);
|
||||
};
|
||||
let settled = false;
|
||||
const finish = (status) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
try { sock.destroy(); } catch { }
|
||||
resolve(status);
|
||||
};
|
||||
|
||||
sock.setTimeout(TIMEOUT);
|
||||
sock.on('timeout', () => finish('err-timeout'));
|
||||
sock.on('error', () => finish('err-fetch'));
|
||||
sock.setTimeout(TIMEOUT);
|
||||
sock.on('timeout', () => finish('err-timeout'));
|
||||
sock.on('error', () => finish('err-fetch'));
|
||||
|
||||
sock.on('connect', () => {
|
||||
const req =
|
||||
`GET ${path} HTTP/1.0\r\n` +
|
||||
`Host: ${host}\r\n` +
|
||||
`User-Agent: ${UA}\r\n` +
|
||||
`Icy-MetaData: 1\r\n` +
|
||||
`Accept: */*\r\n` +
|
||||
`Connection: close\r\n\r\n`;
|
||||
sock.write(req);
|
||||
sock.on('connect', () => {
|
||||
const req =
|
||||
`GET ${path} HTTP/1.0\r\n` +
|
||||
`Host: ${host}\r\n` +
|
||||
`User-Agent: ${UA}\r\n` +
|
||||
`Icy-MetaData: 1\r\n` +
|
||||
`Accept: */*\r\n` +
|
||||
`Connection: close\r\n\r\n`;
|
||||
sock.write(req);
|
||||
});
|
||||
|
||||
let buf = '';
|
||||
sock.on('data', (chunk) => {
|
||||
buf += chunk.toString('latin1');
|
||||
const eol = buf.indexOf('\n');
|
||||
if (eol < 0) return;
|
||||
const statusLine = buf.slice(0, eol).trim();
|
||||
// Accept: HTTP/1.x 2xx, ICY 2xx, SOURCE 2xx
|
||||
const m = statusLine.match(/^(?:HTTP\/\d\.\d|ICY|SOURCE)\s+(\d{3})/i);
|
||||
if (!m) return finish(`bad-${statusLine.slice(0, 16)}`);
|
||||
const code = Number(m[1]);
|
||||
if (code >= 200 && code < 400) finish('up');
|
||||
else finish(`http-${code}`);
|
||||
});
|
||||
|
||||
sock.on('end', () => {
|
||||
if (!settled) finish(buf ? 'err-empty' : 'err-fetch');
|
||||
});
|
||||
});
|
||||
|
||||
let buf = '';
|
||||
sock.on('data', (chunk) => {
|
||||
buf += chunk.toString('latin1');
|
||||
const eol = buf.indexOf('\n');
|
||||
if (eol < 0) return;
|
||||
const statusLine = buf.slice(0, eol).trim();
|
||||
// Accept: HTTP/1.x 2xx, ICY 2xx, SOURCE 2xx
|
||||
const m = statusLine.match(/^(?:HTTP\/\d\.\d|ICY|SOURCE)\s+(\d{3})/i);
|
||||
if (!m) return finish(`bad-${statusLine.slice(0, 16)}`);
|
||||
const code = Number(m[1]);
|
||||
if (code >= 200 && code < 400) finish('up');
|
||||
else finish(`http-${code}`);
|
||||
});
|
||||
|
||||
sock.on('end', () => {
|
||||
if (!settled) finish(buf ? 'err-empty' : 'err-fetch');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user