123 Commits

Author SHA1 Message Date
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
221075ce8a refactor: replace AVAssetWriter+Tap recording with StreamRecorderKit FFmpegRecorder
- PlayerRecorder.swift: 883 lines → ~150 lines
- Recording now uses FFmpeg to download stream directly from URL
- No longer depends on AVPlayerItemVideoOutput/copyPixelBuffer/MTAudioProcessingTap
- Recording decoupled from playback (pausing playback doesn't affect recording)
- Better quality: direct stream copy/transcode instead of re-encoding captured frames
- Added StreamRecorderKit as local SPM dependency
2026-06-28 18:19:10 +08:00
70337add95 fix: remove audioMix pre-install in playIndex - breaks HLS playback
Setting audioMix with MTAudioProcessingTap on HLS playerItem before
stream is ready blocks the audio pipeline entirely, preventing m3u8
streams from playing at all.

Revert to lazy install in startRecording() only.
2026-06-28 15:21:10 +08:00
f68476d57f fix: move video capture off main thread + filter stale end-of-playback notifications
Two fixes for video freeze during recording:

1. Video capture loop (30fps) moved from main-thread Timer to a
   dedicated DispatchSourceTimer on 'miniplayer.videoCapture' queue.
   copyPixelBuffer() on the main thread was competing with AVPlayer's
   rendering pipeline for CPU time, causing frame stalls.

2. AVPlayerItemDidPlayToEndTime observer now checks notification.object
   against player.currentItem. Previously object:nil caught stale
   notifications from replaced items, causing spurious playNext() calls
   that interrupted playback (visible as duplicate playIndex in logs).
2026-06-28 10:34:50 +08:00
9160b3ce2b fix: pre-install audioMix in playIndex to prevent video freeze on record start
Setting playerItem.audioMix while AVPlayer is actively playing causes
the audio/video pipeline to reconfigure, stalling video playback.

Fix: install the MTAudioProcessingTap audioMix at item creation time
(in playIndex, before replaceCurrentItem), so the pipeline is already
configured when playback begins. startRecording now only flips the
isRecording flag and skips re-installation if audioMix exists.

Also removed the guard that blocked tap installation when tracks
weren't available yet (HLS streams) — the wildcard trackID
(kCMPersistentTrackID_Invalid) handles this case.
2026-06-28 10:30:20 +08:00
cff2bd2793 fix: use kCMPersistentTrackID_Invalid for HLS audio tap
Root cause found: HLS variant switching changes audio trackID mid-recording
(e.g. 2→10). Binding to a specific trackID causes tap to never fire.
Using kCMPersistentTrackID_Invalid (wildcard) matches all audio tracks
regardless of variant switches.
2026-06-28 10:24:04 +08:00
4ba3ccde21 diag: add stop-time tracks check to detect HLS variant switching 2026-06-28 10:14:32 +08:00
1eff22a1ad fix: use playerItem.tracks for HLS audio track discovery
HLS virtual asset returns empty tracks from asset.tracks/loadTracks.
Use playerItem.tracks + assetTrack to get actual track IDs.
2026-06-28 09:52:29 +08:00
1880eeb3d3 fix: use async loadTracks for HLS stream audio tracks - HLS AVURLAsset.tracks returns empty array synchronously - must use asset.loadTracks(withMediaType: .audio) 2026-06-28 09:15:50 +08:00
70b264bcc8 fix: revert to actual trackID for audioMix (kCMPersistentTrackID_Invalid never fires tap) 2026-06-27 17:59:45 +08:00