232 lines
5.6 KiB
JavaScript
232 lines
5.6 KiB
JavaScript
// ============================
|
|
// Volume adjustment
|
|
// ============================
|
|
document.querySelectorAll("audio").forEach((audio) => {
|
|
audio.volume = 0.5;
|
|
});
|
|
|
|
// ============================
|
|
// Fisher-Yates Shuffle
|
|
// ============================
|
|
function fisherYatesShuffle(arr) {
|
|
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;
|
|
}
|
|
|
|
// ============================
|
|
// Fetch & build playlist safely
|
|
// ============================
|
|
async function loadMusicData() {
|
|
try {
|
|
const response = await fetch("https://api.veltron.net");
|
|
|
|
if (!response.ok) {
|
|
console.error("HTTP error:", response.status);
|
|
return [];
|
|
}
|
|
|
|
const text = await response.text();
|
|
|
|
let data;
|
|
try {
|
|
data = JSON.parse(text);
|
|
} catch (e) {
|
|
console.error("Invalid JSON returned from API");
|
|
return [];
|
|
}
|
|
|
|
if (!data?.ols || !Array.isArray(data.ols)) {
|
|
console.error("Invalid API structure");
|
|
return [];
|
|
}
|
|
|
|
const section = data.ols.find(
|
|
(item) => item?.["@id"] === "frutigerAeroBliss"
|
|
);
|
|
|
|
if (!section?.li || !Array.isArray(section.li)) {
|
|
console.error("Playlist section missing or invalid");
|
|
return [];
|
|
}
|
|
|
|
const baseUrl = "https://cdn.veltron.net/aero";
|
|
|
|
const playlist = section.li
|
|
.map((item) => {
|
|
const mp3 = item?.a?.["@data-src-mp3"];
|
|
const title = item?.a?.title;
|
|
|
|
if (!mp3 || !title) return null;
|
|
|
|
return {
|
|
title,
|
|
url: baseUrl + mp3.split("/").pop(),
|
|
background: item.a["@data-background"] || "",
|
|
artist: item.a["@data-artist"] || "",
|
|
spotifyLink: item.a["@data-spotify"] || "",
|
|
youtubeLink: item.a["@data-youtube"] || ""
|
|
};
|
|
})
|
|
.filter(Boolean);
|
|
|
|
return fisherYatesShuffle(playlist);
|
|
} catch (err) {
|
|
console.error("Failed to load music data:", err);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// ============================
|
|
// DOM Elements
|
|
// ============================
|
|
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;
|
|
|
|
// ============================
|
|
// Persistence
|
|
// ============================
|
|
function saveState() {
|
|
sessionStorage.setItem("isPlaying", isPlaying);
|
|
}
|
|
|
|
function loadState() {
|
|
const saved = sessionStorage.getItem("isPlaying");
|
|
if (saved !== null) {
|
|
isPlaying = saved === "true";
|
|
}
|
|
}
|
|
|
|
// ============================
|
|
// Load Song (SAFE)
|
|
// ============================
|
|
function loadSong(index) {
|
|
if (!songs?.length) return;
|
|
if (index < 0 || index >= songs.length) return;
|
|
|
|
const song = songs[index];
|
|
if (!song?.url) {
|
|
console.warn("Skipping invalid song:", index);
|
|
return;
|
|
}
|
|
|
|
NAV_MUSIC.src = song.url;
|
|
CURRENT_SONG_INFO.innerText = `Featured song: ${song.title}`;
|
|
NAV_MUSIC.load();
|
|
}
|
|
|
|
// ============================
|
|
// UI State
|
|
// ============================
|
|
function updateButtonStates() {
|
|
if (isPlaying) {
|
|
BUTTON_PLAY.classList.add("active");
|
|
BUTTON_PAUSE.classList.remove("active");
|
|
} else {
|
|
BUTTON_PAUSE.classList.add("active");
|
|
BUTTON_PLAY.classList.remove("active");
|
|
}
|
|
}
|
|
|
|
// ============================
|
|
// Controls
|
|
// ============================
|
|
BUTTON_PLAY.addEventListener("click", () => {
|
|
if (!songs.length) return;
|
|
|
|
isPlaying = true;
|
|
NAV_MUSIC.play();
|
|
updateButtonStates();
|
|
saveState();
|
|
});
|
|
|
|
BUTTON_PAUSE.addEventListener("click", () => {
|
|
isPlaying = false;
|
|
NAV_MUSIC.pause();
|
|
updateButtonStates();
|
|
saveState();
|
|
});
|
|
|
|
BUTTON_PREVIOUS.addEventListener("click", () => {
|
|
if (!songs.length) return;
|
|
|
|
currentIndex = currentIndex === 0 ? songs.length - 1 : currentIndex - 1;
|
|
loadSong(currentIndex);
|
|
|
|
if (isPlaying) NAV_MUSIC.play();
|
|
saveState();
|
|
});
|
|
|
|
BUTTON_NEXT.addEventListener("click", () => {
|
|
if (!songs.length) return;
|
|
|
|
currentIndex = (currentIndex + 1) % songs.length;
|
|
loadSong(currentIndex);
|
|
|
|
if (isPlaying) NAV_MUSIC.play();
|
|
saveState();
|
|
});
|
|
|
|
// ============================
|
|
// Auto-next
|
|
// ============================
|
|
NAV_MUSIC.addEventListener("ended", () => {
|
|
if (!songs.length) return;
|
|
|
|
currentIndex = (currentIndex + 1) % songs.length;
|
|
loadSong(currentIndex);
|
|
|
|
if (isPlaying) NAV_MUSIC.play();
|
|
saveState();
|
|
});
|
|
|
|
// ============================
|
|
// Init
|
|
// ============================
|
|
window.addEventListener("load", async () => {
|
|
songs = await loadMusicData();
|
|
|
|
if (!songs.length) {
|
|
console.error("No songs loaded.");
|
|
CURRENT_SONG_INFO.innerText = "No music available";
|
|
return;
|
|
}
|
|
|
|
loadState();
|
|
loadSong(currentIndex);
|
|
|
|
if (isPlaying) {
|
|
NAV_MUSIC.play().catch(() => {});
|
|
}
|
|
|
|
updateButtonStates();
|
|
});
|
|
|
|
// ============================
|
|
// Pause other audio elements
|
|
// ============================
|
|
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
|
|
);
|