//! Video seek strategy decision logic. //! //! This is pure logic, extracted from the command layer so it can be unit-tested //! in the player core. The `player_seek_video` command translates the resulting //! [`VideoSeekStrategy`] into a concrete backend/frontend action. /// Seek strategy for video playback, derived from a stream's characteristics. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum VideoSeekStrategy { /// Local file - always use native seek on backend LocalNativeSeek, /// HLS or direct stream with HTML5 - frontend handles seek, skip backend Html5NativeSeek, /// HLS or direct stream with native backend - backend handles seek BackendNativeSeek, /// Transcoded non-HLS with HTML5 - reload stream, frontend handles Html5ReloadStream, /// Transcoded non-HLS with native backend - reload stream, backend handles BackendReloadStream, } /// Determine the video seek strategy based on stream characteristics. /// /// This is a pure function extracted for testability. /// /// # Arguments /// * `is_local` - Whether the file is a local download /// * `is_hls` - Whether the stream URL contains ".m3u8" (HLS stream) /// * `needs_transcoding` - Whether the content needs transcoding /// * `use_html5` - Whether frontend is using HTML5 video element pub fn determine_video_seek_strategy( is_local: bool, is_hls: bool, needs_transcoding: bool, use_html5: bool, ) -> VideoSeekStrategy { // Local files always support native seeking via backend if is_local { return VideoSeekStrategy::LocalNativeSeek; } // HLS streams and direct play (non-transcoded) support native seeking if is_hls || !needs_transcoding { if use_html5 { // HTML5 backend - frontend handles seeking via videoElement.currentTime // We don't call backend.seek() because video is in HTML5 element, not in MPV VideoSeekStrategy::Html5NativeSeek } else { // Native backend (MPV) - backend handles seeking VideoSeekStrategy::BackendNativeSeek } } else { // Transcoded non-HLS streams need server-side seek (reload from new position) if use_html5 { VideoSeekStrategy::Html5ReloadStream } else { VideoSeekStrategy::BackendReloadStream } } } #[cfg(test)] mod tests { use super::*; /// Test video seek strategy for local files #[test] fn test_seek_strategy_local_file() { // Local files always use native backend seek regardless of other flags assert_eq!( determine_video_seek_strategy(true, false, false, false), VideoSeekStrategy::LocalNativeSeek ); assert_eq!( determine_video_seek_strategy(true, false, false, true), VideoSeekStrategy::LocalNativeSeek ); assert_eq!( determine_video_seek_strategy(true, true, true, true), VideoSeekStrategy::LocalNativeSeek ); } /// Test video seek strategy for HLS streams #[test] fn test_seek_strategy_hls_stream() { // HLS with HTML5 - frontend handles seek, don't call backend assert_eq!( determine_video_seek_strategy(false, true, false, true), VideoSeekStrategy::Html5NativeSeek ); // HLS with native backend - backend handles seek assert_eq!( determine_video_seek_strategy(false, true, false, false), VideoSeekStrategy::BackendNativeSeek ); // HLS even with needs_transcoding flag - still native seek (HLS supports it) assert_eq!( determine_video_seek_strategy(false, true, true, true), VideoSeekStrategy::Html5NativeSeek ); } /// Test video seek strategy for direct play (non-transcoded) streams #[test] fn test_seek_strategy_direct_play() { // Direct play with HTML5 - frontend handles seek assert_eq!( determine_video_seek_strategy(false, false, false, true), VideoSeekStrategy::Html5NativeSeek ); // Direct play with native backend - backend handles seek assert_eq!( determine_video_seek_strategy(false, false, false, false), VideoSeekStrategy::BackendNativeSeek ); } /// Test video seek strategy for transcoded non-HLS streams #[test] fn test_seek_strategy_transcoded_non_hls() { // Transcoded non-HLS with HTML5 - need to reload stream, frontend handles assert_eq!( determine_video_seek_strategy(false, false, true, true), VideoSeekStrategy::Html5ReloadStream ); // Transcoded non-HLS with native backend - need to reload stream, backend handles assert_eq!( determine_video_seek_strategy(false, false, true, false), VideoSeekStrategy::BackendReloadStream ); } /// Test the specific bug fix: HLS + HTML5 should NOT call backend seek /// This was the bug causing "Raw(-10)" errors #[test] fn test_hls_html5_does_not_use_backend_seek() { let strategy = determine_video_seek_strategy(false, true, false, true); // Should be Html5NativeSeek, NOT BackendNativeSeek assert_eq!(strategy, VideoSeekStrategy::Html5NativeSeek); assert_ne!(strategy, VideoSeekStrategy::BackendNativeSeek); } }