53 Commits

Author SHA1 Message Date
da4495ab3e fix: Xcode ViewBuilder compile error — wrap NSLog in closure for iOS sheet
NSLog returns Void which doesn't conform to View in SwiftUI ViewBuilder.
Wrapped in inline closure  to avoid compilation error.
2026-07-17 20:58:22 +08:00
04990674cc fix: save dialog not appearing after recording on macOS/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
2026-07-17 20:27:49 +08:00
0a9ed6e746 feat: network file browser — browse SMB/NFS shares, auto-pair ASS subtitles
- macOS: browse /Volumes (mounted shares), manual SMB/NFS connect
- iOS: system file picker (Files app SMB) + manual URL entry
- Auto-detect matching .ass files when selecting media
- Network button in control toolbar
- loadExternalSubtitle() skeleton for future ASS parsing
2026-07-04 17:33:58 +08:00
1412c234f8 fix(iOS): move pendingExportURL to PlayerBridge — SwiftUI can now observe it and trigger save sheet 2026-07-03 08:29:02 +08:00
58c34250c5 fix(iOS): replace lock overlay with allowsHitTesting — icon now reliably unlocks 2026-07-02 22:24:51 +08:00
d519e9aa17 fix(iOS): fix lock overlay ZStack order — icon now above overlay, unlock shows toolbar 2026-07-02 22:20:26 +08:00
01116180d1 fix: compile errors — frame API + sheet binding for let hlsRecorder 2026-07-02 22:13:26 +08:00
4c38b06860 修复四个问题: 录制后弹保存窗/本地文件播放/左上角锁定/音轨循环切换
1. HLSRecorder: 添加 pendingExportURL,录制完成后通过 SwiftUI sheet 弹出分享面板
2. PlayerBridge: AVAudioSession mode 改为 .default 兼容视频+音频,本地文件加载失败增强日志
3. PlayerContentView: iOS 锁屏功能,30秒无操作自动锁定,左上角图标切换锁状态
4. ControlToolbar: 音轨按钮改为直接循环切换(仅多音轨时可用),添加 cycleTrack() 方法
2026-07-02 22:10:55 +08:00
bf7d3de9ba iOS: PiP画中画、AirPlay投屏、录制保存弹窗、播放记录持久化恢复 2026-07-02 08:16:33 +08:00
44708f39ef feat(iOS): add recording, media library, and local file import
- Wire up HLSRecorder for iOS stream recording (toggleRecording)
- Add MediaLibraryView sheet for iOS (toggleLibraryWindow)
- Add .fileImporter for iOS local file playback (openFileDialog)
- Add Assets.xcassets with app icons
- Add UniformTypeIdentifiers import for iOS
- Add handleFileImport() with security-scoped resource access
2026-07-01 22:55:35 +08:00
6c21bdbef7 fix: iOS .ipa 打包成功 - 修复 Team ID、Bundle.module、@main 入口点
- 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)
2026-07-01 21:38:52 +08:00
d22a5037b8 feat: iOS/macOS 双平台打包支持
- Package.swift: 重构为 MiniPlayerCore(共享库) + MiniPlayer(macOS) + MiniPlayeriOS(iOS) 三 target
- 所有源文件添加 #if os(macOS) / #if os(iOS) 条件编译
- HLSRecorder.swift: iOS 纯 Swift HLS 录制器(下载TS+合并+AVAssetExportSession转MP4)
- scripts/build-macos.sh: macOS 打包脚本 (.app + .dmg)
- scripts/build-ios.sh: iOS 打包脚本 (xcodegen + xcodebuild archive → .ipa)
- .gitignore: 排除构建产物
2026-07-01 08:29:16 +08:00
16fdd72271 fix: 用 NSEvent.addLocalMonitorForEvents 替代 .onHover/tracking area
之前的方案(.onHover 和 .background tracking area)都不可靠:
- .onHover 在 macOS 上行为不稳定
- .background() 里的 NSView 被 AVPlayerLayer 挡住收不到鼠标事件

