This commit addresses crashes caused by accessing nil format values on streams:
- QualityProfile.swift: Add guard check for stream.format to prevent nil access crash
- MPVBackend.swift: Add nil check in canPlay method before comparing format
- PlayerStreams.swift: Add nil check before comparing format in asset processing
The crashes occurred when stream.format was nil and accessed as an implicitly unwrapped optional, causing "Unexpectedly found nil while implicitly unwrapping an Optional value" errors.
This commit addresses crashes caused by race conditions when accessing audio track arrays:
- MPVBackend.swift: Use safe index clamping to prevent array out of bounds crashes when selecting audio tracks
- PlayerModel.swift: Add selectedAudioTrack computed property for thread-safe audio track access
- ControlsOverlay.swift: Use safe accessor with "Original" fallback label
- PlaybackSettings.swift: Use safe accessor with "Original" fallback label
This fix resolves approximately 37% of crashes (23 out of 62 crash logs) that were caused by index out of range errors in MPVBackend.playStream at line 345.
Changed the default audio track content type from "Unknown" to "Original"
when the content type is not specified. This provides a more accurate
description for the default audio track.
When switching from AVPlayer to MPV backend, Now Playing controls (play/pause/seek) were disabled because AVPlayer maintained control of the remote command center and audio session. This fix ensures MPV can properly reclaim control.
Key changes:
- Clear AVPlayer's current item when switching to MPV to release media control
- Clear Now Playing info and set playback state to stopped before MPV takes over
- Reset remote command center by removing all targets (including AVPlayer's internal handlers) and re-adding custom handlers
- Force audio session deactivation/reactivation with .notifyOthersOnDeactivation
- Add forceReactivate parameter to setupAudioSessionForNowPlaying() for backend switches
- Ensure stream loading continues after Now Playing setup (don't return early)
The fix properly handles the transition by:
1. Clearing AVPlayer's media session completely
2. Scheduling async Now Playing setup without blocking stream loading
3. Resetting remote command handlers to reclaim control from AVPlayer
4. Re-activating audio session to establish MPV as the active player
The MPV backend now properly displays Now Playing information in iOS Control Center. The fix addresses the issue where the AVAudioSession would become inactive during MPV's playback lifecycle.
Key changes:
- Added setupAudioSessionForNowPlaying() method to activate AVAudioSession with proper playback category and movie playback mode
- Re-activate audio session at critical MPV events: FILE_LOADED, PLAYBACK_RESTART, AUDIO_RECONFIG, and during periodic updates
- Initialize audio session immediately after mpv_initialize() in MPVClient
The audio session must be re-activated at multiple points during playback, not just at initialization, to ensure iOS recognizes the app as playing media.
- Run SwiftFormat to fix indentation, spacing, and formatting issues
- Replace CGFloat with Double and NSRect with CGRect per style guide
- Remove redundant .center alignment specifications
- Remove unnecessary @available checks for satisfied deployment targets
- Fix closure brace indentation for consistency
- Disable closure_end_indentation rule to resolve SwiftFormat conflict
All linting checks now pass with zero errors and warnings.
Implemented automatic retry logic with exponential backoff (2, 4, 6 seconds) when file loading fails in AVPlayerBackend. Retries up to 3 times before showing error to user. Retry state is properly reset on successful loads. This matches the retry implementation added to MPVBackend in commit b6df73f9.
This commit enables proper Now Playing Info Center integration on iOS, allowing video playback information to appear in Control Center and Lock Screen with working remote controls.
Key changes:
- Activate audio session on app launch with setCategory(.playback, mode: .moviePlayback) and setActive(true)
- Set up remote commands on first play() call instead of during app initialization to avoid claiming Now Playing slot prematurely
- Remove removeTarget(nil) calls that were claiming Now Playing without content
- Enable remote commands (play, pause, toggle, seek) explicitly and add proper target handlers
- Use backend.isPlaying instead of PlayerModel.isPlaying to avoid race conditions
- Include playback rate (1.0 for playing, 0.0 for paused) in Now Playing info
- Update Now Playing info on main queue for thread safety
- Update Now Playing when switching between backends
- Remove audio session deactivation from pause() and stop() methods
Note: This fix works for AVPlayer backend. MPV backend has fundamental incompatibility with iOS Now Playing system.
Remove incorrect safe area insets from offsetY calculation that was
causing unequal black bars (smaller top, larger bottom). Now properly
centers video with equal padding like AVPlayer backend.
Refactored glUpdate to use requestRedraw method for better control. Added needsRedraw flag to prevent redundant display calls. Enabled asynchronous drawing on VideoLayer for improved performance. Modified displayLinkCallback to only report swap without triggering display to avoid flooding the main thread.
Added guard check to return early if currentTime is nil in getTimeUpdates. Simplified optional unwrapping by using the guarded currentTime value throughout the method.
Added comprehensive nil checks for stream resolution values across PlayerBackend, QualityProfile, and PlayerQueue to prevent crashes when streams have missing resolution metadata. Also added backend nil checks in PlayerQueue.
Implemented automatic retry logic with exponential backoff (2, 4, 6 seconds) when file loading fails in MPVBackend. Retries up to 3 times before showing error to user. Retry state is properly reset on successful loads.
Refactor orientation logic when entering fullscreen to better handle
button-initiated vs gesture-initiated transitions:
- Consolidate orientation determination into a single expression that
considers whether fullscreen was initiated by button or gesture
- When initiated by button, always use rotateToLandscapeOnEnterFullScreen
preference
- When initiated by gesture, respect current device orientation if already
in landscape, otherwise use preference
- Apply .landscape lock only for button-initiated transitions, .all for
gesture-initiated (when not orientation locked)
This provides more intuitive behavior where button taps rotate to preferred
orientation, while gestures respect current device orientation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add logging for video and audio asset URLs during stream filtering to help debug stream selection issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add proper validation and fallback logic for audio track selection in MPV backend:
- Validate audio track index is within bounds before switching
- Handle streams without separate audio tracks (single asset streams)
- Reset selectedAudioTrackIndex if out of bounds
- Add fallback path for streams without audioTracks array
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Extract and provide all available audio tracks (ORIGINAL, DUBBED, etc.) from Piped API instead of only using the first ORIGINAL track. This allows users to select between different audio languages and track types.
Changes:
- Extract all M4A audio tracks grouped by type and language
- Keep highest bitrate stream for each unique track combination
- Sort tracks with ORIGINAL first, then others alphabetically
- Pass audio tracks array to Stream for player selection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Clear the published string when a proper publishedAt date is extracted from uploadDate to prevent duplicate or inconsistent date display. Only fallback to string-based published date when no structured date is available.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update companion API endpoint URLs to use /companion/latest_version instead of /latest_version to match the correct API path structure.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- fix subtitle disabling not working
- make subtitle adding/removing async
- make subtitle menu non blocking
Signed-off-by: Toni Förster <toni.foerster@gmail.com>
- have a separate body view for each os
- macOS: set navigation title for search
- macOS: set min width to 835 for main content
- macOS: set main window min height to 600
- macOS: don’t have text behind the cancel button
- split SearchTextField into macOS and iOS/tvOS
- iOS: move search menu to the right
- iOS: unified search field
- make min width a constant
- add option to disable search suggestions
Signed-off-by: Toni Förster <toni.foerster@gmail.com>
Currently, we have a hard-coded list of resolutions. Since Invidious reports the actual resolution of a stream and does not hard-code them to a fixed value anymore, resolutions that are not in the list won’t be handled, and the stream cannot be played back.
Instead of hard-coding even more resolutions (and inadvertently might not cover all), we revert the list back to a finite set of resolutions, the users can select from. All other resolutions are handled dynamically and compared to the existing set of defined resolutions when selecting the best stream for playback.
Signed-off-by: Toni Förster <toni.foerster@gmail.com>
- use displays refresh rate
- execute needs drawing with higher priority
- run create() with higher priority
- determine the number of threads used for rendering
- enable VSYNC and change video-sync to display-resample
- iOS/tvOS: set new display refresh rate on change
- run setSize with higher priority
- add more options to MPVClient
- get refresh rate updates
- sync refresh rate to fps
- update CADisplayLink to current refresh rate
- update refresh rate on macOS
- Add experimental feature to sync display with content fps
Signed-off-by: Toni Förster <toni.foerster@gmail.com>