diff --git a/app/cyp.js b/app/cyp.js index fb3c390..8f93972 100644 --- a/app/cyp.js +++ b/app/cyp.js @@ -197,7 +197,7 @@ function songList(lines) { let batch = []; while (lines.length) { let line = lines[0]; - if (line.startsWith("file:") && batch.length) { + if (line.startsWith("file:") && batch.length) { let song = linesToStruct(batch); songs.push(song); batch = []; @@ -337,7 +337,7 @@ class MPD { async albumArt(songUrl) { let data = []; let offset = 0; - let params = ["albumart", `"${escape(songUrl)}"`, offset]; + let params = ["readpicture", `"${escape(songUrl)}"`, offset]; while (1) { params[2] = offset; @@ -347,6 +347,7 @@ class MPD { let metadata = linesToStruct(lines.slice(0, 2)); if (data.length >= Number(metadata["size"])) { return data; } offset += Number(metadata["binary"]); + if (Number.isNaN(offset)) { return null; } } catch (e) { return null; } } return null; @@ -418,6 +419,7 @@ function serializeFilter(filter, operator = "==") { function createURL(ticket) { let url = new URL(location.href); url.protocol = "ws"; + if (window.location.protocol == "https:") { url.protocol = "wss"; } url.hash = ""; url.searchParams.set("ticket", ticket); return url; @@ -639,28 +641,75 @@ class App extends HTMLElement { } // DOM (using media session controls are allowed only if there is audio/video tag) - const audio = node("audio", {loop: true}, "", this); - node("source", {src: 'https://raw.githubusercontent.com/anars/blank-audio/master/10-seconds-of-silence.mp3'}, '', audio); + let divaudio = document.createElement("div") + const c_audiosrc = "live.ogg"; + const s_audiosrc = "data:audio/ogg;base64,T2dnUwACAAAAAAAAAADBYbzUAAAAAHH29ZQBE09wdXNIZWFkAQI4AYC7AAAAAABPZ2dTAAAAAAAAAAAAAMFhvNQBAAAAmpXnlgE/T3B1c1RhZ3MNAAAATGF2ZjU4Ljc2LjEwMAEAAAAeAAAAZW5jb2Rlcj1MYXZjNTguMTM0LjEwMCBsaWJvcHVzT2dnUwAAgLsAAAAAAADBYbzUAgAAACzzQrIyBwYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYIC/H5FoXgCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GT2dnUwAAAHcBAAAAAADBYbzUAwAAAE7xGeMyBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsZPZ2dTAACAMgIAAAAAAMFhvNQEAAAAZjEKVjIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxk9nZ1MAAADuAgAAAAAAwWG81AUAAAA5L4iZMgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GT2dnUwAAgKkDAAAAAADBYbzUBgAAAAALRksyBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsZPZ2dTAAAAZQQAAAAAAMFhvNQHAAAACQLc8zIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxk9nZ1MAAIAgBQAAAAAAwWG81AgAAADh/D/CMgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GT2dnUwAAANwFAAAAAADBYbzUCQAAAP+1C4MyBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsYICnizDsZPZ2dTAACAlwYAAAAAAMFhvNQKAAAA3Kuq1zIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxggKeLMOxk9nZ1MAAABTBwAAAAAAwWG81AsAAABipvi0MgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GCAp4sw7GT2dnUwAEOFQHAAAAAADBYbzUDAAAAMNyGAwBBggKeLMOxg=="; + let audio = node("audio", {autoplay: 'autoplay', playsinline: 'playsinline', controls: "controls"}, "", divaudio); + node("source", {src: c_audiosrc, type: 'audio/ogg; codecs=opus'}, '', audio); + let audio1 = node("audio", {autoplay: 'autoplay', playsinline: 'playsinline', loop: true}, "", divaudio); + node("source", {src: s_audiosrc, type: 'audio/ogg; codecs=opus'}, '', audio1); + { + let root = this; + try { root = document.getElementsByTagName("cyp-settings")[0]; } catch {}; + let d1 = document.createElement("div"); + d1.style = "margin-top: 8px; padding: 8px;"; + let divbutton = document.createElement("div"); + divbutton.style = "margin-left: 20px; display: flex;" + let btnkill = document.createElement("button"); + btnkill.innerText = "Kill audio" + btnkill.style = "margin-right: 10px;" + btnkill.onclick = function() { + if (audio) { try { audio.children[0].src = ""; } catch {}; audio.load(); audio.remove(); btnkill.hidden = true; btnlive.hidden = true; } + audio = undefined; + } + let btnlive = document.createElement("button"); + btnlive.innerText = "Back to live"; + btnlive.onclick = function() { + if (audio) { audio.load(); audio.play(); } + } + divbutton.appendChild(btnkill); + divbutton.appendChild(btnlive); + d1.appendChild(divbutton); + d1.appendChild(divaudio); + root.appendChild(d1); + } + let audio_paused = false; + let first_run = true; + audio.onplay = audio1.onplay = async (event) => { + if (first_run) { first_run=false; await audio1.play(); await audio.play(); } + if (audio.children[0].src == c_audiosrc || !audio_paused) return; + await audio1.play(); + audio_paused = false; + audio.children[0].src = c_audiosrc; + audio.loop = false; + await audio.load(); + await audio.play(); + navigator.mediaSession.playbackState = 'none'; + } + audio.onpause = audio1.onpause = async (event) => { + if (audio.children[0].src == s_audiosrc || audio_paused) return; + audio_paused = true; + audio.children[0].src = s_audiosrc; + audio.loop = true; + await audio.load(); + await audio.pause(); + await audio1.pause(); + navigator.mediaSession.playbackState = 'paused'; + } // Init event session (play audio) on click (because restrictions by web browsers) window.addEventListener('click', () => { - audio.play(); + audio1.play(); audio.play(); }, {once: true}); // mediaSession define metadata navigator.mediaSession.metadata = new MediaMetadata({ - title: 'Control Your Player' + title: 'Radio - Starting' }); // mediaSession define action handlers - navigator.mediaSession.setActionHandler('play', () => { - this.mpd.command("play"); - audio.play(); - }); - navigator.mediaSession.setActionHandler('pause', () => { - this.mpd.command("pause 1"); - audio.pause(); - }); + navigator.mediaSession.setActionHandler('play', audio.onplay); + navigator.mediaSession.setActionHandler('pause', audio.onpause); navigator.mediaSession.setActionHandler('previoustrack', () => { this.mpd.command("previous"); audio.play(); @@ -827,7 +876,7 @@ const MIME = "image/jpeg"; const STORAGE_PREFIX = `art-${artSize}` ; function store(key, data) { - localStorage.setItem(`${STORAGE_PREFIX}-${key}`, data); + try { localStorage.setItem(`${STORAGE_PREFIX}-${key}`, data); } catch (e) { console.log(e); } } function load(key) { @@ -949,6 +998,7 @@ class Player extends Component { _onAppLoad() { this._addEvents(); + this._mpd.command("binarylimit 1048576"); // 8MB max this._updateStatus(); this._updateCurrent(); this._app.addEventListener("idle-change", this); @@ -974,6 +1024,7 @@ class Player extends Component { if (data["file"] != this._current.song["file"]) { // changed song if (data["file"]) { // is there a song at all? DOM.title.textContent = data["Title"] || fileName(data["file"]); + navigator.mediaSession.metadata.title = DOM.title.textContent; DOM.subtitle.textContent = subtitle(data, {duration:false}); let duration = Number(data["duration"]); @@ -982,6 +1033,7 @@ class Player extends Component { DOM.progress.disabled = false; } else { DOM.title.textContent = ""; + navigator.mediaSession.metadata.title = "Radio - Stopped"; DOM.subtitle.textContent = ""; DOM.progress.value = 0; DOM.progress.disabled = true; @@ -994,18 +1046,21 @@ class Player extends Component { let artistOld = this._current.song["Artist"] || this._current.song["AlbumArtist"]; let albumNew = data["Album"]; let albumOld = this._current.song["Album"]; + if (data["file"]) { navigator.mediaSession.metadata.artist = artistNew || ""; navigator.mediaSession.metadata.album = albumNew || ""; } else { navigator.mediaSession.metadata.artist = ""; navigator.mediaSession.metadata.album = ""; } Object.assign(this._current.song, data); - if (artistNew != artistOld || albumNew != albumOld) { // changed album (art) + if (data["file"]) { // changed album (art) clear(DOM.art); + navigator.mediaSession.metadata.artwork = []; let src = await get(this._mpd, artistNew, data["Album"], data["file"]); if (src) { node("img", {src}, "", DOM.art); + navigator.mediaSession.metadata.artwork = [{src}]; } else { icon("music", DOM.art); } - } + } else { clear(DOM.art); icon("music", DOM.art); navigator.mediaSession.metadata.artwork = []; } } _updateElapsed() { diff --git a/index.js b/index.js index 75eb45c..7185b70 100644 --- a/index.js +++ b/index.js @@ -114,6 +114,7 @@ function onRequest(request, response) { } function requestValidator(request) { + if (request.resourceURL.query["server"]) { return false; } let ticket = request.resourceURL.query["ticket"]; let index = tickets.indexOf(ticket); if (index > -1) { diff --git a/package.json b/package.json index 7fe206a..3e446c8 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dependencies": { "custom-range": "^1.1.0", "node-static": "^0.7.11", - "ws2mpd": "^2.3.0" + "ws2mpd": "git+https://git.jerryxiao.cc/Jerry/ws2mpd.git#b223edc357" }, "devDependencies": { "less": "^3.9.0",