新方案:用 NSEvent.addLocalMonitorForEvents 在应用层监听 mouseMoved 事件,
直接检测鼠标是否在窗口 frame 内,完全绕过 SwiftUI 的 hover 机制。
加上双层防抖(lastState + currentlyVisible 检查)避免重复设置。
2026-07-01 07:48:16 +08:00
fde83a87bc fix: 用 NSTrackingArea 替代 .onHover 解决标题栏闪烁和红绿灯丢失问题
.onHover 在 macOS 上不可靠,鼠标离开后会误触发 true 导致标题栏短暂回来但没有红绿灯按钮。
改用 NSTrackingArea 的 mouseEntered/mouseExited 做可靠的鼠标追踪。
2026-07-01 07:42:16 +08:00
ec8c3bd51c fix: title bar shows/hides based on window hover, not just top strip
- Remove 28pt overlay hover detection
- Add .onHover to entire window content (ZStack)
- Extract setWindowTitleBarVisible() helper function
- Mouse enters window → show title bar, mouse leaves → hide
2026-06-30 23:54:36 +08:00
5a031a57ea fix: fully hide title bar including traffic lights
- 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
2026-06-30 23:49:32 +08:00
7c1a84c413 feat: title bar hover to show/hide
- Title bar hidden by default (titleVisibility + titlebarAppearsTransparent)
- Mouse enter top 28pt area: show title bar
- Mouse exit: hide title bar
- Decouple title visibility from toolbar state
2026-06-30 23:27:14 +08:00
610f619a35 feat: add player status overlay (loading/buffering/error)
- Add PlayerStatus enum with loading/buffering/playing/error states
- Track loading elapsed time with countdown timer
- Detect buffering via timeControlStatus
- Handle playback errors with error message + retry button
- Show timeout warning after 15s of loading
- Buffering indicator in top-right corner (non-blocking)
- Localization strings for all status messages
2026-06-30 23:10:20 +08:00
05afadfd11 feat: 媒体库 - 播放记忆、多播放列表、M3U导入、模糊搜索、喜欢/标签系统
- MediaLibrary.swift: 数据模型(JSON持久化) + 播放列表管理 + 标签/喜欢
- M3UParser.swift: 解析IPTV格式M3U播放列表(EXTINF属性)
- MediaLibraryView.swift: NavigationSplitView侧边栏+内容区UI
- PlayerBridge.swift: 播放位置记忆(暂停/切歌/退出保存,播放恢复)
- 本地视频自动入库,搜索结果临时播放列表,所有操作不中断播放
- 快捷键: Cmd+Shift+L 媒体库, Cmd+Shift+I 导入M3U
2026-06-30 00:28:14 +08:00
d4a39dd7c3 fix: block app termination while recording is finalizing
- RecorderState.isFinalizing flag set during stopRecording → finishWriting
- applicationShouldTerminate returns .terminateLater if finalizing
- Shows alert '正在保存录制文件...' to inform user
- After save dialog completes, calls NSApp.reply(toApplicationShouldTerminate: true)
- All failure paths also check and resolve pendingTerminate
- Fixes: 9-min recording lost because user closed app before async finishWriting completed
2026-06-25 21:27:02 +08:00
eb3e98d567 fix: window close exits app + save dialog diagnostics
- AppDel: use NSWindow.willCloseNotification observer with visibility check
  instead of broken applicationShouldTerminateAfterLastWindowClosed logic
  (SwiftUI WindowGroup keeps hidden windows that blocked termination)
- finishWriting callback: use DispatchQueue.main.async instead of Task @MainActor
  (more reliable from non-main-thread AVFoundation callback)
- Add extensive NSLog diagnostics: callback invoked, file existence check,
  file size, status codes, nil checks
