player.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. let leRadio = [
  2. { "name": "Radio Onda Rossa", "website": "https://www.ondarossa.info"},
  3. { "name": "Radio Spore", "website": "https://radiospore.oziosi.org" },
  4. { "name": "Radio Gramma", "website": "https://test.radiogramma.org" },
  5. ]
  6. let playlist = []
  7. let audio = document.querySelector("#radio");
  8. /* Avoids "mixed content" problems, hoping for the best */
  9. function httpsIze(url) {
  10. if(location.protocol === 'https:' && url.startsWith('http:')) {
  11. return url.replace('http:', 'https:')
  12. }
  13. return url
  14. }
  15. class Player {
  16. constructor(audioEl) {
  17. this.audioEl = audioEl
  18. }
  19. changeStreamURLs(urls) {
  20. this.audioEl.pause()
  21. this.audioEl.innerHTML = ''
  22. urls.forEach((url) => {
  23. let src = document.createElement('source')
  24. src.setAttribute('src', url)
  25. this.audioEl.appendChild(src)
  26. })
  27. this.audioEl.load()
  28. this.audioEl.play()
  29. }
  30. }
  31. function downloadPlaylist() {
  32. let m3u = "#EXTM3U\n"
  33. m3u = playlist.reduce((data, track) => {
  34. data += `#EXTINF:${track.itunes.duration},${track.itunes.name} \n`
  35. data += `${track.enclosure}\n`
  36. return data
  37. }, m3u)
  38. console.log(m3u)
  39. const blob = new Blob([m3u], { 'type': 'audio/x-mpegurl'});
  40. const m3uFile = window.URL.createObjectURL(blob);
  41. var a = document.createElement("a");
  42. a.style = "display: none";
  43. document.body.appendChild(a);
  44. url = window.URL.createObjectURL(blob);
  45. a.href = url;
  46. a.download = "playlist.m3u";
  47. a.click();
  48. window.URL.revokeObjectURL(url);
  49. }
  50. async function main() {
  51. let radio = null
  52. let player = new Player(audio)
  53. async function deployRadio(website) {
  54. radio = await radiomanifest.get(httpsIze(website))
  55. document.querySelector('#radio-name').textContent = 'Loading ...'
  56. document.querySelector('#program-name').textContent = 'Loading ...'
  57. let urls = await radio.getStreaming().pickURLs()
  58. urls = urls.map(httpsIze)
  59. player.changeStreamURLs(urls)
  60. document.querySelector('#radio-name').textContent = radio.getName()
  61. let showName = ''
  62. try {
  63. const show = radio.getShowAtTime()
  64. if (show !== null) {
  65. showName = show.getName()
  66. }
  67. } catch {
  68. true;
  69. }
  70. document.querySelector('#program-name').textContent = showName
  71. setupProgrammi()
  72. }
  73. const playButton = document.querySelector("#play")
  74. const nextButton = document.querySelector("#next")
  75. const prevButton = document.querySelector("#prev")
  76. const contents = document.querySelector("#contents")
  77. const contentsButton = document.querySelector("#contents-button")
  78. const volumeOn = document.querySelector("#volume-on")
  79. const volumeOff = document.querySelector("#volume-off")
  80. const showContentsEl = document.querySelector("#programmi-content")
  81. const showListEl = document.querySelector("#programmi-lista")
  82. let currentStatus
  83. async function playPauseRadio() {
  84. try {
  85. if(audio.paused) {
  86. await audio.play();
  87. }
  88. else {
  89. await audio.pause();
  90. }
  91. }
  92. catch(err) {
  93. console.log(err);
  94. }
  95. }
  96. function onVolumeChange(e) {
  97. if(e.target.value == 0) {
  98. volumeOn.classList.add("hidden");
  99. volumeOff.classList.remove("hidden")
  100. }
  101. else {
  102. volumeOff.classList.add("hidden");
  103. volumeOn.classList.remove("hidden")
  104. }
  105. audio.volume = e.target.value;
  106. }
  107. function setupTabs() {
  108. for(let el of document.querySelectorAll("#menu > input")){
  109. console.log(el)
  110. el.addEventListener('change', (e) => {
  111. for(let ce of document.querySelectorAll("#menu > input")) {
  112. var selector = "#"+ce.id.split('-').slice(1).join('-')
  113. if(ce.checked) {
  114. document.querySelector(selector).classList.remove("hidden")
  115. }
  116. else {
  117. document.querySelector(selector).classList.add("hidden")
  118. }
  119. }
  120. })
  121. }
  122. }
  123. function setupPlayer() {
  124. playButton.addEventListener("click", playPauseRadio, false);
  125. volumeOff.addEventListener("click", (e) => {
  126. volume.disabled = false;
  127. e.target.classList.add("hidden");
  128. volumeOn.classList.remove("hidden");
  129. audio.volume = volume.value;
  130. });
  131. volumeOn.addEventListener("click", (e) => {
  132. volume.disabled = true;
  133. e.target.classList.add("hidden");
  134. volumeOff.classList.remove("hidden");
  135. audio.volume = 0;
  136. });
  137. volume.addEventListener('change', onVolumeChange);
  138. contentsButton.addEventListener("click", (e) => {
  139. contents.hidden = !contents.hidden;
  140. e.target.classList.toggle("arrow-up");
  141. })
  142. let showBackToLive = () => {
  143. document.querySelector(".is-live").classList.add("hidden");
  144. document.querySelector(".back-to-live").classList.remove("hidden");
  145. }
  146. prevButton.addEventListener("click",(e) => {
  147. audio.currentTime -= 15;
  148. showBackToLive()
  149. }, false)
  150. nextButton.addEventListener("click",(e) => {
  151. audio.currentTime += 15;
  152. showBackToLive()
  153. }, false)
  154. audio.addEventListener('pause', (e) => {
  155. showBackToLive()
  156. })
  157. document.querySelector(".back-to-live").addEventListener('click', (e) => {
  158. document.querySelector(".is-live").classList.remove("hidden")
  159. e.target.classList.add("hidden")
  160. audio.currentTime = audio.duration
  161. audio.play()
  162. })
  163. }
  164. function setupRadios() {
  165. for(let r of leRadio) {
  166. let el = document.createElement("span")
  167. el.dataset['website'] = r.website
  168. el.textContent = r.name;
  169. el.addEventListener('click', async (e) => {
  170. console.log('radio selected', r.website)
  171. await deployRadio(r.website)
  172. })
  173. document.querySelector("#other-radios").appendChild(el)
  174. }
  175. }
  176. function setupShowButtons() {
  177. let scrollFwd = (el) => {
  178. let scrollAmount = 0;
  179. let timer = setInterval( () => {
  180. el.scrollLeft += 10;
  181. scrollAmount += 10;
  182. if(scrollAmount >= 100){
  183. window.clearInterval(timer);
  184. }
  185. }, 25);
  186. }
  187. let scrollBack = (el) => {
  188. let scrollAmount = 0;
  189. let timer = setInterval( () => {
  190. el.scrollLeft -= 10;
  191. scrollAmount -= 10;
  192. if(scrollAmount <= -100){
  193. window.clearInterval(timer);
  194. }
  195. }, 25);
  196. }
  197. document.getElementById("prev-show").addEventListener("click", (e) => {
  198. scrollBack(showListEl)
  199. })
  200. document.getElementById("next-show").addEventListener("click", (e) => {
  201. scrollFwd(showListEl)
  202. })
  203. document.getElementById("next-show-card").addEventListener("click", (e) => {
  204. scrollFwd(showContentsEl.querySelector("div.content-box:not(.hidden)"))
  205. })
  206. document.getElementById("prev-show-card").addEventListener("click", (e) => {
  207. scrollBack(showContentsEl.querySelector("div.content-box:not(.hidden)"))
  208. })
  209. }
  210. async function setupProgrammi() {
  211. console.log('programmi', radio)
  212. if(radio === null) return;
  213. const programmi = radio.getShows();
  214. if(programmi === null || programmi === undefined) return;
  215. document.querySelectorAll('#programmi-lista > span').forEach((el) => { el.remove() })
  216. for(let p of programmi) {
  217. let el = document.createElement("span")
  218. el.innerHTML = p.getName();
  219. showListEl.appendChild(el)
  220. el.addEventListener('click', (e) => {
  221. console.log("Hai clickato su", p.getName())
  222. document.querySelectorAll("#programmi-content > .content-box").forEach((el) => {
  223. el.remove()
  224. })
  225. document.querySelectorAll("#programmi > .show-content > .arrow").forEach((el) => {
  226. el.classList.remove("hidden")
  227. })
  228. fetch(httpsIze(p.getFeed()))
  229. .then(res => res.text())
  230. .then(res => new window.DOMParser().parseFromString(res, "text/xml"))
  231. .then(xml => {
  232. // XXX: we'd need a proper podcast-parsing library here
  233. let data = Array.from(xml.getElementsByTagName("item")).
  234. map(item => Array.from(item.childNodes)
  235. .reduce((data, node) => {
  236. if(node.localName) {
  237. if(node.prefix){
  238. if(!data[node.prefix]) {
  239. data[node.prefix] = {}
  240. }
  241. data[node.prefix][node.localName] = node.textContent
  242. }
  243. else {
  244. let val = node.textContent
  245. if(node.localName === 'enclosure') {
  246. val = node.getAttribute('url', '')
  247. }
  248. data[node.localName] = val
  249. // if(localName == "pubDate") {
  250. // data[node.localName] = new Date(data[node.localName])
  251. // }
  252. }}
  253. return data;
  254. }, {}))
  255. console.log(data)
  256. let programma = document.createElement("div")
  257. programma.setAttribute("id", p.name.replaceAll(/[\W_]+/g," ").replaceAll(' ', '-'));
  258. programma.classList.add("content-box");
  259. document.getElementById("programmi-content").append(programma);
  260. for(let s of data) {
  261. let template = document.querySelector("#show-card-template");
  262. let puntata = template.content.cloneNode(true);
  263. let keywords = ''
  264. if(s['itunes'] && s.itunes.keywords) {
  265. s['itunes']['keywords'].split(',').map((k) => `<span>${k}</span>`).join('')
  266. }
  267. puntata.querySelector(".title").textContent = s.title;
  268. puntata.querySelector(".description").textContent = s.describtion ? s.description.substr(0, 150) : "";
  269. puntata.querySelector(".play-pause").addEventListener('click', (e) => {
  270. console.log(httpsIze(s.enclosure))
  271. player.changeStreamURLs([httpsIze(s.enclosure)])
  272. document.querySelector("#radio-name").textContent = p.getName();
  273. document.querySelector("#program-name").textContent = s.title;
  274. })
  275. puntata.querySelector(".add-to-playlist").addEventListener('click', (e) => {
  276. console.log('playlist?', s, s.enclosure)
  277. playlist.push(s)
  278. })
  279. programma.appendChild(puntata)
  280. }
  281. })
  282. })
  283. }
  284. }
  285. document.querySelector('#programmi').classList.add('hidden')
  286. setupPlayer();
  287. setupRadios();
  288. setupTabs();
  289. setupShowButtons();
  290. await deployRadio(leRadio[0].website)
  291. }
  292. main()