Add visible labels for all pickers in settings on macOS by wrapping them
in HStack with Text labels and Spacer() for proper alignment.
Fixed pickers:
- BrowsingSettings: Startup section, Thumbnails quality, Player bar gestures
- PlayerControlsSettings: Action button labels
- PlayerSettings: Source, Inspector, Caption size/color/languages, Sidebar
All picker labels now consistently display with left-aligned text and
right-aligned picker controls on macOS.
- 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.
Wrap favoritesChanged.toggle() calls in MainActor.run blocks to ensure
main actor-isolated state mutations happen on the correct actor context.
This resolves Swift concurrency warnings when updating state from
nonisolated async contexts.
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.
- Update macOS build job to use macos-latest and Xcode 26.0.1 (matching iOS/tvOS)
- Update bump-build workflow to use latest action versions (checkout@v4, create-pull-request@v7)
- Add cache-version to ruby/setup-ruby for consistency
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.
Modified HorizontalCells to conditionally apply edgesIgnoringSafeArea based on navigation style. In sidebar mode (iPad), content now respects safe areas and won't overlap with the sidebar. In tab mode (iPhone), content maintains full-width scrolling behavior.
When displaying the video details overlay on iPad in non-fullscreen windows,
add 65px of left padding (50px for system controls width + 15px spacing) to
prevent content from overlapping with iPad system controls.
When the iPad window is resized (not fullscreen), player controls now have 10px horizontal padding from the edges for better spacing and visual comfort.
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.
Adds fullscreen detection utility to Constants.swift to determine if the window occupies the full screen on iOS. Uses this to conditionally add leading padding to player controls on iPad in non-fullscreen windows, preventing overlap with system window controls.
Changed UIRequiresFullScreen to NO to allow pixel-perfect window
resizing on iPad. Also moved ITSAppUsesNonExemptEncryption to
project settings for cleaner configuration.
When playing video fullscreen in a resizable window on iPad, the player
height was being forced to UIScreen.main.bounds.size.height, which is
the full screen size. In resizable windows, this caused the player
container to extend beyond the visible window bounds, clipping controls
at the bottom.
Now on iPad, the player uses natural geometry provided by its container
which respects actual window bounds, while iPhone continues using
screen-based calculation for proper fullscreen behavior.
Added plain button style for rate increase/decrease buttons on iOS. Fixed safe area insets in VerticalCells to respect sidebar navigation style on iOS.
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 height reservation to FavoriteItemView to prevent layout shifts during content loading. Changed HomeView to use LazyVStack for better performance. Converted QueueView from LazyVStack to VStack. Disabled animations on content count changes across multiple views to prevent jarring layout transitions. Added width constraint to stream button in PlaybackSettings.
Removed border overlay on search text field for macOS 26+ to match new design guidelines. Added conditional padding to sort label for better alignment on macOS 26+.
Standardized picker and button sizing with consistent alignment and control sizes. Added SettingsPickerModifier to all macOS pickers with menu style. Improved rate buttons with proper sizing and alignment. Added text truncation for stream descriptions to prevent overflow.
Restructured PlayerControls view hierarchy by extracting controls content into a separate computed property for better code organization. Added shouldShowCustomControls property to VideoPlayerView to properly determine when custom controls should be shown vs system controls. Updated hover logic to only show/hide custom controls when appropriate.
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.
- Make glassEffect iOS-only as it's not available on other platforms
- Use ultraThinMaterial fallback for macOS and tvOS
- Fixes build error in GitHub Actions with Xcode 16.4
Added checks to skip the framework conversion script when running in
CI environments (GitHub Actions). The script now exits early if either
CI or GITHUB_ACTIONS environment variables are set.
This ensures:
- Script only runs for local development builds
- GitHub Actions builds use frameworks as-is from MPVKit
- iOS/tvOS builds on macOS 15 with Xcode 16.4 work without conversion
🤖 Generated with Claude Code (https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit implements a workaround for MPVKit shipping frameworks as
shallow bundles, which are incompatible with macOS Developer ID
distribution requirements.
Changes:
1. Raised macOS deployment target to 14.0
- Matches MPVKit's minimum requirement
- Previous: 11.0
- New: 14.0
2. Added Run Script phase to convert frameworks
- Converts MPVKit frameworks from shallow to versioned bundles
- Required for macOS Developer ID code signing
- Runs after framework embedding
- Converts all 28 MPVKit frameworks during build
3. Modified fastlane build process
- Build and archive without export
- Create PKG directly from archive
- Avoids extended attribute issues from export process
4. Pinned MPVKit to specific commit
- Commit: e7e914a70e943f0d4f050c9ede793af8f6e74ad7
- Ensures consistent framework structure
Known Issues:
- Some frameworks (Libplacebo, Libluajit) have signature issues after
conversion that still prevent successful notarization
- This is a workaround; the root issue should be fixed in MPVKit by
providing macOS-compatible versioned bundle frameworks
See minimal reproduction project at:
/tmp/MPVKit-Notarization-Issue/MPVKitNotarizationTest/
Related: MPVKit should provide macOS-specific XCFrameworks with
versioned bundles for proper Developer ID distribution support.
🤖 Generated with Claude Code (https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add print_log: true to notarize action to display detailed error
messages when notarization fails in GitHub Actions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit resolves multiple build errors caused by using APIs that
require newer OS versions than the deployment targets (macOS 11.0 and
tvOS 15.0).
macOS fixes:
- Add missing init(frame:) initializer to PlayerLayerView
- Add availability checks for textSelection modifier (macOS 12.0+)
- Add availability checks for AttributedString (macOS 12.0+)
- Add availability checks for listStyle.inset(alternatesRowBackgrounds:) (macOS 12.0+)
- Add availability checks for focusScope modifier (macOS 12.0+)
- Correct listRowSeparator availability from macOS 12.0 to 13.0
tvOS fixes:
- Use older onChange(of:) signature compatible with tvOS 15.0
- Add availability check for Menu with primaryAction (tvOS 17.0+)
All changes include appropriate fallbacks for older OS versions to
maintain backward compatibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Bundler 2.6.3 requires Ruby >= 3.1.0, but workflows were using Ruby 3.0.7,
causing build failures. Updated both release and bump-build workflows to use
Ruby 3.1.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds five new languages to the Xcode project knownRegions that have at least 50% translation coverage from Weblate:
- Finnish (fi): 100%
- Indonesian (id): 100%
- Korean (ko): 100%
- Dutch (nl): 81.9%
- Swedish (sv): 77.4%
Languages with less than 50% coverage (ars, kab, sk) were not added.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>