/** * EasyStream Playlist Enhancements * Adds shuffle, loop, and autoplay features to playlists */ class PlaylistEnhancer { constructor(playlistId, options = {}) { this.playlistId = playlistId; this.currentIndex = 0; this.items = []; this.options = { shuffle: options.shuffle || false, loop: options.loop || false, autoplay: options.autoplay !== false, ...options }; this.originalOrder = []; this.init(); } async init() { await this.loadPlaylistItems(); this.setupControls(); this.setupEventListeners(); } async loadPlaylistItems() { try { const response = await fetch(`/api/playlists/${this.playlistId}/items`); const data = await response.json(); this.items = data.items || []; this.originalOrder = [...this.items]; } catch (error) { console.error('Error loading playlist:', error); } } setupControls() { const controls = document.createElement('div'); controls.className = 'playlist-controls'; controls.innerHTML = ` `; const playlistContainer = document.querySelector('.playlist-container'); if (playlistContainer) { playlistContainer.insertBefore(controls, playlistContainer.firstChild); } } setupEventListeners() { document.getElementById('playlistShuffle')?.addEventListener('click', () => this.toggleShuffle()); document.getElementById('playlistLoop')?.addEventListener('click', () => this.toggleLoop()); document.getElementById('playlistAutoplay')?.addEventListener('click', () => this.toggleAutoplay()); // Listen for video end event const video = document.querySelector('video'); if (video) { video.addEventListener('ended', () => this.onVideoEnded()); } } toggleShuffle() { this.options.shuffle = !this.options.shuffle; document.getElementById('playlistShuffle').classList.toggle('active'); if (this.options.shuffle) { this.shufflePlaylist(); } else { this.items = [...this.originalOrder]; } this.savePreferences(); } toggleLoop() { this.options.loop = !this.options.loop; document.getElementById('playlistLoop').classList.toggle('active'); this.savePreferences(); } toggleAutoplay() { this.options.autoplay = !this.options.autoplay; document.getElementById('playlistAutoplay').classList.toggle('active'); this.savePreferences(); } shufflePlaylist() { const currentItem = this.items[this.currentIndex]; // Fisher-Yates shuffle for (let i = this.items.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [this.items[i], this.items[j]] = [this.items[j], this.items[i]]; } // Keep current item at current position if (currentItem) { const newIndex = this.items.indexOf(currentItem); [this.items[this.currentIndex], this.items[newIndex]] = [this.items[newIndex], this.items[this.currentIndex]]; } this.updatePlaylistUI(); } playNext() { if (this.currentIndex < this.items.length - 1) { this.currentIndex++; } else if (this.options.loop) { this.currentIndex = 0; } else { return; // End of playlist } this.playItem(this.currentIndex); } playPrevious() { if (this.currentIndex > 0) { this.currentIndex--; } else if (this.options.loop) { this.currentIndex = this.items.length - 1; } else { return; // Start of playlist } this.playItem(this.currentIndex); } playItem(index) { if (index < 0 || index >= this.items.length) return; this.currentIndex = index; const item = this.items[index]; // Navigate to video window.location.href = `/watch?v=${item.file_key}&list=${this.playlistId}`; } onVideoEnded() { if (this.options.autoplay) { setTimeout(() => this.playNext(), 1000); // 1 second delay } } updatePlaylistUI() { const playlistItems = document.querySelectorAll('.playlist-item'); playlistItems.forEach((item, index) => { item.dataset.index = index; item.classList.toggle('active', index === this.currentIndex); }); } savePreferences() { localStorage.setItem('playlistPreferences', JSON.stringify({ shuffle: this.options.shuffle, loop: this.options.loop, autoplay: this.options.autoplay })); } loadPreferences() { const prefs = localStorage.getItem('playlistPreferences'); if (prefs) { const parsed = JSON.parse(prefs); this.options = { ...this.options, ...parsed }; } } } // Export for global use window.PlaylistEnhancer = PlaylistEnhancer;