${this.escapeHtml(this.playlist.title)}

${this.items.length} videos • ${this.playlist.view_count || 0} views

${this.renderItems()}
`; // Find insertion point (after video player) const player = this.playerElement; player.parentNode.insertBefore(container, player.nextSibling); this.updateControlStates(); } renderItems() { return this.items.map((item, index) => `
${index + 1}
${this.escapeHtml(item.title)}
${this.formatDuration(item.duration)}
${this.escapeHtml(item.title)}
${this.escapeHtml(item.username)} • ${this.formatViews(item.views)} views
`).join(''); } attachEventListeners() { // Shuffle button document.getElementById('btnShuffle')?.addEventListener('click', () => { this.toggleShuffle(); }); // Previous/Next buttons document.getElementById('btnPrevious')?.addEventListener('click', () => { this.playPrevious(); }); document.getElementById('btnNext')?.addEventListener('click', () => { this.playNext(); }); // Autoplay button document.getElementById('btnAutoplay')?.addEventListener('click', () => { this.toggleAutoplay(); }); // Repeat button document.getElementById('btnRepeat')?.addEventListener('click', () => { this.cycleRepeat(); }); // Playlist item clicks document.getElementById('playlistItems')?.addEventListener('click', (e) => { const item = e.target.closest('.playlist-item'); if (item && !e.target.classList.contains('btn-remove')) { const index = parseInt(item.dataset.index); this.playByIndex(index); } // Remove button if (e.target.classList.contains('btn-remove')) { const videoId = e.target.dataset.videoId; this.removeVideo(videoId); } }); // Listen for video end event if (this.playerElement) { this.playerElement.addEventListener('ended', () => { this.onVideoEnded(); }); } } playVideo(videoId) { const index = this.items.findIndex(item => item.video_id == videoId); if (index !== -1) { this.playByIndex(index); } } playByIndex(index) { if (index < 0 || index >= this.items.length) return; this.currentIndex = index; const item = this.items[index]; // Update active state document.querySelectorAll('.playlist-item').forEach((el, i) => { el.classList.toggle('active', i === index); }); // Load video in player this.loadVideoInPlayer(item.video_id); // Track play this.trackPlay(item.video_id); } playNext() { if (this.repeat === 'one') { // Replay current video this.playByIndex(this.currentIndex); return; } let nextIndex = this.currentIndex + 1; if (nextIndex >= this.items.length) { if (this.repeat === 'all') { nextIndex = 0; } else { // End of playlist return; } } this.playByIndex(nextIndex); } playPrevious() { let prevIndex = this.currentIndex - 1; if (prevIndex < 0) { if (this.repeat === 'all') { prevIndex = this.items.length - 1; } else { prevIndex = 0; } } this.playByIndex(prevIndex); } onVideoEnded() { if (this.autoPlay) { this.playNext(); } } toggleShuffle() { this.shuffle = !this.shuffle; if (this.shuffle) { // Reshuffle items this.shuffleItems(); } else { // Restore original order this.loadPlaylist(); } this.updateControlStates(); } shuffleItems() { // 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]]; } // Re-render items document.getElementById('playlistItems').innerHTML = this.renderItems(); } toggleAutoplay() { this.autoPlay = !this.autoPlay; this.updateControlStates(); } cycleRepeat() { const modes = ['none', 'all', 'one']; const currentIndex = modes.indexOf(this.repeat); this.repeat = modes[(currentIndex + 1) % modes.length]; this.updateControlStates(); } updateControlStates() { const btnShuffle = document.getElementById('btnShuffle'); const btnAutoplay = document.getElementById('btnAutoplay'); const btnRepeat = document.getElementById('btnRepeat'); if (btnShuffle) { btnShuffle.classList.toggle('active', this.shuffle); } if (btnAutoplay) { btnAutoplay.classList.toggle('active', this.autoPlay); } if (btnRepeat) { btnRepeat.classList.toggle('active', this.repeat !== 'none'); btnRepeat.title = `Repeat: ${this.repeat === 'one' ? 'One' : this.repeat === 'all' ? 'All' : 'Off'}`; if (this.repeat === 'one') { btnRepeat.innerHTML = ` 1 `; } } } async removeVideo(videoId) { if (!confirm('Remove this video from the playlist?')) return; try { const response = await fetch(`/api/playlists.php?id=${this.playlistId}&action=remove_video`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ video_id: videoId }) }); const data = await response.json(); if (data.success) { // Reload playlist await this.loadPlaylist(); document.getElementById('playlistItems').innerHTML = this.renderItems(); } } catch (error) { console.error('Failed to remove video:', error); } } loadVideoInPlayer(videoId) { // Redirect to video page with playlist parameter window.location.href = `/watch?v=${videoId}&list=${this.playlistId}`; } async trackPlay(videoId) { try { await fetch(`/api/playlists.php?id=${this.playlistId}&action=track_play`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ video_id: videoId }) }); } catch (error) { console.error('Failed to track play:', error); } } // Utility functions formatDuration(seconds) { const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = seconds % 60; if (h > 0) { return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`; } return `${m}:${s.toString().padStart(2, '0')}`; } formatViews(views) { if (views >= 1000000) { return (views / 1000000).toFixed(1) + 'M'; } if (views >= 1000) { return (views / 1000).toFixed(1) + 'K'; } return views.toString(); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } } // Auto-initialize from URL parameters document.addEventListener('DOMContentLoaded', () => { const params = new URLSearchParams(window.location.search); const playlistId = params.get('list'); const videoId = params.get('v'); if (playlistId) { window.playlistPlayer = new PlaylistPlayer(playlistId, { videoId: videoId, playerElement: document.getElementById('video-player') }); } });