diff --git a/src/lib/VideoPlayer.svelte b/src/lib/VideoPlayer.svelte index 248fd20..c3f2122 100644 --- a/src/lib/VideoPlayer.svelte +++ b/src/lib/VideoPlayer.svelte @@ -50,14 +50,6 @@ hls.attachMedia(videoEl); hls.on(Hls.Events.MANIFEST_PARSED, () => { videoEl?.play().catch(() => {}); - // Warn Safari users about Opus audio - const isSafari = /^((?!chrome|android).)*safari/i.test( - navigator.userAgent, - ); - if (isSafari) { - errorMsg = - "🐸 Audio may not work in Safari — these streams use Opus. Try Chrome or Firefox for sound."; - } }); hls.on(Hls.Events.ERROR, (_event, data) => { console.error("HLS error:", data); @@ -145,13 +137,14 @@ isFullscreen = !!document.fullscreenElement; } - /** Calculate seek position from a mouse event on the scrub bar */ - function scrubFromEvent(e: MouseEvent) { + /** Calculate seek position from a mouse or touch event on the scrub bar */ + function scrubFromEvent(e: MouseEvent | TouchEvent) { if (!scrubBarEl) return; + const clientX = 'touches' in e ? e.touches[0]?.clientX ?? e.changedTouches[0]?.clientX ?? 0 : e.clientX; const rect = scrubBarEl.getBoundingClientRect(); const pct = Math.max( 0, - Math.min(1, (e.clientX - rect.left) / rect.width), + Math.min(1, (clientX - rect.left) / rect.width), ); const delta = pct - lastFrogProgress; if (Math.abs(delta) > 0.02) { @@ -174,7 +167,7 @@ let wasPlayingBeforeScrub = false; // Resume playback after scrub if it was playing /** Start scrubbing — pause video to prevent buffering issues (especially Firefox) */ - function onScrubDown(e: MouseEvent) { + function onScrubDown(e: MouseEvent | TouchEvent) { e.preventDefault(); isScrubbing = true; wasPlayingBeforeScrub = !!(videoEl && !videoEl.paused); @@ -182,10 +175,12 @@ scrubFromEvent(e); window.addEventListener("mousemove", onScrubMove); window.addEventListener("mouseup", onScrubUp); + window.addEventListener("touchmove", onScrubMove, { passive: false }); + window.addEventListener("touchend", onScrubUp); } /** Start scrubbing by grabbing the frog sprite directly */ - function onFrogDown(e: MouseEvent) { + function onFrogDown(e: MouseEvent | TouchEvent) { e.preventDefault(); e.stopPropagation(); isScrubbing = true; @@ -193,10 +188,13 @@ if (videoEl && wasPlayingBeforeScrub) videoEl.pause(); window.addEventListener("mousemove", onScrubMove); window.addEventListener("mouseup", onScrubUp); + window.addEventListener("touchmove", onScrubMove, { passive: false }); + window.addEventListener("touchend", onScrubUp); } - function onScrubMove(e: MouseEvent) { + function onScrubMove(e: MouseEvent | TouchEvent) { if (!isScrubbing) return; + e.preventDefault(); scrubFromEvent(e); } @@ -207,6 +205,8 @@ } window.removeEventListener("mousemove", onScrubMove); window.removeEventListener("mouseup", onScrubUp); + window.removeEventListener("touchmove", onScrubMove); + window.removeEventListener("touchend", onScrubUp); } function onKeyDown(e: KeyboardEvent) { @@ -236,6 +236,7 @@ class="player-wrapper" onmousemove={onMouseActivity} onmouseenter={onMouseActivity} + ontouchstart={onMouseActivity} onmouseleave={() => { if (playing) showControls = false; }} @@ -260,6 +261,7 @@ class="scrub-bar" bind:this={scrubBarEl} onmousedown={onScrubDown} + ontouchstart={onScrubDown} role="slider" aria-valuenow={currentTime} aria-valuemin={0} @@ -279,6 +281,7 @@ style="left: {scrubProgress * 100}%;" class:flipped={frogFlipped} onmousedown={onFrogDown} + ontouchstart={onFrogDown} >