fix: make iOS/Safari media player work (#27)#51
Merged
Conversation
…rt (#27) - Use native <video> element for HLS on iOS/Safari instead of Video.js (VHS tech fights Safari's native HLS player causing corruption/abort) - Add playsinline/webkit-playsinline attributes for inline playback in modals - Defer VideoPlayer mount until SSE confirms stream readiness (prevents hitting HLS endpoint before torrent has peers) - Rewrite HLS segment endpoint with Range request support (206 Partial Content), HEAD/OPTIONS handlers, and full CORS headers for iOS Safari - Increase file wait timeout from 10s to 30s and require 2MB minimum for reliable codec detection on slow torrents - Add FFmpeg error tolerance flags for partial file streaming Closes #27
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The native video src-update short-circuit was not gated behind the Safari/HLS condition, preventing the component from switching back to Video.js when the source changed to a non-HLS format. Now tears down the native element and resets isNativePlayerRef when the source is no longer HLS-on-Safari. Also deduplicates the iOS/Safari detection (single declaration used throughout initializePlayer).
The native HLS video path passes null to onReady since there is no Video.js Player instance. Update the prop type from (player: Player) to (player: Player | null) to fix the TypeScript error.
…ideo init Three root causes prevented iOS/Safari HLS video playback from working: 1. shouldMountPlayer required fileReady (20MB buffer) before mounting the VideoPlayer for HLS streams. The HLS endpoint has its own buffering via FFmpeg, so this gate caused infinite loading. Now only waits for torrent metadata (torrent.ready). 2. Subscription check on HLS playlist route rejected Safari's periodic playlist re-fetches (media engine may not send cookies). Moved auth check to only run for new sessions; existing session re-fetches skip it. Added full CORS headers and OPTIONS handler. 3. Native <video> element for iOS HLS was missing crossOrigin attribute, preload hint, and called play() before loadedmetadata. Fixed init order: attach listeners before setting src, defer autoplay to loadedmetadata, add crossOrigin='anonymous' for CORS segment fetches. Closes #27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes three root causes that prevented iOS/Safari HLS video playback from ever working. Desktop transcoding + ffmpeg streaming works fine, but iOS was broken end-to-end.
Root Causes & Fixes
1. Player mount gated behind 20MB buffer threshold (primary blocker)
shouldMountPlayerfor HLS requiredfileReadywhich demands 20MB of video data downloadedtorrent.ready(metadata available) instead of the full 20MBfileReadythreshold2. Subscription check broke Safari playlist re-fetches
3. Native video element initialization issues
crossOrigin = 'anonymous'— required for Safari to fetch CORS-enabled HLS segmentspreload = 'auto'play()called beforeloadedmetadata— iOS rejects early play() callssrcwas set, missing early eventscrossOriginandpreloadattributes, moved listeners beforesrc, deferplay()to afterloadedmetadataFiles Changed
src/components/media/media-player-modal.tsx— HLS mount gating + loading spinner logicsrc/app/api/stream/hls/route.ts— Auth check ordering, CORS headers, OPTIONS handlersrc/components/video/video-player.tsx— Native video element init for iOS HLSTesting
Closes #27