Skip to content

Fix local video add/drag and large-file cast crash (0.29.7) - #412

Merged
ad-repo merged 5 commits into
mainfrom
fix/local-video-and-large-cast
Aug 2, 2026
Merged

ad-repo merged 5 commits into
mainfrom
fix/local-video-and-large-cast

Conversation

@ad-repo

@ad-repo ad-repo commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes several local-video handling gaps and a crash when casting large media. Ships as 0.29.7.

Local video add / drag / drop

Each drop/add surface has its own handler, and most silently excluded video:

  • Main player window drop — Modern (ModernMainWindowView) and Classic (MainWindowView) skins have separate handlers; both passed includeVideo: false, so dropping a video did nothing. Now accepted and routed to the video player.
  • Library window drop — only appended audio; video now routes to MediaLibrary.addVideoFiles and reveals the Movies tab.
  • "Add Video Files…" menu — added the movie but never switched to Movies, so it looked like a no-op. Now reveals Movies; the file picker no longer greys out .mkv/.avi/.webm/.ts.
  • Track(url:) detection — relied on AVFoundation track probing, which returns empty for .mkv/.avi/.webm (the formats VLCKit was adopted for), misclassifying them as audio. Added an extension fallback.

Large-file cast crash (OOM)

LocalMediaServer served files by reading the whole file into memory (Data(contentsOf:), and readData(ofLength:) for the bytes=0- full-file range a Chromecast requests). Casting a multi-GB movie exhausted memory and the OS killed the process with no crash log. Both handlers now stream via a FileByteStream (256 KB chunks) through HTTPBodySequence(from:count:); memory stays flat and seeking still works.

Testing

  • Build: swift build clean.
  • Manually verified by the user: drag-to-play on the main window, Add Video Files, library drop, and casting an 18 GB file (previously crashed instantly, now plays).
  • Note: drag/drop, menu actions, and real Chromecast casting can't be driven from CI/CLI.

Docs

  • CHANGELOG.md — 0.29.7 entries.
  • skills/local-library/SKILL.md — per-surface video drop/add routing + Track extension fallback.
  • skills/chromecast-casting/SKILL.mdLocalMediaServer must stream, never buffer.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for importing and dragging supported video files across player and Library workflows.
    • Added video file extensions and improved handling of playlists and directories containing videos.
    • Added Movies category visibility after importing local videos.
  • Bug Fixes

    • Improved casting of large local files and range requests to prevent failures and excessive memory use.
    • Fixed video detection for additional container formats.
  • Documentation

    • Updated version 0.29.7 release notes and documentation for video handling and local media casting.

ad-repo and others added 3 commits August 1, 2026 20:46
Video files were silently dropped at several UI surfaces because each has
its own handler and most excluded video:

- Modern and Classic main-window drops passed includeVideo: false, so
  dropping a video onto the player did nothing (no player launched). Both
  now accept video; the Track routes to the video player via mediaType.
- The library-window drop only appended audio; video files are now routed
  to MediaLibrary.addVideoFiles and reveal the Movies tab.
- "Add Video Files..." added the movie but never switched to the Movies
  tab, so the import looked like a no-op. It now reveals Movies, and its
  file picker no longer greys out .mkv/.avi/.webm/.ts.
- Track(url:) detected video only via AVAsset track probing, which returns
  empty for containers AVFoundation can't parse (.mkv/.avi/.webm) — the
  very formats VLCKit was adopted for — so they were misclassified as
  audio and dropped by the audio engine. Added an extension fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LocalMediaServer served registered local files by reading the whole file
into memory: Data(contentsOf:) for full-file GETs, and
readData(ofLength: Int(length)) for range requests — where a cast device
opens playback with "Range: bytes=0-" (the entire file). Casting a
multi-gigabyte movie exhausted memory and the OS killed the process with
no crash log, so the app appeared to vanish the instant the cast started.
Small files fit in RAM, so only large media was affected.

