\n\t\tdocument.addEventListener('alpine:init', () => {\n\t\t\tAlpine.data(\"player\", () => ({\n\t\t\t\tinit() {\n\t\t\t\t\twindow.replaceQueueAndPlayAt = (...params) => this.replaceQueueAndPlayAt(...params);\n\t\t\t\t},\n\t\t\t\tqueue: [],\n\t\t\t\tidx: 0,\n\t\t\t\tcurrentSound: null,\n\t\t\t\tnextSound: null,\n\t\t\t\tvolume: 0.1,\n\t\t\t\tplaying: false,\n\t\t\t\tloading: false,\n\t\t\t\tprogress: 0,\n\n\t\t\t\t// sets the queue, and plays the song at idx\n\t\t\t\treplaceQueueAndPlayAt(queue, idx) {\n\t\t\t\t\tthis.queue = queue;\n\t\t\t\t\tthis.idx = idx;\n\t\t\t\t\tthis.play(idx);\n\n\t\t\t\t\t// setup the preload and progress listener\n\t\t\t\t\tsetInterval(() => this.checkDuration(), 1000);\n\t\t\t\t},\n\n\t\t\t\t// Plays the song at the passed idx, sets this.idx to that,\n\t\t\t\t// and plays a preloaded song\n\t\t\t\t// \n\t\t\t\t// If preloaded=true, this function will assume that it is the\n\t\t\t\t// next song. It will trust that idx is correct.\n\t\t\t\tasync play(idx) {\n\t\t\t\t\tconst preloaded = this.nextSound !== null && idx === this.idx + 1;\n\n\t\t\t\t\t// if a song is currently playing\n\t\t\t\t\t// then fade it out before playing the next sound\n\t\t\t\t\tif (this.playing === true \n\t\t\t\t\t\t&& this.currentSound !== null \n\t\t\t\t\t) {\n\t\t\t\t\t\t// this will not trigger when next() is called,\n\t\t\t\t\t\t// because next() sets this.playing=false\n\n\t\t\t\t\t\tthis.currentSound.fade(this.volume, 0.0, 250);\n\t\t\t\t\t\tawait wait(250);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.currentSound?.unload?.();\n\t\t\t\t\tthis.playing = false;\n\t\t\t\t\tthis.currentSound = null;\n\t\t\t\t\tthis.idx = idx;\n\n\t\t\t\t\t// if a song is preloaded, assume it's the next song and play it\n\t\t\t\t\tif (preloaded === true && this.nextSound !== null) {\n\t\t\t\t\t\tthis.currentSound = this.nextSound;\n\t\t\t\t\t\tthis.nextSound = null;\n\t\t\t\t\t\tthis.currentSound.play();\n\t\t\t\t\t\tthis.playing = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// otherwise, load the song at idx and play it\n\t\t\t\t\t\tconst songId = this.queue[idx].songId;\n\n\t\t\t\t\t\tconst sound = new Howl({\n\t\t\t\t\t\t\tsrc: `https://navidrome.araozu.dev/rest/stream.view?id=${songId}&v=1.13.0&c=music-to-go&u=fernando&s=49805d&t=4148cd1c83ae1bd01334facf4e70a947`,\n\t\t\t\t\t\t\thtml5: true,\n\t\t\t\t\t\t\tvolume: this.volume,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.currentSound = sound;\n\t\t\t\t\t\tthis.loading = true;\n\t\t\t\t\t\tsound.play();\n\t\t\t\t\t\tsound.once(\"load\", () => {\n\t\t\t\t\t\t\tthis.loading = false;\n\t\t\t\t\t\t\tthis.playing = true;\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// set-up preloading for the next song\n\t\t\t\t\tconst sound = this.currentSound;\n\t\t\t\t\tsound.once(\"play\", () => {\n\t\t\t\t\t\tconst length = sound.duration();\n\t\t\t\t\t\tconst targetLength = length - 5;\n\t\t\t\t\t\tlet preloadInterval;\n\t\t\t\t\t\tpreloadInterval = setInterval(() => {\n\t\t\t\t\t\t\tconst pos = sound.seek();\n\t\t\t\t\t\t\tif (pos > targetLength) {\n\t\t\t\t\t\t\t\tthis.preload();\n\t\t\t\t\t\t\t\tclearInterval(preloadInterval);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, 1000);\n\t\t\t\t\t});\n\n\t\t\t\t\t// set-up playing the next song when the current finishes\n\t\t\t\t\tsound.once(\"end\", () => {\n\t\t\t\t\t\tthis.playing = false;\n\t\t\t\t\t\tthis.next();\n\t\t\t\t\t});\n\t\t\t\t\tthis.currentSound = sound;\n\t\t\t\t},\n\n\t\t\t\t// checks the duration of the playing song and:\n\t\t\t\t// - updates the song progress (0-100%)\n\t\t\t\t// - begins preloading\n\t\t\t\tcheckDuration() {\n\t\t\t\t\tconst sound = this.currentSound;\n\t\t\t\t\tif (this.playing) {\n\t\t\t\t\t\tconst length = sound.duration();\n\t\t\t\t\t\tif (length <= 0) return;\n\n\t\t\t\t\t\tconst position = sound.seek();\n\n\t\t\t\t\t\t// preload 5s before the song ends\n\t\t\t\t\t\tif (position >= length - 5 && this.nextSound === null) {\n\t\t\t\t\t\t\tthis.preload();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// update the song progress percentage\n\t\t\t\t\t\tthis.progress = Math.floor((position * 100) / length);\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\ttogglePlayPause() {\n\t\t\t\t\tif (this.playing === true) {\n\t\t\t\t\t\tthis.playing = false;\n\t\t\t\t\t\tthis.currentSound?.pause();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.playing = true;\n\t\t\t\t\t\tthis.currentSound?.play();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tnext() {\n\t\t\t\t\tif (this.idx + 1 < this.queue.length) {\n\t\t\t\t\t\tthis.play(this.idx + 1);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tpreload() {\n\t\t\t\t\tconsole.log(\"preloading\");\n\t\t\t\t\tif (!(this.idx + 1 < this.queue.length)) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tconst nextSongId = this.queue[this.idx + 1].songId;\n\t\t\t\t\tconst nextSound = new Howl({\n\t\t\t\t\t\tsrc: `https://navidrome.araozu.dev/rest/stream.view?id=${nextSongId}&v=1.13.0&c=music-to-go&u=fernando&s=49805d&t=4148cd1c83ae1bd01334facf4e70a947`,\n\t\t\t\t\t\thtml5: true,\n\t\t\t\t\t\tvolume: 0,\n\t\t\t\t\t\tpreload: true,\n\t\t\t\t\t});\n\t\t\t\t\t// Attempt to immediately play the song, immediately pause it, rewind it and set volume back up\n\t\t\t\t\tnextSound.play();\n\t\t\t\t\tlet preloadInterval;\n\t\t\t\t\tnextSound.once(\"load\", () => {\n\t\t\t\t\t\tnextSound.pause();\n\t\t\t\t\t\tnextSound.seek(0);\n\t\t\t\t\t\tnextSound.volume(this.volume);\n\t\t\t\t\t});\n\t\t\t\t\tthis.nextSound = nextSound;\n\t\t\t\t}\n\t\t\t}));\n\t\t})\n\n\t\tfunction wait(ms) {\n\t\t\treturn new Promise(r => setTimeout(r, ms));\n\t\t}\n\t\t