Manual HLS segment download fails for CBS streams (DRM/CDN protection
returns 438-byte placeholder responses). New approach captures video
frames directly from AVPlayer via AVPlayerItemVideoOutput + AVAssetWriter.
- AVPlayerItemVideoOutput captures pixel buffers during playback
- AVAssetWriter encodes to H.264/AAC MP4
- CADisplayLink drives capture at 30fps
- Works with any content AVPlayer can play (HLS, local files, SMB)
CBS streams require iPhone UA to serve actual TS data. Without it, segments
download as empty/small placeholder responses. Also added per-segment size
logging for diagnostics.
FileHandle(forWritingTo:) throws when file doesn't exist — it opens, not creates.
Changed to Data.append + Data.write which creates the file automatically.
- Added recorderErrorMessage @Published var for persistent error display
- Recording errors now shown as overlay with error details + Dismiss button
- presentShareSheet now logs errors and sets recorderErrorMessage on failure
- Errors like 'No window scene' or 'No view controller' visible on screen
SwiftUI multiple .sheet modifiers can conflict — replaced the pendingExportURL
sheet binding with a direct UIKit UIActivityViewController presentation from
the key window's root VC. This bypasses any SwiftUI sheet stack issues.
Also added copyLocalFile() for local file recording (FileManager.copyItem
instead of URLSession.download which fails on file:// URLs).
HLSRecorder.recordStream() now checks url.isFileURL first and copies
locally instead of using URLSession.download() which only works for
http/https URLs. Fixes recording local mp4 files on iOS.
macOS:
- Replace deprecated NSSavePanel.begin(completionHandler:) with async begin() API
- The old API has been deprecated since macOS 12 and may silently fail on macOS 26
- Added diagnostic logging throughout recording completion flow
iOS:
- Added diagnostic logging to track onRecordingSaved → pendingExportURL → sheet chain
- Logs file existence, size after recording completes
Diagnostic logging added to help verify the save dialog flow:
- macOS: file size check, showSaveDialog entry, NSSavePanel response
- iOS: recordStream output verification, onRecordingSaved call, sheet binding activation
Recording:
- stopRecording() now only sets stopRequested=true, no longer cancels task
- Task exits polling loop naturally → merges TS segments → exports MP4
- System share sheet + Photos save shown on completion
- Added HEAD request to detect HLS Content-Type for non-.m3u8 URLs
- Faster exit when stopRequested && segments exist
Playback:
- Copy imported files to app Documents sandbox (security-scoped URLs expire after defer)
- Remove Referer header that blocks some streams; use iPhone UA only
- AVURLAsset options only for http/https schemes
Media Library UI (iOS):
- Replace NavigationSplitView with VStack + horizontal category chips
- Search bar at top, swipe actions for queue/delete
- scrollDismissesKeyboard(.interactively) on list
- Tap gesture sends resignFirstResponder to dismiss keyboard
- Compact rows with context menu, inline play button
- Close button in navigation bar
- presentationDetents(.medium) for tag/playlist sheets
- Poll m3u8 every 2s to download new segments (supports live HLS)
- For non-HLS URLs, download entire file
- Accumulate TS segments incrementally, merge on stop
- Remux TS→MP4 via AVAssetExportSession
- Show system share sheet + save to Photos on completion
- Proper cancellation support via Task.cancel()
- build-ios.sh: 设置正确 Team ID (LR8YT5M53U), 添加 GENERATE_INFOPLIST_FILE
- MiniPlayerApp.swift: Bundle.module → Bundle.main (非SPM), @main 条件编译
- 签名: Apple Development + Personal Team provisioning profile
- 产物: MiniPlayer.ipa (584K)
- Hide close/minimize/zoom buttons on startup
- Show all buttons + title on hover, hide on exit
- Use asyncAfter to ensure window is ready
- Find visible window instead of first window
- Enable window drag by background when title bar hidden