/** * Mobile-specific player features: * - Double-tap left/right to skip ±10s * - Object-fit toggle button for widescreen fill */ export function isMobile() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } export function initMobileWidescreen(playerWrapper, video) { if (!isMobile()) { return; } const controls = playerWrapper.querySelector('.hstream-player__controls'); if (!controls) { return; } const btn = document.createElement('button'); btn.className = 'hstream-player__button hstream-player__mobile-fill-btn'; btn.type = 'button'; btn.setAttribute('aria-label', 'Toggle screen fill'); btn.innerHTML = ''; btn.title = 'Fill Screen'; const fullscreenBtn = controls.querySelector('[data-action="fullscreen"]'); if (fullscreenBtn) { fullscreenBtn.insertAdjacentElement('beforebegin', btn); } else { controls.appendChild(btn); } let fillEnabled = true; video.style.objectFit = 'cover'; btn.addEventListener('click', (e) => { e.stopPropagation(); if (fillEnabled) { video.style.objectFit = 'contain'; fillEnabled = false; btn.classList.remove('hstream-player__button--active'); } else { video.style.objectFit = 'cover'; fillEnabled = true; btn.classList.add('hstream-player__button--active'); } }); btn.classList.add('hstream-player__button--active'); } export function initMobileDoubleTap(playerWrapper, video, player) { if (!isMobile()) { return; } const skipOverlay = playerWrapper.querySelector('.hstream-player__skip-overlay'); if (!skipOverlay) { return; } class MultiClickCounter { constructor() { this.timers = []; this.count = 0; this.reseted = 0; this.lastSide = null; } clicked() { this.count += 1; const xcount = this.count; this.timers.push(setTimeout(() => this.reset(xcount), 500)); return this.count; } resetCount(n) { this.reseted = this.count; this.count = n; this.timers.forEach(t => clearTimeout(t)); this.timers = []; } reset(xcount) { if (this.count > xcount) return; this.count = 0; this.lastSide = null; this.reseted = 0; skipOverlay.classList.remove('hstream-player__skip-overlay--visible'); this.timers = []; } } const counter = new MultiClickCounter(); const handleTap = (e) => { e.preventDefault(); const count = counter.clicked(); if (count < 2) return; const rect = e.target.getBoundingClientRect(); const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left; const perc = (x / rect.width) * 100; let shouldReset = true; const lastSide = counter.lastSide; if (lastSide === null) { shouldReset = false; } if (perc < 40) { if (player.currentTime === 0) return; counter.lastSide = 'L'; if (shouldReset && lastSide !== 'L') { counter.resetCount(1); return; } const skipSeconds = (count - 1) * 10; player.currentTime = Math.max(0, player.currentTime - skipSeconds); skipOverlay.innerHTML = '' + skipSeconds + 's'; skipOverlay.classList.add('hstream-player__skip-overlay--visible'); setTimeout(() => skipOverlay.classList.remove('hstream-player__skip-overlay--visible'), 800); } else if (perc > 60) { if (player.currentTime >= player.duration) return; counter.lastSide = 'R'; if (shouldReset && lastSide !== 'R') { counter.resetCount(1); return; } const skipSeconds = (count - 1) * 10; player.currentTime = Math.min(player.duration, player.currentTime + skipSeconds); skipOverlay.innerHTML = '' + skipSeconds + 's'; skipOverlay.classList.add('hstream-player__skip-overlay--visible'); setTimeout(() => skipOverlay.classList.remove('hstream-player__skip-overlay--visible'), 800); } else { player.togglePlay(); counter.lastSide = 'C'; } }; playerWrapper.addEventListener('click', handleTap); video.addEventListener('dblclick', (e) => { e.preventDefault(); e.stopPropagation(); }); }