'use strict'; // ---- Radio: The Journey FM on top, your saved stations, and a search of thousands more (Radio Browser). The hub passes the stream on (and reads the song title // out of it), so stations that only offer plain http still play, and everything plays in the same player as Music. const rdoApi = (p, opts) => jfetch('/api/radio' + p, opts); const rdoOpt = (m, o) => ({ method: m, headers: { 'content-type': 'application/json' }, body: o ? JSON.stringify(o) : undefined }); const rdoIcon = s => s.favicon ? '/api/radio/icon?u=' + encodeURIComponent(s.favicon) : ''; const rdoTrack = s => ({ key: 'radio:' + s.id, title: s.name, artist: 'Live radio', album: [s.state, s.country].filter(Boolean).join(', '), thumb: rdoIcon(s), src: '/api/radio/play/' + encodeURIComponent(s.id), nocache: true, dur: 0, station: s.name }); const rdoPlaying = () => { const t = cur(); return t && String(t.key).startsWith('radio:') ? t : null; }; const rdoMeta = s => [s.tags && s.tags.split(',').slice(0, 2).join(', '), s.state || s.country, s.codec && (s.codec + (s.bitrate ? ' ' + s.bitrate + 'k' : ''))].filter(Boolean).join(' · '); const rdoChips = [['Christian', 'christian'], ['Worship', 'worship'], ['Gospel', 'gospel'], ['Talk', 'talk'], ['News', 'news'], ['Classical', 'classical'], ['Jazz', 'jazz'], ['Country', 'country'], ['Oldies', 'oldies']]; let rdoSaved = [], rdoNow = {}; /** While a station plays, ask what song is on every 20 seconds and show it in the player. */ async function rdoPoll() { const t = rdoPlaying(); if (!t) return; const id = t.key.slice(6); try { const n = await rdoApi('/now/' + encodeURIComponent(id)); if (!rdoPlaying() || rdoPlaying().key !== t.key) return; rdoNow[id] = n; t.title = n.title || t.station; t.artist = n.title ? (n.artist || t.station) : 'Live radio'; t.album = n.title ? t.station : t.album; if (typeof updateNow === 'function') updateNow(); const el = document.getElementById('rdonow'); if (el && rdoPlaying()) el.dataset.id === id && rdoDrawNow(el, n); } catch {} } setInterval(rdoPoll, 20000); pl.a.addEventListener('playing', () => { if (rdoPlaying()) setTimeout(rdoPoll, 1500); }); function rdoDrawNow(el, n) { el.innerHTML = n && n.title ? `Now playing${esc(n.title)}${n.artist ? ' · ' + esc(n.artist) + '' : ''}` : 'Live'; } async function viewRadio() { const v = $('#view'); v.innerHTML = '
Loading…
${esc(e.message)}
${esc(rdoMeta(j))}
Searching…
' : 'No stations found.
'; bindRows($('#rdores'), id => S.items.find(x => x.id === id)); $('#rdomore').innerHTML = S.more && !S.busy ? '' : ''; if ($('#rdomb')) $('#rdomb').onclick = () => run(false); }; const drawAll = () => { drawMine(); drawRes(); }; const run = async reset => { const my = ++S.seq; if (reset) { S.off = 0; S.items = []; } S.busy = true; if (reset) drawRes(); try { const r = await rdoApi(`/search?q=${encodeURIComponent(S.q)}&tag=${encodeURIComponent(S.q ? '' : S.tag)}&offset=${S.off}`); if (my !== S.seq) return; S.items = S.items.concat(r.items); S.more = r.more; S.off += 30; } catch (e) { if (my === S.seq) { $('#rdores').innerHTML = `${esc(e.message)}
`; S.busy = false; return; } } if (my === S.seq) { S.busy = false; drawRes(); } }; $('#rdojp').onclick = () => playStation(j); $('#rdojr').onclick = async () => { const el = openSheet('Recently played', j.name, 'Loading…
'); try { const n = await rdoApi('/now/journeyfm'); el.querySelector('.empty').outerHTML = n.recent.length ? `Nothing to show right now.
'; } catch (e) { el.querySelector('.empty').outerHTML = `${esc(e.message)}
`; } }; $('#rdochips').querySelectorAll('a').forEach(a => a.onclick = e => { e.preventDefault(); S.tag = a.dataset.t; S.q = ''; $('#rdoq').value = ''; $('#rdochips').querySelectorAll('a').forEach(x => x.classList.toggle('on', x === a)); run(true); }); let t; $('#rdoq').oninput = e => { clearTimeout(t); t = setTimeout(() => { S.q = e.target.value.trim(); $('#rdochips').querySelectorAll('a').forEach(x => x.classList.toggle('on', !S.q && x.dataset.t === S.tag)); run(true); }, 450); }; drawMine(); run(true); rdoApi('/now/journeyfm').then(n => { const el = $('#rdonow'); if (el) { rdoNow.journeyfm = n; rdoDrawNow(el, n); } }).catch(() => {}); }