// ============================ // Volume control // ============================ document.querySelectorAll("audio").forEach((audio) => { audio.volume = 0.5; }); // ============================ // Shuffle (safe) // ============================ function fisherYatesShuffle(arr) { if (!Array.isArray(arr)) return []; for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } return arr; } // ============================ // SAFE FETCH (fixes JSON crash) // ============================ async function safeFetchJSON(url) { try { const res = await fetch(url); if (!res.ok) { console.error("HTTP error:", res.status); return null; } const text = await res.text(); // Reject non-JSON responses (HTML, error pages, etc.) const trimmed = text.trim(); if (!trimmed || (trimmed[0] !== "{" && trimmed[0] !== "[")) { console.error("Non-JSON response detected:", trimmed.slice(0, 120)); return null; } return JSON.parse(text); } catch (err) { console.error("Fetch failed:", err); return null; } } // ============================ // Load music data // ============================ async function loadMusicData(category = "frutigerAeroBliss") { const data = await safeFetchJSON("https://api.veltron.net/music"); if (!data?.ols || !Array.isArray(data.ols)) { console.error("Invalid API structure"); return []; } const section = data.ols.find((x) => x?.["@id"] === category) || data.ols[0]; if (!section?.li || !Array.isArray(section.li)) { console.error("Missing playlist section"); return []; } const baseUrl = "https://cdn.veltron.net/aero"; const playlist = section.li .map((item) => { const a = item?.a; const mp3 = a?.["@data-src-mp3"]; const title = a?.title; if (!mp3 || !title) return null; return { title, url: baseUrl + mp3.split("/").pop(), background: a["@data-background"] || "", artist: a["@data-artist"] || "", youtube: a["@data-youtube"] || "" }; }) .filter(Boolean); return fisherYatesShuffle(playlist); } // ============================ // DOM // ============================ const NAV_MUSIC = document.getElementById("navMusic"); const BUTTON_PLAY = document.getElementById("navMusicPlay"); const BUTTON_PAUSE = document.getElementById("navMusicPause"); const BUTTON_PREVIOUS = document.getElementById("navMusicPrevious"); const BUTTON_NEXT = document.getElementById("navMusicNext"); const CURRENT_SONG_INFO = document.getElementById("currentSongInfo"); // ============================ // State // ============================ let songs = []; let currentIndex = 0; let isPlaying = false; // ============================ // Save / Load state // ============================ function saveState() { sessionStorage.setItem("isPlaying", JSON.stringify(isPlaying)); } function loadState() { const saved = sessionStorage.getItem("isPlaying"); if (saved !== null) isPlaying = JSON.parse(saved); } // ============================ // Safe song loader // ============================ function loadSong(index) { if (!Array.isArray(songs) || songs.length === 0) return; if (index < 0 || index >= songs.length) return; const song = songs[index]; if (!song?.url) return; NAV_MUSIC.src = song.url; CURRENT_SONG_INFO.innerText = `Now playing: ${song.title}`; NAV_MUSIC.load(); } // ============================ // UI state // ============================ function updateUI() { BUTTON_PLAY.classList.toggle("active", isPlaying); BUTTON_PAUSE.classList.toggle("active", !isPlaying); } // ============================ // Controls // ============================ BUTTON_PLAY.addEventListener("click", () => { if (!songs.length) return; isPlaying = true; NAV_MUSIC.play().catch(() => {}); updateUI(); saveState(); }); BUTTON_PAUSE.addEventListener("click", () => { isPlaying = false; NAV_MUSIC.pause(); updateUI(); saveState(); }); BUTTON_PREVIOUS.addEventListener("click", () => { if (!songs.length) return; currentIndex = currentIndex === 0 ? songs.length - 1 : currentIndex - 1; loadSong(currentIndex); if (isPlaying) NAV_MUSIC.play().catch(() => {}); saveState(); }); BUTTON_NEXT.addEventListener("click", () => { if (!songs.length) return; currentIndex = (currentIndex + 1) % songs.length; loadSong(currentIndex); if (isPlaying) NAV_MUSIC.play().catch(() => {}); saveState(); }); // ============================ // Auto next // ============================ NAV_MUSIC.addEventListener("ended", () => { if (!songs.length) return; currentIndex = (currentIndex + 1) % songs.length; loadSong(currentIndex); if (isPlaying) NAV_MUSIC.play().catch(() => {}); saveState(); }); // ============================ // Init // ============================ window.addEventListener("load", async () => { songs = await loadMusicData("frutigerAeroBliss"); if (!songs.length) { CURRENT_SONG_INFO.innerText = "No music available"; console.error("Playlist failed to load"); return; } loadState(); loadSong(currentIndex); updateUI(); if (isPlaying) { NAV_MUSIC.play().catch(() => {}); } }); // ============================ // Prevent multiple audio overlap // ============================ document.addEventListener( "play", (e) => { const audios = document.getElementsByTagName("audio"); for (let i = 0; i < audios.length; i++) { if (audios[i] !== e.target) { audios[i].pause(); } } }, true );