Both handlers now stream through a FileByteStream (an AsyncBufferedSequence
of UInt8 backed by a FileHandle, 256 KB chunks) wrapped in
HTTPBodySequence(from:count:), mirroring the existing URLSessionByteStream
proxy path. Content-Length/Content-Range are unchanged, so seeking still
works; memory stays flat regardless of file size.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump version to 0.29.7 and add changelog entries for the local video
add/drag routing fixes and the large-file cast OOM fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ad-repo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d0482fea-5b7b-4f6d-9d46-86be9c671f64

📥 Commits

Reviewing files that changed from the base of the PR and between 5e17db9 and f8e361b.

📒 Files selected for processing (6)
  • Sources/NullPlayer/Casting/LocalMediaServer.swift
  • Sources/NullPlayer/Resources/ThirdPartyLicenses/ThirdPartyNotices.txt
  • Sources/NullPlayer/Windows/ModernLibraryBrowser/ModernLibraryBrowserView.swift
  • Sources/NullPlayer/Windows/PlexBrowser/PlexBrowserView.swift
  • skills/chromecast-casting/SKILL.md
  • skills/local-library/SKILL.md
📝 Walkthrough

Walkthrough

Local video detection and import flows now support additional extensions across player and library windows. Local casting streams full-file and range responses in bounded chunks. Release metadata and documentation now describe version 0.29.7 and these behaviors.

Changes

Local media handling

Layer / File(s) Summary
Video classification and import routing
Sources/NullPlayer/Data/Models/Track.swift, Sources/NullPlayer/Windows/*
Video detection falls back to supported file extensions. Main-window and library drag-and-drop flows accept video files. Library imports route videos to Movies, and playback combines video URLs with cue-expanded tracks. File selection uses all supported video extensions.
Bounded local file streaming
Sources/NullPlayer/Casting/LocalMediaServer.swift, skills/chromecast-casting/SKILL.md
LocalMediaServer streams full-file and range responses through FileByteStream and HTTPBodySequence in 256 KiB chunks while preserving response length and range metadata.
Release metadata and documentation
Sources/NullPlayer/Resources/Info.plist, CHANGELOG.md, skills/local-library/SKILL.md
The app version and changelog now use 0.29.7. Local video routing and casting requirements are documented.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CastingClient
  participant LocalMediaServer
  participant FileByteStream
  CastingClient->>LocalMediaServer: Request full file or byte range
  LocalMediaServer->>FileByteStream: Read bounded chunks
  FileByteStream-->>LocalMediaServer: Return file bytes
  LocalMediaServer-->>CastingClient: Stream response with range metadata
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: fixing local video import and drag-and-drop, and preventing large-file casting crashes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/local-video-and-large-cast

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/NullPlayer/Casting/LocalMediaServer.swift`:
- Around line 208-227: Update FileByteStream initialization and seekIfNeeded to
propagate file-open and seek failures instead of suppressing them with try? or
optional handles. Ensure handleMediaRequest validates successful opening and
seeking before returning the response, and make nextBuffer throw on subsequent
read or seek failures rather than returning nil, while preserving normal EOF
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e6fb96e-5e78-47b0-bd63-2fcb2d897d57

📥 Commits

Reviewing files that changed from the base of the PR and between 232cdab and 5e17db9.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • Sources/NullPlayer/Casting/LocalMediaServer.swift
  • Sources/NullPlayer/Data/Models/Track.swift
  • Sources/NullPlayer/Resources/Info.plist
  • Sources/NullPlayer/Windows/MainWindow/MainWindowView.swift
  • Sources/NullPlayer/Windows/ModernLibraryBrowser/ModernLibraryBrowserView.swift
  • Sources/NullPlayer/Windows/ModernMainWindow/ModernMainWindowView.swift
  • skills/chromecast-casting/SKILL.md
  • skills/local-library/SKILL.md

Comment thread Sources/NullPlayer/Casting/LocalMediaServer.swift Outdated
@ad-repo
ad-repo merged commit 9961cf5 into main Aug 2, 2026
1 check was pending
@ad-repo
ad-repo deleted the fix/local-video-and-large-cast branch August 31, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant