index.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <html lang="en">
  2. <head>
  3. <meta charset="utf-8">
  4. <meta name="theme-color" content="#ffffff">
  5. <title>Test player</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <link rel="stylesheet" type="text/css" href="/player.css">
  8. </head>
  9. <body>
  10. <audio id="radio" controls></audio>
  11. <div id="player">
  12. <div id="contents" hidden>
  13. <div id="menu" class="tags">
  14. <input type="radio" name="tab-selector" id="tab-other-radios" checked/>
  15. <label for="tab-other-radios"> Altre Radio</label>
  16. <input type="radio" name="tab-selector" id="tab-programmi"/>
  17. <label for="tab-programmi"> Programmi</label>
  18. <input type="radio" name="tab-selector" id="tab-playlist"/>
  19. <label for="tab-playlist"> Playlist</label>
  20. </div>
  21. <div class="tabs">
  22. <div id="other-radios" class="tags">
  23. </div>
  24. <div id="programmi" class="hidden">
  25. <div class="show-list">
  26. <img id="prev-show" class="arrow arrow-left" alt="Scorri a sinistra" src="/icons/ico-arrow.png" />
  27. <div id="programmi-lista"></div>
  28. <img id="next-show" class="arrow arrow-right" alt="Scorri a destra" src="/icons/ico-arrow.png" />
  29. </div>
  30. <div class="show-content">
  31. <img id="prev-show-card" class="arrow arrow-left" alt="Scorri a sinistra" src="/icons/ico-arrow.png"/>
  32. <div id="programmi-content">
  33. <template id="show-card-template">
  34. <div class="show-card">
  35. <span class="title"></span>
  36. <div class="small-tags"></div><br>
  37. <div class="description"></div>
  38. <div class="controls">
  39. <img src="/icons/plus.png" class="add-to-playlist">
  40. <img src="/icons/playIcon.svg" class="play-pause">
  41. </div>
  42. </div>
  43. </template>
  44. </div>
  45. <img id="next-show-card" class="arrow arrow-right" alt="Scorri a destra" src="/icons/ico-arrow.png"/>
  46. </div>
  47. </div>
  48. <div id="playlist" class="hidden">
  49. Qui ci va la playlist, ancora non puoi riprodurla, però puoi scaricarle l'<span onclick="downloadPlaylist"><b>m3u 📥</b></span>
  50. </div>
  51. </div>
  52. </div>
  53. <div id="controls" class="controls">
  54. <div class="controlBlock controlLeft">
  55. <div class="info-content">
  56. <span id="radio-name">Radio Spore</span>
  57. <br/>
  58. <span id="program-name">Un programma a caso</span>
  59. </div>
  60. </div>
  61. <div class="controlBlock controlCenter">
  62. <span id="prev" class="prev-step control">
  63. <img alt="Indietro di 15 secondi" src="/icons/15_sec_prima.svg">
  64. </span>
  65. <span id="play" class="play-pause control" label="Premi per iniziare la riproduzione del player">
  66. <img alt="Ascolta la diretta" src="/icons/playIcon.svg">
  67. </span>
  68. <span id="next" class="prev-step control">
  69. <img alt="Avanti di 15 secondi" src="/icons/15_sec_dopo.svg">
  70. </span>
  71. </div>
  72. <div class="controlBlock controlRight">
  73. <div>
  74. <span class="is-live live">· LIVE</span>
  75. <span class="back-to-live live hidden">TORNA AL LIVE</span>
  76. <img id="volume-on" src="/icons/volume.svg" />
  77. <img id="volume-off" src="/icons/volume_off.svg" class="hidden"/>
  78. <input id="volume" type="range" min="0" max="1" step="0.01" value="1" />
  79. <img id="contents-button" class="arrow-up" alt="Apri il player" src="/icons/ico-arrow.png" />
  80. </div>
  81. </div>
  82. </div>
  83. </div>
  84. <script>
  85. let leRadio = [
  86. { "name": "Radio Spore", "stream": "https://stream.radiospore.oziosi.org:8003/spore.ogg" },
  87. { "name": "Radio Wombat", "stream": "https://s.streampunk.cc/wombat.ogg"},
  88. { "name": "RadioGramma", "stream": "https://stream.radiospore.oziosi.org:8003/radiogramma.ogg"},
  89. { "name": "RadioForte", "stream": "http://stream.s.streampunk.cc/player/RadioForte.ogg"},
  90. { "name": "RadioEustachio", "stream": "https://stream.radiospore.oziosi.org:8003/radioeustachio.ogg"}]
  91. let playlist = []
  92. let radio = document.querySelector("#radio");
  93. leRadio = leRadio.map((r) => {
  94. r.stream += "?" + Math.floor((Math.random() * 10000) + 1)
  95. return r
  96. });
  97. radio.src = leRadio[0].stream
  98. const playButton = document.querySelector("#play")
  99. const nextButton = document.querySelector("#next")
  100. const prevButton = document.querySelector("#prev")
  101. const contents = document.querySelector("#contents")
  102. const contentsButton = document.querySelector("#contents-button")
  103. const volumeOn = document.querySelector("#volume-on")
  104. const volumeOff = document.querySelector("#volume-off")
  105. const showContentsEl = document.querySelector("#programmi-content")
  106. const showListEl = document.querySelector("#programmi-lista")
  107. let currentStatus
  108. async function playPauseRadio() {
  109. try {
  110. if(radio.paused) {
  111. await radio.play();
  112. }
  113. else {
  114. await radio.pause();
  115. }
  116. }
  117. catch(err) {
  118. console.log(err);
  119. }
  120. }
  121. function onVolumeChange(e) {
  122. if(e.target.value == 0) {
  123. volumeOn.classList.add("hidden");
  124. volumeOff.classList.remove("hidden")
  125. }
  126. else {
  127. volumeOff.classList.add("hidden");
  128. volumeOn.classList.remove("hidden")
  129. }
  130. radio.volume = e.target.value;
  131. }
  132. function setupTabs() {
  133. for(let el of document.querySelectorAll("#menu > input")){
  134. console.log(el)
  135. el.addEventListener('change', (e) => {
  136. for(let ce of document.querySelectorAll("#menu > input")) {
  137. if(ce.checked) {
  138. document.querySelector("#"+ce.id.split('-').slice(1).join('-')).classList.remove("hidden")
  139. }
  140. else {
  141. document.querySelector("#"+ce.id.split('-').slice(1).join('-')).classList.add("hidden")
  142. }
  143. }
  144. })
  145. }
  146. }
  147. function setupPlayer() {
  148. playButton.addEventListener("click", playPauseRadio, false);
  149. volumeOff.addEventListener("click", (e) => {
  150. volume.disabled = false;
  151. e.target.classList.add("hidden");
  152. volumeOn.classList.remove("hidden");
  153. radio.volume = volume.value;
  154. });
  155. volumeOn.addEventListener("click", (e) => {
  156. volume.disabled = true;
  157. e.target.classList.add("hidden");
  158. volumeOff.classList.remove("hidden");
  159. radio.volume = 0;
  160. });
  161. volume.addEventListener('change', onVolumeChange);
  162. contentsButton.addEventListener("click", (e) => {
  163. contents.hidden = !contents.hidden;
  164. e.target.classList.toggle("arrow-up");
  165. })
  166. let showBackToLive = () => {
  167. document.querySelector(".is-live").classList.add("hidden");
  168. document.querySelector(".back-to-live").classList.remove("hidden");
  169. }
  170. prevButton.addEventListener("click",(e) => {
  171. radio.currentTime -= 15;
  172. showBackToLive()
  173. }, false)
  174. nextButton.addEventListener("click",(e) => {
  175. radio.currentTime += 15;
  176. showBackToLive()
  177. }, false)
  178. radio.addEventListener('pause', (e) => {
  179. showBackToLive()
  180. })
  181. document.querySelector(".back-to-live").addEventListener('click', (e) => {
  182. document.querySelector(".is-live").classList.remove("hidden")
  183. e.target.classList.add("hidden")
  184. radio.currentTime = radio.duration
  185. radio.play()
  186. })
  187. }
  188. function downloadPlaylist() {
  189. let m3u = "#EXTM3U\n"
  190. m3u = playlist.reduce((data, track) => {
  191. data += `#EXTINF:${track.itunes.duration},${track.itunes.name} \n`
  192. data += `${track.guid}\n`
  193. return data
  194. }, m3u)
  195. console.log(m3u)
  196. const blob = new Blob([m3u], { 'type': 'audio/x-mpegurl'});
  197. const m3uFile = window.URL.createObjectURL(blob);
  198. var a = document.createElement("a");
  199. a.style = "display: none";
  200. document.body.appendChild(a);
  201. url = window.URL.createObjectURL(blob);
  202. a.href = url;
  203. a.download = "playlist.m3u";
  204. a.click();
  205. window.URL.revokeObjectURL(url);
  206. }
  207. function setupRadios() {
  208. for(let r of leRadio) {
  209. let el = document.createElement("span")
  210. el.innerHTML = r.name;
  211. el.addEventListener('click', (e) => {
  212. radio.src = r.stream;
  213. document.querySelector("#radio-name").textContent = r.name
  214. radio.play()
  215. })
  216. document.querySelector("#other-radios").appendChild(el)
  217. }
  218. }
  219. function setupShowButtons() {
  220. let scrollFwd = (el) => {
  221. let scrollAmount = 0;
  222. let timer = setInterval( () => {
  223. el.scrollLeft += 10;
  224. scrollAmount += 10;
  225. if(scrollAmount >= 100){
  226. window.clearInterval(timer);
  227. }
  228. }, 25);
  229. }
  230. let scrollBack = (el) => {
  231. let scrollAmount = 0;
  232. let timer = setInterval( () => {
  233. el.scrollLeft -= 10;
  234. scrollAmount -= 10;
  235. if(scrollAmount <= -100){
  236. window.clearInterval(timer);
  237. }
  238. }, 25);
  239. }
  240. document.getElementById("prev-show").addEventListener("click", (e) => {
  241. scrollBack(showListEl)
  242. })
  243. document.getElementById("next-show").addEventListener("click", (e) => {
  244. scrollFwd(showListEl)
  245. })
  246. document.getElementById("next-show-card").addEventListener("click", (e) => {
  247. scrollFwd(showContentsEl.querySelector("div.content-box:not(.hidden)"))
  248. })
  249. document.getElementById("prev-show-card").addEventListener("click", (e) => {
  250. scrollBack(showContentsEl.querySelector("div.content-box:not(.hidden)"))
  251. })
  252. }
  253. async function setupProgrammi() {
  254. const res = await fetch("https://www.arkiwi.org/path64/cmFkaW9zcG9yZQ/shows");
  255. const shows = Array.from(await new window.DOMParser().parseFromString(await res.text(), "text/xml").getElementsByTagName("show"));
  256. programmi = shows.map(show => Array.from(show.childNodes).reduce((data, node) => {
  257. if(node.localName) {
  258. data[node.localName] = node.textContent // + ( node.localName == 'feed' ? '?limit=6' : '')
  259. }
  260. return data;
  261. }, {}))
  262. for(let p of programmi) {
  263. let el = document.createElement("span")
  264. el.innerHTML = p.name;
  265. el.addEventListener('click', (e) => {
  266. const id = p.name.replaceAll(/[\W_]+/g," ").replaceAll(' ','-');
  267. for(let box of document.getElementById("programmi-content").getElementsByClassName("content-box")) {
  268. if(box.id != id) {
  269. box.classList.add("hidden");
  270. }
  271. }
  272. console.log(id)
  273. document.getElementById(id).classList.toggle("hidden");
  274. })
  275. showListEl.appendChild(el)
  276. fetch(p.feed)
  277. .then(res => res.text())
  278. .then(res => new window.DOMParser().parseFromString(res, "text/xml"))
  279. .then(xml => {
  280. let data = Array.from(xml.getElementsByTagName("item")).
  281. map(item => Array.from(item.childNodes)
  282. .reduce((data, node) => {
  283. if(node.localName) {
  284. if(node.prefix){
  285. if(!data[node.prefix]) {
  286. data[node.prefix] = {}
  287. }
  288. data[node.prefix][node.localName] = node.textContent
  289. }
  290. else {
  291. data[node.localName] = node.textContent
  292. // if(localName == "pubDate") {
  293. // data[node.localName] = new Date(data[node.localName])
  294. // }
  295. }}
  296. return data;
  297. }, {}))
  298. let programma = document.createElement("div")
  299. programma.setAttribute("id", p.name.replaceAll(/[\W_]+/g," ").replaceAll(' ', '-'));
  300. programma.classList.add("content-box");
  301. programma.classList.add("hidden");
  302. document.getElementById("programmi-content").append(programma);
  303. for(let s of data) {
  304. let template = document.querySelector("#show-card-template");
  305. let puntata = template.content.cloneNode(true);
  306. let keywords = ''
  307. if(s['itunes'] && s.itunes.keywords) {
  308. s['itunes']['keywords'].split(',').map((k) => `<span>${k}</span>`).join('')
  309. }
  310. puntata.querySelector(".title").textContent = s.title;
  311. puntata.querySelector(".description").textContent = s.description;
  312. //puntata.innerHTML = `<span><b>${data[a].title}</b></span> <div class="small-tags">${keywordso}</div> <br/> <br/><div>${data[a].description}</div>`;
  313. puntata.querySelector(".play-pause").addEventListener('click', (e) => {
  314. radio.src = s.guid;
  315. playPauseRadio();
  316. document.querySelector("#radio-name").textContent = p.name;
  317. document.querySelector("#program-name").textContent = s.title;
  318. })
  319. puntata.querySelector(".add-to-playlist").addEventListener('click', (e) => {
  320. playlist.push(s)
  321. })
  322. programma.appendChild(puntata)
  323. }
  324. })
  325. }
  326. }
  327. setupPlayer();
  328. setupRadios();
  329. setupTabs();
  330. setupShowButtons();
  331. setupProgrammi();
  332. </script>
  333. </body>
  334. </html>