(function () { let isPlaying = false; let currentTracks = []; let cursor = 0; let howlerInstance; const retroWaveRu = "https://retrowave.ru"; let titleEl = document.getElementById("track-name"); let coverArtEl = document.getElementsByClassName("music-player")[0]; let refreshBtn = document.querySelector(".refresh"); let ovr = document.querySelector(".OVR"); let fullScreenBtn = document.querySelector(".fullscreen"); const modalEl = document.getElementById("intro-modal"); // const uploadInfoEl = document.getElementById("upload-info"); const fileUploadEl = document.getElementById("file-upload"); let volume = 1; let effectCanvas; let starField; let line3D; const synthwaveColor = 0xff2975; openDialog(); getMusic(); listenUploadFileChange(); function initDynamicTooltips() { document.querySelectorAll("[title]").forEach((element) => { const title = element.getAttribute("title"); element.removeAttribute("title"); element.setAttribute("data-title", title); element.addEventListener("mouseenter", function (e) { const tooltip = document.createElement("div"); tooltip.textContent = this.getAttribute("data-title"); tooltip.style.cssText = ` font-family: "VCR", sans-serif; position: fixed; background:rgb(255, 255, 255); color: #000000; border: 1px solidrgb(0, 0, 0); padding: 4px 8px; font-size: 12px; border-radius: 3px; z-index: 10000; pointer-events: none; white-space: nowrap; `; document.body.appendChild(tooltip); this.tooltipEl = tooltip; const rect = this.getBoundingClientRect(); tooltip.style.left = rect.left + rect.width / 2 - tooltip.offsetWidth / 2 + "px"; tooltip.style.top = rect.top - tooltip.offsetHeight - 8 + "px"; }); element.addEventListener("mouseleave", function () { if (this.tooltipEl) { document.body.removeChild(this.tooltipEl); this.tooltipEl = null; } }); }); } document.addEventListener("DOMContentLoaded", initDynamicTooltips); coverArtEl.addEventListener("click", (event) => { const isNoPause = event.target.classList.contains("no-pause"); if (howlerInstance && !isNoPause) { togglePlay(); } }); // uploadInfoEl.addEventListener("click", () => { // const confirmText = `Do you really want to change your playlist?\nThis will replace all your retrowave music history.\nIf you are sure about this, make sure to upload a valid json file probably downloaded using history link.`; // if (confirm(confirmText)) { // fileUploadEl.click(); // } // }); function listenUploadFileChange() { fileUploadEl.onchange = function () { const selectedFile = fileUploadEl.files[0]; const reader = new FileReader(); reader.readAsText(selectedFile, "UTF-8"); reader.onload = function (event) { try { const uploadedPlaylist = JSON.parse(event.target.result); localStorage.setItem("retrowave-history", event.target.result); } catch (error) { alert("malformed/invalid json file"); } }; }; } document.getElementById("initButton")?.addEventListener("click", () => { var hydra = new Hydra({ detectAudio: false }); modalEl.classList.remove("open"); playMusic(); initHydra(); initControls(); }); document.getElementById("history").addEventListener("click", () => { downloadHistory(); }); document.addEventListener("keyup", (event) => { const { key } = event; switch (key) { case " ": togglePlay(); break; case "a": break; case "d": break; case "w": volumeUp(); break; case "s": volumeDown(); break; case "n": playNextTrack(); break; case "f": toggleFullScreen(); break; case "h": toggleControls(); break; case 'x': toggleEverything(); } }); let touchstartX = 0; let touchendX = 0; let touchstartY = 0; let touchendY = 0; const musixPlayerEl = document.getElementsByClassName("music-player")[0]; if (musixPlayerEl) { musixPlayerEl.addEventListener("touchstart", (e) => { touchstartX = e.changedTouches[0].screenX; touchstartY = e.changedTouches[0].screenY; }); musixPlayerEl.addEventListener("touchend", (e) => { touchendX = e.changedTouches[0].screenX; touchendY = e.changedTouches[0].screenY; checkDirection(); }); } function checkDirection() { if (touchendX < touchstartX) { // Swipe Left } if (touchendX > touchstartX) { // Swipe Right } if (touchendY < touchstartY) { volumeUp(); } if (touchendY > touchstartY) { volumeDown(); } } function openDialog() { modalEl.classList.add("open"); } async function getMusic() { const res = await fetch( `https://retrowave.ru/api/v1/tracks?limit=10&cursor=${cursor}` ).then((res) => res.json()); const { body: { tracks, cursor: currentCursor }, } = res; cursor = currentCursor; currentTracks = tracks; } function initPlayer() {} function playMusic() { const currentTrack = currentTracks[0]; const singleTrack = currentTrack.streamUrl; const fullTrack = `${retroWaveRu}${singleTrack}`; howlerInstance = new Howl({ src: [fullTrack], html5: true, onend: function () { playNextTrack(); }, }); setVolume(); isPlaying = true; window.hw = howlerInstance; updateInfo(currentTrack); addToHistory(currentTrack); howlerInstance.play(); } function volumeDown() { if (volume > 0.1) { volume -= 0.1; } setVolume(); } function volumeUp() { if (volume < 1) { volume += 0.1; } setVolume(); } function setVolume() { howlerInstance.volume(volume); } function togglePlay() { isPlaying = !isPlaying; if (isPlaying) { howlerInstance.play(); } else { howlerInstance.pause(); } } function updateInfo(trackDetails) { titleEl.innerText = trackDetails.title; document.title = `Now Playing... ${trackDetails.title}`; coverArtEl.style.backgroundImage = `url("${retroWaveRu}${trackDetails.artworkUrl}")`; ovr.innerHTML = `${trackDetails.title}` } function getHistory() { let localHistoryStore = localStorage.getItem("retrowave-history") || "[]"; let historyArray = JSON.parse(localHistoryStore); return historyArray; } function addToHistory(trackDetails) { let historyArray = getHistory(); historyArray.push(trackDetails); localStorage.setItem("retrowave-history", JSON.stringify(historyArray)); } function downloadHistory() { const historyArray = getHistory(); let element = document.createElement("a"); let playListData = "#EXTM3U"; historyArray.forEach((musicData) => { playListData = `${playListData} #EXTINF:${Math.ceil(musicData.duration / 1000)}, ${musicData.title} https://retrowave.ru/${musicData.streamUrl} `; }); element.setAttribute( "href", "data:audio/x-mpegurl;;charset=utf-8," + encodeURIComponent(playListData) ); element.setAttribute("download", "retrowave_playlist.m3u"); element.style.display = "none"; document.body.appendChild(element); element.click(); document.body.removeChild(element); } function playNextTrack() { resetHowler(); currentTracks.shift(); if (currentTracks.length <= 3) { getMusic(); } playMusic(); } function resetHowler(destroy = false) { howlerInstance.stop(); if (destroy) { howlerInstance.unload(); howlerInstance = null; } } function initControls() { refreshBtn.addEventListener("click", () => { playNextTrack(); }); fullScreenBtn.addEventListener("click", () => { toggleFullScreen(); }); } function toggleFullScreen() { if (!document.fullscreenElement) { document.documentElement.requestFullscreen(); } else { if (document.exitFullscreen) { document.exitFullscreen(); } } } function toggleControls() { const toggleableElements = document.querySelectorAll(".toggleable"); toggleableElements.forEach((el) => { el.classList.toggle("hidden"); }); } function toggleEverything() { musixPlayerEl.classList.toggle("hidden"); ovr.classList.toggle("hidden"); } // function initCodef() { // const width = window.innerWidth; // const height = window.innerHeight; // effectCanvas = new canvas(width, height, 'codef-canvas'); // starField = new starfield3D(effectCanvas, 500, 2, width, height, width/2, height/2, '#ffffff', 100, 0, 0); // line3D = new codef3D(effectCanvas, 320, 75, 1, 1500 ); // line3D.line({x:-320, y:0, z:0},{x:320, y:0, z:0}, new LineBasicMaterial({ color: synthwaveColor, linewidth:2})); // line3D.line({x: 0, y:-240, z:0},{x:0, y:240, z:0}, new LineBasicMaterial({ color: synthwaveColor, linewidth:2})); // renderCodeFx(); // } // function renderCodeFx() { // effectCanvas.fill('#000000'); // line3D.group.rotation.x+=0.01; // line3D.group.rotation.y+=0.02; // line3D.group.rotation.z+=0.04; // starField.draw(); // line3D.draw(); // requestAnimationFrame(renderCodeFx); // } function initHydra() { try { voronoi(350, 0.15) .modulateScale(osc(8).rotate(Math.sin(time)), 0.5) .thresh(0.8) .modulateRotate(osc(7), 0.4) .thresh(0.7) .diff(src(o0).scale(1.8)) .modulateScale(osc(2).modulateRotate(o0, 0.74)) .diff( src(o0) .rotate([-0.012, 0.01, -0.002, 0]) .scrollY(0, [-1 / 199800, 0].fast(0.7)) ) .brightness([-0.02, -0.17].smooth().fast(0.5)) //.modulate(o0, () => a.fft[1] * .2) .out(); } catch (error) { console.error("Hydra initialization failed:", error); } } })();