const config = { defaultVideo: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", defaultTitle: "Stream Player", defaultColor: "#6366f0" }; document.addEventListener('DOMContentLoaded', () => { // Elementos del DOM const video = document.getElementById('videoPlayer'); const videoWrapper = document.querySelector('.video-wrapper'); const videoTitle = document.querySelector('.video-title h1'); const loader = document.querySelector('.loader'); const errorMessage = document.querySelector('.error-message'); const playPauseButton = document.querySelector('.play-pause'); const bigPlayButton = document.querySelector('.big-play-button'); const progressBar = document.querySelector('.progress-bar'); const progressBarFilled = document.querySelector('.progress-bar-filled'); const timeCurrent = document.querySelector('.current'); const timeDuration = document.querySelector('.duration'); const volumeButton = document.querySelector('.volume-button'); const volumeSlider = document.querySelector('.volume-slider'); const volumeFilled = document.querySelector('.volume-filled'); const speedSelect = document.querySelector('.speed-select'); const aspectRatioSelect = document.querySelector('.aspect-ratio-select'); const settingsButton = document.querySelector('.settings-button'); const settingsMenu = document.querySelector('.settings-menu'); const lockButton = document.querySelector('.lock-button'); const unlockOverlay = document.querySelector('.unlock-overlay'); const fullscreenButton = document.querySelector('.fullscreen'); let isPlaying = false; let hasPlayed = false; let isLocked = false; // Obtener y decodificar parámetros de la URL const urlParams = new URLSearchParams(window.location.search); let videoUrl = urlParams.get('video'); let posterUrl = urlParams.get('p'); let title = urlParams.get('t'); // Función para validar y decodificar URL function decodeAndValidateUrl(encodedUrl) { if (!encodedUrl) return null; try { const decoded = atob(encodedUrl); const url = decodeURIComponent(decoded).split('?')[0]; // Remover parámetros como ?download=1 // Validar que sea una URL válida new URL(url); return url; } catch (error) { console.error('Error decoding or validating URL:', error); return null; } } // Decodificar parámetros videoUrl = decodeAndValidateUrl(videoUrl); posterUrl = decodeAndValidateUrl(posterUrl); title = title ? decodeAndValidateUrl(title) || decodeURIComponent(atob(title)) : null; // Establecer título title = title || extractTitleFromUrl(videoUrl) || config.defaultTitle; videoTitle.textContent = title; // Configurar video if (videoUrl) { loader.style.display = 'block'; if (videoUrl.toLowerCase().endsWith('.m3u8')) { if (Hls.isSupported()) { const hls = new Hls({ enableWorker: true, lowLatencyMode: true, }); hls.loadSource(videoUrl); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => { loader.style.display = 'none'; bigPlayButton.style.display = 'block'; // Mostrar botón de reproducción }); hls.on(Hls.Events.ERROR, (event, data) => { loader.style.display = 'none'; console.error('HLS Error:', data); if (data.type === Hls.ErrorTypes.NETWORK_ERROR) { showError('Error de red al cargar el video HLS. Puede ser un problema de CORS o un enlace inválido.'); } else { showError('Error al procesar el video HLS. Verifica el enlace.'); } }); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = videoUrl; video.addEventListener('loadedmetadata', () => { loader.style.display = 'none'; bigPlayButton.style.display = 'block'; }); video.addEventListener('error', () => { loader.style.display = 'none'; showError('Error al cargar el video HLS en este navegador.'); }); } else { loader.style.display = 'none'; showError('Tu navegador no soporta videos HLS (m3u8). Usa otro navegador.'); } } else if (videoUrl.toLowerCase().endsWith('.mp4')) { video.src = videoUrl; video.addEventListener('loadedmetadata', () => { loader.style.display = 'none'; bigPlayButton.style.display = 'block'; }); video.addEventListener('error', () => { loader.style.display = 'none'; showError('No se pudo cargar el video MP4. Verifica el enlace o la conexión.'); }); } else { loader.style.display = 'none'; showError('Formato de video no soportado. Usa MP4 o M3U8.'); } } else { video.src = config.defaultVideo; video.addEventListener('loadedmetadata', () => { loader.style.display = 'none'; bigPlayButton.style.display = 'block'; }); } if (posterUrl) { video.poster = posterUrl; } // Funciones de control function togglePlay(event) { if (event.target.closest('.controls') && !event.target.closest('.play-pause')) return; if (!hasPlayed) { hasPlayed = true; videoWrapper.classList.add('has-played'); } if (video.paused) { video.play().then(() => { playPauseButton.querySelector('i').classList.replace('fa-play', 'fa-pause'); bigPlayButton.querySelector('i').classList.replace('fa-play', 'fa-pause'); isPlaying = true; }).catch(error => { console.error('Error playing video:', error); showError('No se pudo reproducir el video. Haz clic para intentar de nuevo.'); }); } else { video.pause(); playPauseButton.querySelector('i').classList.replace('fa-pause', 'fa-play'); bigPlayButton.querySelector('i').classList.replace('fa-pause', 'fa-play'); isPlaying = false; } event.preventDefault(); event.stopPropagation(); } // Actualizar barra de progreso y tiempo video.addEventListener('timeupdate', () => { if (!isNaN(video.duration)) { const progress = (video.currentTime / video.duration) * 100; progressBarFilled.style.width = `${progress}%`; timeCurrent.textContent = formatTime(video.currentTime); timeDuration.textContent = formatTime(video.duration); } }); // Seek en la barra de progreso progressBar.addEventListener('click', (e) => { const rect = progressBar.getBoundingClientRect(); const pos = (e.clientX - rect.left) / rect.width; video.currentTime = pos * video.duration; }); // Control de volumen volumeButton.addEventListener('click', () => { video.muted = !video.muted; volumeButton.querySelector('i').classList.toggle('fa-volume-up', !video.muted); volumeButton.querySelector('i').classList.toggle('fa-volume-mute', video.muted); volumeFilled.style.width = video.muted ? '0%' : `${video.volume * 100}%`; }); volumeSlider.addEventListener('click', (e) => { const rect = volumeSlider.getBoundingClientRect(); const pos = (e.clientX - rect.left) / rect.width; video.volume = pos; video.muted = false; volumeButton.querySelector('i').classList.replace('fa-volume-mute', 'fa-volume-up'); volumeFilled.style.width = `${pos * 100}%`; }); // Cambiar velocidad de reproducción speedSelect.addEventListener('change', () => { video.playbackRate = parseFloat(speedSelect.value); }); // Cambiar relación de aspecto aspectRatioSelect.addEventListener('change', () => { video.style.objectFit = aspectRatioSelect.value; }); // Mostrar/ocultar menú de ajustes settingsButton.addEventListener('click', () => { settingsMenu.classList.toggle('active'); }); // Bloquear/desbloquear controles lockButton.addEventListener('click', () => { isLocked = !isLocked; videoWrapper.classList.toggle('locked'); lockButton.querySelector('i').classList.toggle('fa-unlock'); lockButton.querySelector('i').classList.toggle('fa-lock'); unlockOverlay.classList.toggle('show'); }); unlockOverlay.addEventListener('click', () => { isLocked = false; videoWrapper.classList.remove('locked'); lockButton.querySelector('i').classList.replace('fa-lock', 'fa-unlock'); unlockOverlay.classList.remove('show'); }); // Pantalla completa fullscreenButton.addEventListener('click', () => { if (!document.fullscreenElement) { videoWrapper.requestFullscreen(); } else { document.exitFullscreen(); } }); // Formatear tiempo function formatTime(seconds) { if (isNaN(seconds)) return '0:00'; const minutes = Math.floor(seconds / 60); seconds = Math.floor(seconds % 60); return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; } // Mostrar mensaje de error function showError(message) { errorMessage.textContent = message; errorMessage.style.display = 'block'; video.style.display = 'none'; loader.style.display = 'none'; } // Extraer título de la URL function extractTitleFromUrl(url) { if (!url) return null; try { const fileName = url.split('/').pop().split('?')[0]; const title = fileName.replace(/\.[^/.]+$/, '') .replace(/[-_]/g, ' ') .replace(/\b\w/g, c => c.toUpperCase()); return title; } catch { return null; } } // Eventos bigPlayButton.addEventListener('click', togglePlay); playPauseButton.addEventListener('click', togglePlay); video.addEventListener('click', togglePlay); // Ocultar controles después de 3 segundos de inactividad let timeout; videoWrapper.addEventListener('mousemove', () => { if (!isLocked) { videoWrapper.classList.remove('hide-controls'); clearTimeout(timeout); timeout = setTimeout(() => { if (isPlaying) videoWrapper.classList.add('hide-controls'); }, 3000); } }); });