Spaces:
Running
Running
| // Shared JavaScript across all pages | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Sample playlist data | |
| const playlist = [ | |
| { | |
| id: 1, | |
| title: "Bohemian Rhapsody", | |
| artist: "Queen", | |
| duration: "355", | |
| url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" | |
| }, | |
| { | |
| id: 2, | |
| title: "Blinding Lights", | |
| artist: "The Weeknd", | |
| duration: "200", | |
| url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3" | |
| }, | |
| { | |
| id: 3, | |
| title: "Shape of You", | |
| artist: "Ed Sheeran", | |
| duration: "233", | |
| url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3" | |
| }, | |
| { | |
| id: 4, | |
| title: "Dance Monkey", | |
| artist: "Tones and I", | |
| duration: "209", | |
| url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-4.mp3" | |
| }, | |
| { | |
| id: 5, | |
| title: "Uptown Funk", | |
| artist: "Mark Ronson ft. Bruno Mars", | |
| duration: "270", | |
| url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-5.mp3" | |
| } | |
| ]; | |
| // DOM Elements | |
| const playBtn = document.getElementById('play-btn'); | |
| const prevBtn = document.getElementById('prev-btn'); | |
| const nextBtn = document.getElementById('next-btn'); | |
| const playlistContainer = document.getElementById('playlist'); | |
| const trackTitle = document.getElementById('track-title'); | |
| const trackArtist = document.getElementById('track-artist'); | |
| const currentTimeEl = document.getElementById('current-time'); | |
| const totalTimeEl = document.getElementById('total-time'); | |
| const progressBar = document.getElementById('progress-bar'); | |
| const vinyl = document.querySelector('.w-64.h-64'); | |
| const needle = document.getElementById('vinyl-needle'); | |
| // Player state | |
| let currentTrackIndex = 0; | |
| let isPlaying = false; | |
| let sound = null; | |
| // Initialize playlist | |
| function initPlaylist() { | |
| playlistContainer.innerHTML = ''; | |
| playlist.forEach((track, index) => { | |
| const item = document.createElement('div'); | |
| item.className = `playlist-item p-3 rounded-lg cursor-pointer ${index === currentTrackIndex ? 'active' : ''}`; | |
| item.innerHTML = ` | |
| <div class="flex justify-between items-center"> | |
| <div> | |
| <div class="font-medium">${track.title}</div> | |
| <div class="text-sm text-gray-400">${track.artist}</div> | |
| </div> | |
| <div class="text-sm text-gray-400">${formatTime(track.duration)}</div> | |
| </div> | |
| `; | |
| item.addEventListener('click', () => { | |
| playTrack(index); | |
| }); | |
| playlistContainer.appendChild(item); | |
| }); | |
| } | |
| // Play track | |
| function playTrack(index) { | |
| // Stop current sound if playing | |
| if (sound) { | |
| sound.stop(); | |
| } | |
| // Update current track index | |
| currentTrackIndex = index; | |
| // Update UI | |
| const track = playlist[currentTrackIndex]; | |
| trackTitle.textContent = track.title; | |
| trackArtist.textContent = track.artist; | |
| totalTimeEl.textContent = formatTime(track.duration); | |
| // Update playlist active item | |
| document.querySelectorAll('.playlist-item').forEach((item, i) => { | |
| if (i === currentTrackIndex) { | |
| item.classList.add('active'); | |
| } else { | |
| item.classList.remove('active'); | |
| } | |
| }); | |
| // Create new sound | |
| sound = new Howl({ | |
| src: [track.url], | |
| html5: true, | |
| onplay: function() { | |
| isPlaying = true; | |
| updatePlayButton(); | |
| rotateVinyl(true); | |
| moveNeedle(true); | |
| requestAnimationFrame(updateProgress); | |
| }, | |
| onpause: function() { | |
| isPlaying = false; | |
| updatePlayButton(); | |
| rotateVinyl(false); | |
| moveNeedle(false); | |
| }, | |
| onend: function() { | |
| isPlaying = false; | |
| updatePlayButton(); | |
| rotateVinyl(false); | |
| moveNeedle(false); | |
| nextTrack(); | |
| } | |
| }); | |
| // Play the sound | |
| sound.play(); | |
| } | |
| // Toggle play/pause | |
| function togglePlay() { | |
| if (sound) { | |
| if (isPlaying) { | |
| sound.pause(); | |
| } else { | |
| sound.play(); | |
| } | |
| } else { | |
| playTrack(currentTrackIndex); | |
| } | |
| } | |
| // Next track | |
| function nextTrack() { | |
| currentTrackIndex = (currentTrackIndex + 1) % playlist.length; | |
| playTrack(currentTrackIndex); | |
| } | |
| // Previous track | |
| function prevTrack() { | |
| currentTrackIndex = (currentTrackIndex - 1 + playlist.length) % playlist.length; | |
| playTrack(currentTrackIndex); | |
| } | |
| // Update play button icon | |
| function updatePlayButton() { | |
| const playIcon = playBtn.querySelector('i'); | |
| if (isPlaying) { | |
| playIcon.setAttribute('data-feather', 'pause'); | |
| } else { | |
| playIcon.setAttribute('data-feather', 'play'); | |
| } | |
| feather.replace(); | |
| } | |
| // Rotate vinyl | |
| function rotateVinyl(playing) { | |
| if (playing) { | |
| vinyl.classList.add('vinyl-rotation', 'vinyl-playing'); | |
| vinyl.classList.remove('vinyl-paused'); | |
| } else { | |
| vinyl.classList.remove('vinyl-playing'); | |
| vinyl.classList.add('vinyl-paused'); | |
| } | |
| } | |
| // Move needle | |
| function moveNeedle(playing) { | |
| if (playing) { | |
| needle.style.transform = 'rotate(0deg)'; | |
| } else { | |
| needle.style.transform = 'rotate(-30deg)'; | |
| } | |
| } | |
| // Update progress bar | |
| function updateProgress() { | |
| if (!sound) return; | |
| const seek = sound.seek() || 0; | |
| const duration = sound.duration(); | |
| const progress = (seek / duration) * 100; | |
| progressBar.style.width = `${progress}%`; | |
| currentTimeEl.textContent = formatTime(Math.floor(seek)); | |
| if (isPlaying) { | |
| requestAnimationFrame(updateProgress); | |
| } | |
| } | |
| // Format time (seconds to mm:ss) | |
| function formatTime(seconds) { | |
| const mins = Math.floor(seconds / 60); | |
| const secs = seconds % 60; | |
| return `${mins}:${secs < 10 ? '0' : ''}${secs}`; | |
| } | |
| // Event listeners | |
| playBtn.addEventListener('click', togglePlay); | |
| nextBtn.addEventListener('click', nextTrack); | |
| prevBtn.addEventListener('click', prevTrack); | |
| // Initialize | |
| initPlaylist(); | |
| updatePlayButton(); | |
| }); |