132 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
01bf7d6f4a fix(iOS): Connect+Browse buttons — Connect opens Files for auth, Browse opens file picker. Add auto-opens Files. 2026-07-04 20:06:30 +08:00
6c7a6e567f fix(iOS): simplified — server list + system file picker. FileManager can't browse SMB directly on iOS, use .fileImporter instead. 2026-07-04 20:02:16 +08:00
3cb5b1d8a4 fix(iOS): connect flow — new SMB opens Files for auth, saved servers use FileManager directly. Both flows save to history. 2026-07-04 19:57:33 +08:00
6a605451e4 fix: use browseURL scheme to detect SMB context — iOS maps SMB files to file:// URLs 2026-07-04 19:55:07 +08:00
80e332bda0 feat: iOS — saved server list, built-in file browser, HTTP stream server
- LocalStreamServer: NWListener-based HTTP proxy, streams SMB via localhost
- Saved server list with delete, one-tap reconnect
- Removed file importer — all browsing in MiniPlayer
- Removed SMBStreamingLoader (custom scheme approach didn't work)
2026-07-04 19:51:42 +08:00
b1a04526a2 feat: SMB streaming via AVAssetResourceLoaderDelegate — no file copy, reads on-demand.
- SMBStreamingLoader: wraps smb:// as miniplayer-smb:// custom scheme
- PlayerBridge.playSMBSource(): creates AVURLAsset with resource loader
- iOS NetworkFileBrowser: tap SMB media → stream directly
- Removed old FileManager.copyItem approach
2026-07-04 18:24:10 +08:00
b9e65f306a fix(iOS): copy SMB files to local Documents before playing — AVPlayer can't handle smb:// URLs 2026-07-04 18:10:31 +08:00
bf734454f8 fix(iOS): SMB connect flow — use FileManager first, fallback to Files app. Extract shared buildFileItems helper. 2026-07-04 18:06:50 +08:00
592a23b02e fix(iOS): SMB URL detection — show alert guiding to Files app instead of trying to play. Add import UIKit. 2026-07-04 18:00:44 +08:00
d01baed1c9 test: SMB network share verified — mount /Volumes/sambashare, browse media dirs. Fix NSWorkspace API + MediaLibraryView platform guard 2026-07-04 17:39:13 +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
3ed5696ae0 fix(iOS): read file data into memory BEFORE revoking security-scoped access, prevents exists=false 2026-07-03 08:22:14 +08:00
6503712be9 fix: show file diagnostics (exists, size, magic bytes) directly on error screen 2026-07-03 08:19:53 +08:00
0e172e015e fix: add file magic number verification to diagnose corruption 2026-07-03 08:18:33 +08:00
beaeb3b4c6 fix: ensure AVAudioSession is active right before player.play(), add diagnostic logging 2026-07-03 08:17:32 +08:00
15b26ce416 fix: show full error details (code+domain+filename) on playback failure 2026-07-03 08:14:10 +08:00
e81af97437 fix: add device diagnostic log (miniplayer_diag.log in Documents) to trace playback issues 2026-07-02 22:58:57 +08:00
37d7338823 fix: change audio mode back to .default — .moviePlayback breaks all playback on iOS 26 2026-07-02 22:41:07 +08:00
6f786cd1a8 fix: revert to .moviePlayback audio mode for proper video A/V sync 2026-07-02 22:36:01 +08:00
0388ae2a45 fix: add diagnostics for local file playback — file existence, audio route, player rate 2026-07-02 22:31:16 +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
a74a021518 fix(iOS): add CFBundleIcons to Info.plist so app icon loads from Assets.car 2026-07-02 21:57:33 +08:00
75a319d7fb fix(iOS): fix presentActivitySheet keyWindow deprecation, use windowScene.keyWindow + fallback 2026-07-02 21:54:21 +08:00
bfb7631714 fix(iOS): improve PiP KVO observation, fix recording stop flow and export sheet
- PiPController: add KVO on isPictureInPicturePossible, avoid duplicate setup, better logging
- HLSRecorder: add stopRequested check after Task.sleep to ensure recording exits cleanly
- HLSRecorder: add 120s timeout to remuxToMP4 via TaskGroup to prevent hanging
- HLSRecorder: improve presentActivitySheet VC lookup (key window, foregroundActive scene)
- Add detailed logging throughout recording/export flow for debugging
2026-07-02 21:13:24 +08:00
bf7d3de9ba iOS: PiP画中画、AirPlay投屏、录制保存弹窗、播放记录持久化恢复 2026-07-02 08:16:33 +08:00
ce5878957e fix(iOS): fix recording, playback compatibility, and media library UI
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
2026-07-02 07:44:09 +08:00
88f1ecbe9c fix(iOS): rewrite HLSRecorder for continuous recording until user stops
- 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()
2026-07-01 23:18:39 +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
4bfd4e5565 fix: .ipa 复制到项目根目录,Xcode Add App 弹窗可见 2026-07-01 22:05:33 +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
103b994c60 debug: 添加录制流程日志 2026-07-01 08:01:45 +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
03b8ef8d40 feat: URL deduplication for media library and queue
- On load: merge duplicate URLs (keep first, merge tags/liked/position)
- addItem: check queue for existing URL, show toast if duplicate
- addItemNoPlay: skip if URL already in queue
- Add hasItem(url:) helper
- Playlist references updated when merging duplicates
2026-06-30 23:45:02 +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
b1937bfc16 fix: 修复新增availability字段导致JSON解码失败
自定义init(from decoder:),为新增字段提供默认值:
- availability默认.unknown
- 兼容旧版JSON数据,避免解码失败清空媒体库
2026-06-30 22:43:11 +08:00
af710e4e4f feat: 流媒体URL并发可用性检测
- 后台并发检测所有远程流URL(最多10并发)
- 5秒超时,403标记forbidden,连接失败标记unavailable
- 默认过滤不可用频道,眼睛图标切换显示
- 进度条+状态文本实时显示检测进度
- 行内状态图标:绿✓可用/红🔒无权限/灰✗不可用
- 支持取消检测
2026-06-30 22:41:04 +08:00
48011dd4f0 fix: 修复M3U导入后UI冻结问题
- save()改为async+debounce(500ms),不阻塞主线程
- URL查找从O(n)线性扫描改为O(1)索引
- 播放位置保存增加节流(30秒/5秒差值)
- 列表分页渲染(先100条,滚动加载)
- 退出时强制同步保存位置
- 直播流跳过seek恢复
2026-06-30 08:49:18 +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
49948049c0 fix: show save dialog even on error if file has content; better default filename 2026-06-28 21:46:41 +08:00