6 Commits

Author SHA1 Message Date
85f5699878 fix: use AVPlayerView (AVKit) instead of custom AVPlayerLayer
Root cause: custom AVPlayerLayer lifecycle management causes CoreMedia
FigNotificationCenter weak listener race condition. Apple's AVPlayerView
handles all internal teardown correctly.

Changes:
- VideoPlayerView.swift: NSViewRepresentable wrapping AVPlayerView (macOS)
  and UIViewControllerRepresentable wrapping AVPlayerViewController (iOS)
- PlayerBridge.swift: fullscreen uses AVPlayerView, removed
  FullscreenPlayerView class entirely
- Fullscreen interaction via NSEvent.addLocalMonitorForEvents:
  double-click exits, single-click toggles play/pause, Escape exits
- cleanup() now closes fullscreen window and removes event monitor
- Removed ExitFullscreen NotificationCenter listener from MiniPlayerApp
2026-06-22 01:16:49 +08:00
0d63414214 fix: replace CoreMedia periodic time observer with pure Swift Timer to eliminate autorelease pool race
Root cause: addPeriodicTimeObserver's internal FigNotificationCenter weak
listener mechanism races with autorelease pool drain on main thread, causing
double-free of weak reference wrappers (KERN_INVALID_ADDRESS).

Changes:
- Replace addPeriodicTimeObserver with Timer.scheduledTimer (bypasses CoreMedia
  weak listener infrastructure entirely)
- Remove ALL Task { @MainActor in } from observer callbacks — these created
  unstructured tasks whose weak ref wrappers conflicted with CoreMedia internals
- Use DispatchQueue.main.async for KVO callbacks (may fire from non-main thread)
- Direct calls for queue: .main callbacks (NotificationCenter, end observer)
- Add isTearingDown flag to prevent callbacks firing during cleanup
- Fix cleanup() order: timer → KVO → notifications → replaceCurrentItem(nil)
- Fix FullscreenPlayerView: use addSublayer instead of replacing backing layer
- Add .onDisappear { bridge.cleanup() } to ensure cleanup before dealloc
- Remove Combine import (no longer needed)
2026-06-22 01:06:39 +08:00
f14d47b5c8 fix: use sublayer instead of replacing NSView backing layer to prevent autorelease crash
- PlayerNSView: AVPlayerLayer as sublayer (not layer= replacement)
  NSView internally manages a sublayer array; replacing the backing
  layer directly causes dangling refs during autorelease pool drain
- cleanup(): only pause + remove observers, no replaceCurrentItem
  CoreMedia internal state gets corrupted when item is replaced
  during view teardown; let it release naturally with deinit
2026-06-22 00:47:05 +08:00
6811572b7e fix: rewrite UI as pure SwiftUI, fix crash/fullscreen/height issues
- Skip BricksView, render video directly in SwiftUI (fixes 1/3 height)
- Fullscreen uses plain NSView+AVPlayerLayer (fixes objc_release crash)
- Remove NSApp.hide(nil) (fixes fullscreen not showing)
- Add volume +/- buttons and volume slider indicator
- Add iOS/iPadOS support with #if os guards
- ProgressSlider decoupled from BricksEngine
- PlayerBridge no longer depends on player.ui JSON
2026-06-22 00:04:06 +08:00
2a5e3b8ad7 feat: video fills entire window, compact control bar, single-tap play/pause 2026-06-21 23:27:17 +08:00
Hermes Agent
c69ec38dc3 refactor: rewrite MiniPlayer using SwiftBricks framework
- UI defined in player.json (Bricks JSON schema)
- Custom widgets: VideoPlayer (AVPlayer layer), ProgressSlider (seek bar)
- PlayerBridge connects AVPlayer to BricksEngine event bus
- All interactions via binds/events (no imperative UI code)
- Depends on SwiftBricks SPM package
2026-06-21 17:48:06 +08:00