diff --git a/assets/audio/noise.wav b/assets/audio/noise.wav new file mode 100644 index 0000000000000000000000000000000000000000..eda297c883435c88d6b9160cfaad9056a453a56f Binary files /dev/null and b/assets/audio/noise.wav differ diff --git a/index.html b/index.html index 853657b7f957ab8e53690284e8cf379e4a542d03..5e3285961cf46a3f10f6e8e408d76ed661e28157 100644 --- a/index.html +++ b/index.html @@ -30,7 +30,7 @@ </header> <audio id="player"> - <source src="https://radio.nya.tf/listen/nya_nya_radio/emilia.flac"> + <source> </audio> <nav id="navbar"> @@ -53,7 +53,7 @@ <aside id="first"> <div id="controls"> <button id="playpause"> - <span class="fa-fw fa-solid fa-stop"></span> + <span class="fa-fw fa-solid fa-play"></span> Stopped </button> <div class="volume"> diff --git a/js/app.js b/js/app.js index c36a6c5f295393c9a27b91bd4464f146c3436b6b..eece690dc2163115b9e9530ec2ecb090dba4a1d4 100644 --- a/js/app.js +++ b/js/app.js @@ -5,6 +5,11 @@ const player = document.getElementById("player"); const audioSource = player.children.item(0); console.debug(audioSource); +// Important init stuff + +audioSource.src = "/assets/audio/noise.wav"; +let currentStation = null; + // Play/Pause button const playPauseButton = document.getElementById("playpause"); @@ -71,7 +76,7 @@ controlVolume.addEventListener("input", _ => { function setPlayPauseButtonIcon(status="stopped") { const statuses = { - stopped: "<span class='fa-fw fa-solid fa-stop'></span> Stopped", + stopped: "<span class='fa-fw fa-solid fa-play'></span> Stopped", playing: "<span class='fa-fw fa-solid fa-pause'></span> Playing", paused: "<span class='fa-fw fa-solid fa-play'></span> Paused", buffer: "<span class='fa-fw fa-solid fa-spin fa-spinner'></span> Buffering", @@ -95,8 +100,12 @@ playPauseButton.addEventListener("click", togglePlay); // Give user feedback on buffering +let noise = null; + player.addEventListener('playing', function() { console.log('Playback started.'); + noise.pause(); + noise = null; setPlayPauseButtonIcon("playing"); }); @@ -107,6 +116,10 @@ player.addEventListener('pause', function() { player.addEventListener('waiting', function() { console.log("Buffering..."); + noise = new Audio("/assets/audio/noise.wav"); + noise.play(); + noise.volume = 0.4; + noise.loop = true; setPlayPauseButtonIcon("buffer"); }); @@ -122,20 +135,51 @@ function getLocalStorageItem (item, fallback = null) { setVolume(getLocalStorageItem("volume", 100)); let stationList = JSON.parse(getLocalStorageItem("stations", "[]")); -// Creates stations -function addStation(url, name="", metadata_type="icecast", azuracast_server_url="", azuracast_station_shortname="") { +// Stations +function addStation(url, name="", metadata_type="icecast", azuracast_server_url="", azuracast_station_shortcode="") { console.log("Adding station: ", { url, name, metadata_type, azuracast_server_url, - azuracast_station_shortname + azuracast_station_shortcode }); - stationList.push({url, name, metadata_type, azuracast_server_url, azuracast_station_shortname}); + stationList.push({url, name, metadata_type, azuracast_server_url, azuracast_station_shortcode}); localStorage.setItem("stations", JSON.stringify(stationList)); } +function removeStation(station) { + console.log(stationList.indexOf(station)) + + if (stationList.length === 1) { + + stationList = []; + localStorage.setItem("stations", JSON.stringify(stationList)); + renderStationListInTuner(); + + return + } + + stationList = stationList.slice(stationList.indexOf(station), 1); + console.log(stationList); + localStorage.setItem("stations", JSON.stringify(stationList)); + + + renderStationListInTuner(); +} + +function tuneRadio(station={url:"/assets/audio/noise.wav"}) { + player.pause(); + console.log(station) + audioSource.src = station.url; + currentStation = station; + player.load(); + player.play(); + + location.hash = "#station"; +} + // View-specific code // Tuner @@ -144,9 +188,16 @@ function renderStationListInTuner() { const tunerStationList = document.getElementById("tunerStationList"); tunerStationList.innerText = ""; for (let result of stationList) { + let station = result; const newStation = document.createElement("div"); newStation.classList = "station"; - newStation.innerHTML = `<h3 class="station_title">${result.name}</h3><button class="tune_station"><span class="fa-fw fa-solid fa-play-circle"></span> Play Station</button>` + newStation.innerHTML = `<h3 class="station_title">${result.name}</h3> +<button class="tune_station"><span class="fa-fw fa-solid fa-play-circle"></span> Play Station</button> +<button class="remove_station dangerous"><span class="fa-fw fa-solid fa-trash" title="Remove Station"></span></button> +` + newStation.getElementsByClassName("tune_station")[0].onclick = _=> { tuneRadio(result) }; + newStation.getElementsByClassName("remove_station")[0].onclick = _=> { removeStation(station) }; + document.getElementById("tunerStationList").appendChild(newStation); } } @@ -165,16 +216,16 @@ document.getElementById("add_station_add_station").addEventListener("click", fun const audioURL = document.getElementById("add_station_url").value; const stationName = document.getElementById("add_station_name").value; const stationMetadataType = document.querySelector("input[name='add_metadata_type']:checked").value; - let azuracastServerURL, azuracastStationShortname; + let azuracastServerURL, azuracastStationshortcode; if (stationMetadataType === "azuracast") { azuracastServerURL = document.getElementById("add_station_azuracast_server").value; - azuracastStationShortname = document.getElementById("add_station_azuracast_shortname").value; + azuracastStationshortcode = document.getElementById("add_station_azuracast_shortcode").value; } else { azuracastServerURL = ""; - azuracastStationShortname = ""; + azuracastStationshortcode = ""; } - addStation(audioURL, stationName, stationMetadataType, azuracastServerURL, azuracastStationShortname); + addStation(audioURL, stationName, stationMetadataType, azuracastServerURL, azuracastStationshortcode); location.hash = "#tuner"; }); @@ -189,7 +240,7 @@ async function getStationsFromAzuracastServer(url) { for (let result of results) { const newStation = document.createElement("div"); newStation.classList = "station search_result"; - newStation.innerHTML = `<h3 class="station_title">${result.name}</h3><p class="description">${result.description}</p><button onclick="addStation('${result.default_url}', '${result.name}', 'azuracast', '${url}', ${result.shortname});" class="add_station"><span class="fa-fw fa-solid fa-plus-circle"></span> Add Station</button>` + newStation.innerHTML = `<h3 class="station_title">${result.name}</h3><p class="description">${result.description}</p><button onclick="addStation('${result.listen_url}', '${result.name}', 'azuracast', '${url}', '${result.shortcode}'); location.hash='#tuner';" class="add_station"><span class="fa-fw fa-solid fa-plus-circle"></span> Add Station</button>` document.getElementById("add_azuracast_results").appendChild(newStation); } } @@ -213,6 +264,29 @@ const views = { const viewFunctions = { // These run when a view is loaded. "#tuner": renderStationListInTuner, + "#station": async function () { + const stationThingy = views['#station']; + + if (!currentStation) { + stationThingy.innerHTML = `<span class="fa-fw fa-solid fa-warning"></span> Not tuned.`; + alert("You need to tune the radio first."); + location.hash = "#tuner"; + return; + } + + if (currentStation.metadata_type === "azuracast") { + let request = await fetch(currentStation.azuracast_server_url + `/api/station/${currentStation.azuracast_station_shortcode}`); + let json = await request.json(); + + stationThingy.innerHTML = ` + <h2>${json.name}</h2> + <code>${json.shortcode}</code> + <p>${json.description}</p>` + } else { + stationThingy.innerHTML = "Yeah this is icecast, metadata coming soon." + } + + } } function updateView() { diff --git a/package-lock.json b/package-lock.json index bb86fc22d376fe4e50774c9c03dd62d136525098..93a0d9eacf4b279761c9168a5f47a477fbde4ac3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "3.0.0", "dependencies": { "@fortawesome/fontawesome-free": "^6.5.1", + "livereload": "^0.9.3", "mpris-service": "^2.1.2" }, "devDependencies": { @@ -788,7 +789,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -819,7 +819,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -905,7 +904,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -1057,7 +1055,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, "funding": [ { "type": "individual", @@ -1084,7 +1081,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -2018,7 +2014,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2139,7 +2134,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -2738,7 +2732,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -2791,7 +2784,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2800,7 +2792,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2812,7 +2803,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -2982,6 +2972,48 @@ "shell-quote": "^1.8.1" } }, + "node_modules/livereload": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz", + "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "dependencies": { + "chokidar": "^3.5.0", + "livereload-js": "^3.3.1", + "opts": ">= 1.2.0", + "ws": "^7.4.3" + }, + "bin": { + "livereload": "bin/livereload.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/livereload-js": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz", + "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==" + }, + "node_modules/livereload/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -3275,7 +3307,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3416,6 +3447,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opts": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==" + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -3575,7 +3611,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3768,7 +3803,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -4658,7 +4692,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, diff --git a/package.json b/package.json index 6f50173b11f331bbadd77f032054fabc094bc510..4ef2e3e6c41fc6ff87ab3c9b8bae36775755e27c 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "author": "", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "webpack serve --open --mode development --config webpack.config.dev.js", + "start": "live-server", "electron_start": "electron .", "build": "webpack --config webpack.config.prod.js" },