var player; var audio_ctx; var analyser; var audio_data; var canvas_ctx; var draw_loop; var vol_prev; function init() { player = document.querySelector('#player'); //init_analyzer(); init_canvas(); player.addEventListener('ended', nextTrack); player.addEventListener('loadedmetadata', display_duration); player.addEventListener('timeupdate', update_position); navigator.mediaSession.setActionHandler('play', playTrack); navigator.mediaSession.setActionHandler('pause', pauseTrack); navigator.mediaSession.setActionHandler('stop', stopTrack); navigator.mediaSession.setActionHandler('previoustrack', prevTrack); navigator.mediaSession.setActionHandler('nexttrack', nextTrack); let seek = document.querySelector('#seek-slider'); seek.addEventListener('input', function(){ player.currentTime = seek.value; display_current_time(); }); let vol = document.querySelector('#volume-slider'); vol.addEventListener('input', function(){ player.volume = vol.value/100; if (vol.value == 0) { document.querySelector('#vol-btn').style.display = 'none'; document.querySelector('#vol-off-btn').style.display = 'initial'; } else { document.querySelector('#vol-btn').style.display = 'initial'; document.querySelector('#vol-off-btn').style.display = 'none'; } }); } async function get_rand_track() { let playlist = document.querySelector('#playlist-select').value; let q = new URLSearchParams(); q.set('playlist', playlist); let res = await fetch("./rand_track/?" + q.toString()) let data = await res.json(); return data; } async function select_artist(event) { let artist = event.target.value; let playlist = document.querySelector('#playlist-select').value; let q = new URLSearchParams(); q.set('albumartist', artist); q.set('playlist', playlist); let res = await fetch("./albums/?" + q.toString()); let data = await res.json(); populate_albums(data); } async function select_album(event) { let artist = document.querySelector('input[name=artist]:checked').value; let album = document.querySelector('input[name=album]:checked').value; let date = event.srcElement.parentElement.querySelector('.album_year').textContent; let playlist = document.querySelector('#playlist-select').value; let q = new URLSearchParams(); q.set('albumartist', artist); q.set('album', album); q.set('date', date); q.set('playlist', playlist); let res = await fetch("./tracks/?" + q.toString()); let data = await res.json(); populate_tracks(data); } function populate_tracks(data) { clear_tracks(); for (let i = 0; i < data.length; i++) { let track = document.importNode(document.querySelector('#track_template').content, true); track.querySelector('label').setAttribute('for', 'track_' + i); track.querySelector('input').setAttribute('id', 'track_' + i); track.querySelector('input').setAttribute('value', data[i]['title']); track.querySelector('.track_num').textContent = data[i]['discnumber'] + '.' + String(data[i]['tracknumber']).padStart(2, '0'); track.querySelector('.track_title').textContent = data[i]['title']; let min = Math.floor(Math.round(data[i]['duration']) / 60); let sec = Math.round(data[i]['duration']) % 60; sec = String(sec).padStart(2, '0'); track.querySelector('.track_length').textContent = min + 'm' + sec + 's'; track.querySelector('.track').dataset.url = data[i]['filepath']; document.querySelector('#track_list').appendChild(track); } } function populate_albums(data) { clear_albums(); clear_tracks(); for (let i = 0; i < data.length; i++) { let album = document.importNode(document.querySelector('#album_template').content, true); album.querySelector('label').setAttribute('for', 'album_' + i); album.querySelector('input').setAttribute('id', 'album_' + i); album.querySelector('input').setAttribute('value', data[i]['album']); album.querySelector('img').setAttribute('src', data[i]['cover_art_path']); album.querySelector('.album_name').textContent = data[i]['album']; album.querySelector('.album_year').textContent = data[i]['date']; document.querySelector('#album_list').appendChild(album); } } function select_track(event) { let track = {} track['artist'] = document.querySelector('input[name=artist]:checked').value; track['album'] = document.querySelector('input[name=album]:checked').value; track['title'] = document.querySelector('input[name=track]:checked').value; let art_url = document.querySelector('input[name=album]:checked').parentElement.querySelector('img').src; art_url = art_url.replace('64x64', '256x256'); let artwork = [{'src': art_url, 'sizes': '256x256', 'type': 'image/jpeg'}]; track['artwork'] = artwork; let track_url = new URL(event.srcElement.parentElement.querySelector('.track').dataset.url, window.location); update_now_playing(track); player.src = track_url; playTrack(); } function select_playlist(event) { let playlist = document.querySelector('#playlist').value; document.cookie = "playlist=" + playlist; } function clear_tracks() { let tracks = document.querySelector('#track_list'); while (tracks.firstChild) { tracks.removeChild(tracks.lastChild); } } function clear_albums() { let albums = document.querySelector('#album_list'); while (albums.firstChild) { albums.removeChild(albums.lastChild); } } function clear_artists() { let artists = document.querySelector('#artist_list'); while (artists.firstChild) { artists.removeChild(artists.lastChild); } } async function nextTrack() { track = await get_rand_track(); player.src = track.source; update_now_playing(track); playTrack(); } function prevTrack() { // TODO: implement a history array } async function playTrack() { if (!audio_ctx) { init_analyzer(); } if (!player.src) { return nextTrack(); } player.play(); navigator.mediaSession.playbackState = "playing"; if (document.querySelector("#visualizer-select").value === 'bar_graph') { bar_graph(); } document.querySelector("#play-btn").style.display = "none"; document.querySelector("#pause-btn").style.display = "initial"; } function pauseTrack() { player.pause(); navigator.mediaSession.playbackState = "paused"; document.querySelector("#play-btn").style.display = "initial"; document.querySelector("#pause-btn").style.display = "none"; } function stopTrack() { player.pause(); player.load(); navigator.mediaSession.playbackState = "none"; document.querySelector("#play-btn").style.display = "initial"; document.querySelector("#pause-btn").style.display = "none"; } function setMediaSession(track) { navigator.mediaSession.metadata = new MediaMetadata({ title: track.title, artist: track.artist, album: track.album, artwork: track.artwork }); } function update_now_playing(track) { document.querySelector('#now_playing_artist').textContent = track.artist; document.querySelector('#now_playing_album').textContent = track.album; document.querySelector('#now_playing_title').textContent = track.title; document.querySelector('#cover_art').firstChild.src = track.artwork[0].src; setMediaSession(track); } function update_position() { if (!player.duration){return;} navigator.mediaSession.setPositionState({ duration: Math.floor(player.duration), playbackRate: player.playbackRate, position: Math.floor(player.currentTime) }); document.querySelector('#seek-slider').value = Math.floor(player.currentTime); display_current_time(); } function calculate_time(raw_secs) { let mins = Math.floor(raw_secs / 60); let secs = Math.round(raw_secs % 60); let ret = mins + ':' + String(secs).padStart(2,'0'); return ret; } function display_duration() { let dur = document.querySelector('#duration'); dur.textContent = calculate_time(player.duration); document.querySelector('#seek-slider').max = Math.floor(player.duration); } function display_current_time() { cur = document.querySelector('#current-time'); cur.textContent = calculate_time(player.currentTime); } function volume_off() { let vol = document.querySelector('#volume-slider'); vol_prev = vol.value; vol.value = 0; player.volume = 0; document.querySelector('#vol-btn').style.display = 'none'; document.querySelector('#vol-off-btn').style.display = 'initial'; } function volume_restore() { let vol = document.querySelector('#volume-slider'); vol.value = vol_prev; player.volume = vol_prev / 100; document.querySelector('#vol-btn').style.display = 'initial'; document.querySelector('#vol-off-btn').style.display = 'none'; } // --- visualizer --- function select_visualizer(event) { if (event.target.value == 'bar_graph') { bar_graph(); } else if (event.target.value == 'disabled') { cancelAnimationFrame(draw_loop); let canvas = document.querySelector('#visualizer'); canvas_ctx.clearRect(0, 0, canvas.width, canvas.height); } } function init_analyzer() { audio_ctx = new AudioContext(); analyser = audio_ctx.createAnalyser(); let source = audio_ctx.createMediaElementSource(player); source.connect(analyser); source.connect(audio_ctx.destination); } function init_canvas() { let canvas = document.querySelector('#visualizer'); canvas_ctx = canvas.getContext('2d'); canvas_ctx.clearRect(0, 0, canvas.width, canvas.height); } function bar_graph() { analyser.fftSize = 256; analyser.minDecibels = -90; analyser.maxDecibels = -10; analyser.smoothingTimeConstant = 0.85; audio_data = new Uint8Array(analyser.frequencyBinCount); draw(); } function draw() { draw_loop = requestAnimationFrame(draw); analyser.getByteTimeDomainData(audio_data); let canvas = document.querySelector('#visualizer'); canvas_ctx.fillStyle = "rgb(0 0 0)"; canvas_ctx.fillRect(0, 0, canvas.width, canvas.height); const barWidth = (canvas.width / analyser.frequencyBinCount) * 2.5; let barHeight; let x = 0; for (let i = 0; i < analyser.frequencyBinCount; i++) { barHeight = audio_data[i]; canvas_ctx.fillStyle = `rgb(${barHeight + 100} 50 50)`; canvas_ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight); x += barWidth + 1; } }