player.js 12 KB

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