Spaces:
Running
Running
| // Simple MySpace-style music player with fake EQ bars | |
| document.addEventListener("DOMContentLoaded", () => { | |
| // === PLAYLIST === | |
| const playlist = [ | |
| { | |
| title: "The Ocean", | |
| artist: "Super Fuzz", | |
| src: "https://cdn-uploads.huggingface.co/production/uploads/693f06e4c231c8267144eb68/WwdKDnPZAW9q7tXWomfSY.mpga", | |
| }, | |
| ]; | |
| // === GRAB ELEMENTS FROM THE PAGE === | |
| const audioEl = document.getElementById("audio-player"); | |
| const titleEl = document.getElementById("track-title"); | |
| const artistEl = document.getElementById("track-artist"); | |
| const playPauseBtn = document.getElementById("play-pause-btn"); | |
| const prevBtn = document.getElementById("prev-btn"); | |
| const nextBtn = document.getElementById("next-btn"); | |
| const seekBar = document.getElementById("seek-bar"); | |
| const eqBars = document.querySelectorAll(".eq-visualizer .eq-bar"); | |
| // Safety check – if something's missing, don't crash | |
| if ( | |
| !audioEl || | |
| !titleEl || | |
| !artistEl || | |
| !playPauseBtn || | |
| !prevBtn || | |
| !nextBtn || | |
| !seekBar | |
| ) { | |
| console.warn("Music player: required elements not found."); | |
| return; | |
| } | |
| let currentTrackIndex = 0; | |
| let isPlaying = false; | |
| let isSeeking = false; | |
| let eqInterval = null; | |
| // === EQ BAR ANIMATION (FAKE VISUALIZER) === | |
| function startEq() { | |
| if (!eqBars.length || eqInterval) return; | |
| eqInterval = setInterval(() => { | |
| eqBars.forEach((bar) => { | |
| const height = 20 + Math.random() * 80; // 20%–100% | |
| bar.style.height = `${height}%`; | |
| }); | |
| }, 120); | |
| } | |
| function stopEq() { | |
| if (eqInterval) { | |
| clearInterval(eqInterval); | |
| eqInterval = null; | |
| } | |
| eqBars.forEach((bar) => { | |
| bar.style.height = "25%"; // idle height | |
| }); | |
| } | |
| // === CORE FUNCTIONS === | |
| function loadTrack(index) { | |
| const track = playlist[index]; | |
| if (!track) return; | |
| titleEl.textContent = track.title; | |
| artistEl.textContent = track.artist; | |
| audioEl.src = track.src; | |
| } | |
| function playTrack() { | |
| if (!audioEl.src) { | |
| // just in case loadTrack didn't run for some reason | |
| loadTrack(currentTrackIndex); | |
| } | |
| const playPromise = audioEl.play(); | |
| if (playPromise !== undefined) { | |
| playPromise | |
| .then(() => { | |
| isPlaying = true; | |
| playPauseBtn.textContent = "⏸"; // pause icon | |
| startEq(); | |
| }) | |
| .catch((err) => { | |
| console.warn("Autoplay/play blocked or failed:", err); | |
| }); | |
| } | |
| } | |
| function pauseTrack() { | |
| audioEl.pause(); | |
| isPlaying = false; | |
| playPauseBtn.textContent = "▶"; // play icon | |
| stopEq(); | |
| } | |
| function togglePlay() { | |
| if (isPlaying) { | |
| pauseTrack(); | |
| } else { | |
| playTrack(); | |
| } | |
| } | |
| function playNext() { | |
| currentTrackIndex = (currentTrackIndex + 1) % playlist.length; | |
| loadTrack(currentTrackIndex); | |
| playTrack(); | |
| } | |
| function playPrev() { | |
| currentTrackIndex = | |
| (currentTrackIndex - 1 + playlist.length) % playlist.length; | |
| loadTrack(currentTrackIndex); | |
| playTrack(); | |
| } | |
| // === EVENT LISTENERS === | |
| // Play / Pause button | |
| playPauseBtn.addEventListener("click", togglePlay); | |
| // Next / Previous | |
| nextBtn.addEventListener("click", playNext); | |
| prevBtn.addEventListener("click", playPrev); | |
| // Keep seek bar in sync with audio | |
| audioEl.addEventListener("timeupdate", () => { | |
| if (!audioEl.duration || isSeeking) return; | |
| const progress = (audioEl.currentTime / audioEl.duration) * 100; | |
| seekBar.value = progress; | |
| }); | |
| // Scrub / seek when user drags the slider | |
| seekBar.addEventListener("input", () => { | |
| if (!audioEl.duration) return; | |
| isSeeking = true; | |
| const newTime = (seekBar.value / 100) * audioEl.duration; | |
| audioEl.currentTime = newTime; | |
| }); | |
| seekBar.addEventListener("change", () => { | |
| isSeeking = false; | |
| }); | |
| // When a track ends, loop playlist | |
| audioEl.addEventListener("ended", () => { | |
| playNext(); | |
| }); | |
| // If there's an audio error (bad URL, etc.) | |
| audioEl.addEventListener("error", (e) => { | |
| console.warn("Audio element error:", e); | |
| }); | |
| // === INITIALIZE === | |
| // Load first track | |
| loadTrack(currentTrackIndex); | |
| // Try autoplay on load (browser may block this until user clicks) | |
| playTrack(); | |
| }); | |