2026-06-25 21:08:59 +08:00
f5e35a6ee7 fix: close main window exits app; playlist panel close doesn't 2026-06-25 20:47:23 +08:00
be0cf09941 fix: HLS audio (playerItem.tracks + better tap callback) + window title sync with toolbar
- HLS录制音频: 改用playerItem.tracks查找trackID(fallback到asset.loadTracks)
- tap回调: 始终先调GetSourceAudio保证管道畅通,加详细日志
- 窗口标题: onAppear初始隐藏,随toolbar一起30秒后消失/点击图标回来
2026-06-25 09:08:02 +08:00
6e94bb46b7 feat: window title hides/shows with toolbar on idle timeout 2026-06-25 09:00:56 +08:00
87c66c3c70 Recording: red icon, live duration display, save dialog with editable filename 2026-06-24 19:56:07 +08:00
35de7263e9 Fix: app quit on playlist close, request screen capture permission, playlist as panel 2026-06-24 19:50:54 +08:00
5960c5f7c8 Move record button to front of toolbar for visibility 2026-06-24 19:45:36 +08:00
8af1140b06 Add drag-and-drop file/URL playback and screen recording with audio 2026-06-24 19:33:21 +08:00
32bf49fa0d Increase toolbar button spacing from 4 to 10 2026-06-24 19:18:45 +08:00
6d1ff50074 fix: QR scanner crash - use independent NSWindow instead of SwiftUI sheet 2026-06-23 09:14:23 +08:00
ae96218d20 feat: add QR code scanner for media URLs 2026-06-23 09:09:23 +08:00
9cab9dd2cb feat: add URL input button to control toolbar 2026-06-23 09:06:32 +08:00
f303832a1b feat: fullscreen uses SwiftUI with icon+toolbar, double-click toggle, single-click play/pause 2026-06-23 09:02:08 +08:00
4879f56ef1 fix: set window background to black 2026-06-23 08:55:50 +08:00
f1eca64212 fix: compact toolbar with all controls (open/volume/track/playlist)
- Replaced text buttons with SF Symbol icon buttons (28x28)
- All features: folder/prev/play/next/volume+slider/track/repeat/fullscreen/playlist
- Removed Spacer() to prevent buttons from being clipped
- Single compact row layout, no overflow
2026-06-23 08:40:01 +08:00
ad1be14a0c refactor: restore Bricks .ui for video player + native overlay controls
- miniplayer.ui: VideoPlayer 100% fullscreen (Bricks renders video)
- BricksAppView: loads .ui via BricksView, ZStack overlays native controls
- ControlToolbar: semi-transparent (0.6 opacity), slides from bottom
- 30-second auto-hide timer with mouse-hover reset
- Left-top icon toggles toolbar visibility
2026-06-23 08:36:27 +08:00
20f4dec0b9 feat: fullscreen video + click icon to toggle control toolbar
- Video fills entire window via VideoPlayerRepresentable
- MiniPlayerIcon in top-left corner toggles ControlToolbar visibility
- ControlToolbar slides up from bottom with animation
- Removed Bricks JSON UI dependency for main view
- All toolbar features preserved: open/prev/play/next/volume/tracks/repeat/fullscreen/playlist
2026-06-22 20:47:27 +08:00
e7bca7269e feat: add i18n support and app icon in top-left corner
- Added i18n JSON files (zh.json, en.json) with auto language detection
- Updated miniplayer.ui to use i18n keys for all button labels
- Added MiniPlayerIcon overlay in top-left corner of main window
- Load i18n translations on app startup based on system locale
2026-06-22 20:43:13 +08:00
49bc681cf7 chore: rename miniplayer.json to miniplayer.ui 2026-06-22 20:16:32 +08:00
bdf834012a feat: SwiftBricks UI integration with shared AVPlayer
- Inject PlayerBridge AVPlayer into BricksStore for VideoPlayer widget
- Load miniplayer.json from SPM bundle resources
- Fix infinite render loop: pass JSON string instead of schema object
- Fix async closure mismatch in setupBridgeEvents
- Enhanced JSON: URL input, load/play/pause buttons, title bar
- Remove duplicate Resources/miniplayer.json (keep SPM-compliant path)
2026-06-22 09:52:28 +08:00
cae63c6588 fix: persistent fullscreen window — never destroy AVPlayerView
Root cause: every fullscreen exit destroyed the AVPlayerView, triggering
CoreMedia's FigNotificationCenterRemoveWeakListeners which races with
the main thread's autorelease pool drain. No amount of nil-ing the
player reference or delaying the close could prevent this race.

Solution: the fullscreen NSWindow + AVPlayerView are created ONCE on
first use and never destroyed. Toggle uses orderFront/orderOut instead
of makeKeyAndOrderFront/close. The AVPlayerView stays alive for the
entire app lifetime, so CoreMedia teardown never happens during toggle.

Also:
- Removed onDisappear { bridge.cleanup() } — cleanup during app
  termination races with autorelease pool drain. Process exit handles
  all teardown correctly.
- Double-click exit uses DispatchQueue.main.async to avoid calling
  toggleFullscreen from inside the event monitor callback.
- isReleasedWhenClosed = false on fullscreen window
2026-06-22 09:01:04 +08:00
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
aa19ab9799 feat: i18n support, tri-color icon, fix crash
- Add i18n: Localization.swift + zh-Hans/en Localizable.strings
- Add MiniPlayerIcon SwiftUI view (tri-color play button + ring)
- Fix crash: isInteracting/lastInteraction no longer @Published
- Fix crash: ExitFullscreen notification wrapped in DispatchQueue.main.async
- Auto-hide toolbar uses local @State + Timer (not @Published)
- Replace emoji logo with MiniPlayerIcon
- Move icon sets out of Resources/ to avoid SPM conflicts
- Package.swift: add defaultLocalization, process Resources
2026-06-22 00:40:20 +08:00
3e3a990f5e fix: replace Combine KVO with NSKeyValueObservation to prevent autorelease crash
- Remove Combine import and cancellables
- Use NSKeyValueObservation for timeControlStatus (controlled teardown)
- Invalidate itemStatusObserver before replacing playerItem
- Store endObserver token for proper removal
- Add cleanup() method called on view disappear
- Proper deinit with direct property cleanup (nonisolated-safe)
2026-06-22 00:36:18 +08:00
1fa6f6a4bb feat: toolbar auto-hides after 60s of no interaction 2026-06-22 00:12:43 +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
e4ca9bc80a fix: fullscreen crash + video size - use system toggleFullScreen, direct VideoPlayer render 2026-06-21 23:50:54 +08:00
4b94f11664 feat: hidden toolbar with logo toggle, semi-transparent controls, playlist popup window 2026-06-21 23:43:20 +08:00
2a5e3b8ad7 feat: video fills entire window, compact control bar, single-tap play/pause 2026-06-21 23:27:17 +08:00