From 44708f39ef2fce342f1c5a1561bcc6937bb20303 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 1 Jul 2026 22:55:35 +0800 Subject: [PATCH] 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 --- .../AppIcon.appiconset/Contents.json | 74 ++++++++++++++++++ .../AppIcon.appiconset/icon_1024.png | Bin 0 -> 15906 bytes .../AppIcon.appiconset/icon_120.png | Bin 0 -> 1406 bytes .../AppIcon.appiconset/icon_152.png | Bin 0 -> 1717 bytes .../AppIcon.appiconset/icon_167.png | Bin 0 -> 1849 bytes .../AppIcon.appiconset/icon_180.png | Bin 0 -> 2049 bytes .../AppIcon.appiconset/icon_40.png | Bin 0 -> 503 bytes .../AppIcon.appiconset/icon_58.png | Bin 0 -> 683 bytes .../AppIcon.appiconset/icon_60.png | Bin 0 -> 696 bytes .../AppIcon.appiconset/icon_80.png | Bin 0 -> 903 bytes .../AppIcon.appiconset/icon_87.png | Bin 0 -> 1039 bytes Assets.xcassets/Contents.json | 6 ++ Sources/MiniPlayerApp.swift | 39 ++++++++- Sources/PlayerBridge.swift | 44 +++++++++++ 14 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_1024.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_120.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_152.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_167.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_180.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_40.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_58.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_60.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_80.png create mode 100644 Assets.xcassets/AppIcon.appiconset/icon_87.png create mode 100644 Assets.xcassets/Contents.json diff --git a/Assets.xcassets/AppIcon.appiconset/Contents.json b/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..7b3bbd2 --- /dev/null +++ b/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,74 @@ +{ + "images": [ + { + "filename": "icon_40.png", + "idiom": "iphone", + "scale": "2x", + "size": "20x20" + }, + { + "filename": "icon_60.png", + "idiom": "iphone", + "scale": "3x", + "size": "20x20" + }, + { + "filename": "icon_58.png", + "idiom": "iphone", + "scale": "2x", + "size": "29x29" + }, + { + "filename": "icon_87.png", + "idiom": "iphone", + "scale": "3x", + "size": "29x29" + }, + { + "filename": "icon_80.png", + "idiom": "iphone", + "scale": "2x", + "size": "40x40" + }, + { + "filename": "icon_120.png", + "idiom": "iphone", + "scale": "3x", + "size": "40x40" + }, + { + "filename": "icon_120.png", + "idiom": "iphone", + "scale": "2x", + "size": "60x60" + }, + { + "filename": "icon_180.png", + "idiom": "iphone", + "scale": "3x", + "size": "60x60" + }, + { + "filename": "icon_152.png", + "idiom": "ipad", + "scale": "2x", + "size": "76x76" + }, + { + "filename": "icon_167.png", + "idiom": "ipad", + "scale": "2x", + "size": "83.5x83.5" + }, + { + "filename": "icon_1024.png", + "idiom": "ios-marketing", + "scale": "1x", + "size": "1024x1024" + } + ], + "info": { + "author": "MiniPlayer", + "version": 1 + } +} \ No newline at end of file diff --git a/Assets.xcassets/AppIcon.appiconset/icon_1024.png b/Assets.xcassets/AppIcon.appiconset/icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..0285f35a572db7ab48809cbea7a8730cbaf5a7c6 GIT binary patch literal 15906 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 literal 0 HcmV?d00001 diff --git a/Assets.xcassets/AppIcon.appiconset/icon_120.png b/Assets.xcassets/AppIcon.appiconset/icon_120.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5f9a8ea1e436265b4d879577f0c7f8722e654d GIT binary patch literal 1406 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 LNkvXXu0mjf@j)>% literal 0 HcmV?d00001 diff --git a/Assets.xcassets/AppIcon.appiconset/icon_167.png b/Assets.xcassets/AppIcon.appiconset/icon_167.png new file mode 100644 index 0000000000000000000000000000000000000000..8d4d35e9509c41ef231056fb172de110f7d11efe GIT binary patch literal 1849 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 literal 0 HcmV?d00001 diff --git a/Assets.xcassets/AppIcon.appiconset/icon_40.png b/Assets.xcassets/AppIcon.appiconset/icon_40.png new file mode 100644 index 0000000000000000000000000000000000000000..d22550ea5f6d8108621ae0ee901223c7a9c51a12 GIT binary patch literal 503 zcmV>6$P~tI1DNmb_i)X za8fV{Xbu>nI=4g|E5FB^m_kx^*AUi;E zigV#Gw;+LlG52!dQjn=N=`IN;fh46^6ONm1hq;Hrd0+305@gIp_9ltRR|{5cYQmFt=xi-}RZN$Z1IgnRITYCWV^jntI zN(^}WusItHTY8}O#q93hH{j%9eK7NIPzg*Gu#LEv3l`lZT0uiboN?h!?&9U^Jfv1FEwlzXvrnN+ALspg7Wyd5=VnvgyM2YuCm4o(H>8E~Sr zI0w=T&SdoBo&pNQ-8+o&@${v4ai@ZE)j?7&O=lN(s6RJFtt*Le>_Xi`O5F^CKPl=D t4z)yJB~W00@Gn}w><;VPdemm_`T=8-*d%pR!qWf%002ovPDHLkV1nb`>4yLS literal 0 HcmV?d00001 diff --git a/Assets.xcassets/AppIcon.appiconset/icon_58.png b/Assets.xcassets/AppIcon.appiconset/icon_58.png new file mode 100644 index 0000000000000000000000000000000000000000..f44e57eb6066d40a724140c6d4b141cd62a8fb51 GIT binary patch literal 683 zcmV;c0#yBpP)q$by`wBUz$!YTPR#wZ>%h8?0~+&cuJ7AxK%7j{jd(YZn~Ut zryg`xNkWFg#e&ucuu1|_8c-5g?_iBJS_)*RW_H0ILxs9Y1vI(hGfw+@wSQl)e@OH8 zt_Fen1Q(ia$73HthQeySQ5823=>?>}-ZoE{%2ZfIanv@s)V4sWPhVKAH_Cf4$^q$u zp)68ZGNdFdaso&J$yselpe8p{Lb_mmT*a|Ka$%7=QBFus8e`7ZPFjt{RgCFo)2vre zh^q(@rJi8k!AdmcT%m8;a%;1Ll?EPiwK2>Q)zRKnc>CI?a4vT-$D}dbEdo>>xtaqQ zOV~R(z`m<5LL!-5JycA05eieRbgm$wI#*kVtG$}tYhBOg(Lk^77JvTUE+wV{XrEzq z^Re&>e0%>6qjfX~AgM+tFSiie%?>aG(zl)~NFW=p#F`I2p6N&zeJj1+w*%LOX|NlslCtR%2&yy(BwZyoz9 z7ue_ekcUhv^!N=GhO>HqK7O;0-&7E&K5$=PZd!{y@z##>t~pyp4y4|3iQK(V-KCBz zArTcUlMRG`UOHBJW2CA Rg<}8!002ovPDHLkV1jegEtLQO literal 0 HcmV?d00001 diff --git a/Assets.xcassets/AppIcon.appiconset/icon_60.png b/Assets.xcassets/AppIcon.appiconset/icon_60.png new file mode 100644 index 0000000000000000000000000000000000000000..ca052327ebf98ae838799b87be34abea8b1750f2 GIT binary patch literal 696 zcmV;p0!RIcP)*|9E-SF@VN^tK6{qVHb6LwOOTh>4a7} zU}GV3!Rrxd#R*tj-R9FL%G>b`;faC~T}1 zTe}C~USgqjYncQb0-Lz*aypXS?N-@r?D-e(7FYVn!_$wZqOl+4cxpR zV;ZLn+m8#%cNRngmxkqp#*_g_!gfiyBn1K6?3RG_L5EmIN{1z3Q%&Pkc2ZpNS;pLU z>w33^30X!7yXISuxpZ*!$6~l5%w6>axA69Lu$%)r)-r-{q-_TQkDbmbD|6p+7f$Fg z%eM9r3Z{f@lTdn- zz5Rx#!{!;b*B|R3fc^bD%-(P*&?0j<#BNU5Ng2)!Dh_P3D+TI2rN;(w{&EAHF@12s zj$OiRcTorh>`GfD1E*T%v`XEepzQ;$ff>7I;~;6(^-us~8;iBfxl7{MexmEcl5;KB zBOjzR%DrMs-dv#X0Gs8K&^FkR2YQ&&aS7m#b1wh)Rm3CZpN{r74|B6U eS3#!$WBvfSj7U|`7G}@@0000aInb!_8ZBT*Q3;)J$v@_8x8reb8jd0O5*+BA{M%5Xc#b!%196Ja?ew9 zgY%y+pwGLfqvEttbHMXJXtiP9f~af3^F+{U!Q6s)hdPP?aLaM2OI%ag=b6B*W%~Y3 z9WMV!EHpj9n=>5WC}`HWpqH>OfLB^(xns=`F-(k>aEXCF^JLH``IYgn_N}E90eBR1qG|E|mO42Wn%?Zgw$MQB za}B~vftYF`*>zxQhT(En4F?3!A=sxM)oF%^v0ukg%0|-SF)%@d98a-C2pkkp1q{_8A=g;S=5*90$N%Qg|dk?Wp-}PaF18gM)5!i7H{qrHZ}^W0vu>u&*{e$n9SgV9k>mnaj0gml|{( zXb~)&?VImexxhg;AJ{LX4;60sDU$^7mK~PilBR}zsSlY@473eG#M(DZ1}JHAr%uiz zPP94DHe?~@u5}i`n1!|hh%tD%57@H}Y#HK!46nF^TZA|ez$3j5w1OZ~KB)t}t!Wb> zNx#d;wGZuW%`p)ShmDl4yQ05vZ8HE-+ZZ0puP=Y(zi?e@88*nR@wXsC%)1E?+O@+L zlxr!>l(Ciop(PvbnSjyu#0px0Lx>c;V@NJzMuGRuR4-kVbuN-(Bj%U6*14!!^D$c) zBz6_BH6L3$Au5<0QwK}`z%WplHJ}yu5A3b=CrJm(UP1cAMzp3ztKim>bDn!2>CWWX dvuBSA^B;lItv+7Sr9%J!002ovPDHLkV1mukq+S33 literal 0 HcmV?d00001 diff --git a/Assets.xcassets/AppIcon.appiconset/icon_87.png b/Assets.xcassets/AppIcon.appiconset/icon_87.png new file mode 100644 index 0000000000000000000000000000000000000000..07af9707ad7a421d13da4cebda3c5aba08e35aee GIT binary patch literal 1039 zcmV+q1n~QbP) zJ&xNj5QRy>CrG7&j?WMvsd5A(WtuJa6zR6kMYc`t0|ZEb04Y+YcF2O&UO|BXYe5jA z#GjerP}XbKuy-x$^YhbC;*NyawQJX|T~v~#Z=lrML2OvY{L5yP^@m#r3I#gpm+uv7 zE@a6ata-c!UsKN~!5OgR0@f@*OcfRMHNdhaSOW;O7U(6wk{YZb07?xy1XxmmH5H*! zK%N8Kfp3L5SjzE8OxGG_P$v!F-@QIQUSH7MdjNZ9Ah4?7E1R=TeR=~57}dEsJE$-+ zFfb0+wZ2Lr`_7d$!774!)F0ClTxh^xa8tml3Nx#5d~0AK0E5Gg1*>SMTaR_87V9-f~3(?#Fa|7!_^}#}y zWF4eyUB@b>P<>56Lk=!+Z`J{A?Euya*c6Ddt_D4z?FuUy?iO7e zVxe`g7!^a;`sqVzrMMTwxm$59w0l`q3tC?A9CPWE8mA<(CWqriNS+4J@4- zEAB344OcF>w}q%+4L24rSI8arN+DI0kk_`u-nl96b;ZHOO~B1n?WjOoh=<|cA3Ip3u`r68Q&AT`FS!&7gx1#!^~seS*Dgf@+r`gECSaii z0vEe1qZZUT;O?F>korU7J)5X*uGI?8VR}`QrfF`%qroM$i4+xslq1g@LIoy1sC%B|v?N$bp z_Pd*;AN1oJ2mx0REerhiF|O{_#Rl`nNB0QY?BCd3yLJgM{Rd~d6AQLSjy?bY002ov JPDHLkV1iAC;v4_~ literal 0 HcmV?d00001 diff --git a/Assets.xcassets/Contents.json b/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/MiniPlayerApp.swift b/Sources/MiniPlayerApp.swift index 4e8a2cb..ea038fc 100644 --- a/Sources/MiniPlayerApp.swift +++ b/Sources/MiniPlayerApp.swift @@ -6,6 +6,7 @@ import SwiftBricks import AppKit #elseif os(iOS) import UIKit +import UniformTypeIdentifiers #endif #if os(macOS) @@ -456,6 +457,23 @@ struct BricksAppView: View { .sheet(isPresented: $bridge.showPlaylistSheet) { PlaylistWindowView(bridge: bridge) } + .sheet(isPresented: $bridge.showLibrarySheet) { + MediaLibraryView(library: bridge.library, bridge: bridge) + } + #if os(iOS) + .fileImporter( + isPresented: $bridge.showFileImporter, + allowedContentTypes: [.movie, .video, .audio, .mpeg4Movie, .quickTimeMovie, .mp3, .wav, .mpeg4Audio, .plainText, .item], + allowsMultipleSelection: true + ) { result in + switch result { + case .success(let urls): + bridge.handleFileImport(urls) + case .failure(let error): + bridge.showToastMsg(error.localizedDescription) + } + } + #endif } } @@ -486,8 +504,8 @@ struct ControlToolbar: View { // 按钮行 HStack(spacing: 10) { + // 录制 #if os(macOS) - // 录制(放最前面,更显眼) if bridge.screenRecorder.isRecording { HStack(spacing: 4) { Image(systemName: "stop.circle.fill") @@ -504,9 +522,26 @@ struct ControlToolbar: View { bridge.toggleRecording(); onTouch() } } + #elseif os(iOS) + if bridge.hlsRecorder.isRecording { + HStack(spacing: 4) { + Image(systemName: "stop.circle.fill") + .foregroundColor(.red) + .font(.system(size: 18)) + Text(bridge.hlsRecorder.durationText) + .font(.system(size: 12, design: .monospaced)) + .foregroundColor(.red) + } + .contentShape(Rectangle()) + .onTapGesture { bridge.toggleRecording(); onTouch() } + } else { + TB(icon: "record.circle") { + bridge.toggleRecording(); onTouch() + } + } + #endif Divider().frame(height: 20).padding(.horizontal, 4) - #endif TB(icon: "folder") { bridge.openFileDialog(); onTouch() } TB(icon: "link") { bridge.showURLDialog = true; onTouch() } diff --git a/Sources/PlayerBridge.swift b/Sources/PlayerBridge.swift index 65bb5fa..3e81f28 100644 --- a/Sources/PlayerBridge.swift +++ b/Sources/PlayerBridge.swift @@ -73,6 +73,8 @@ final class PlayerBridge: ObservableObject { @Published var showURLDialog = false @Published var showTrackDialog = false @Published var showQRScanner = false + @Published var showLibrarySheet = false + @Published var showFileImporter = false #if os(macOS) var qrScannerController: QRScannerWindowController? #endif @@ -115,6 +117,10 @@ final class PlayerBridge: ObservableObject { let screenRecorder = PlayerRecorder() #endif + #if os(iOS) + let hlsRecorder = HLSRecorder() + #endif + // MARK: - 初始化 func setup() { #if os(macOS) @@ -135,6 +141,15 @@ final class PlayerBridge: ObservableObject { self?.showToast(msg) } #endif + + #if os(iOS) + hlsRecorder.onRecordingSaved = { [weak self] url in + self?.showToast("Saved: \(url.lastPathComponent)") + } + hlsRecorder.onError = { [weak self] msg in + self?.showToast(msg) + } + #endif } // MARK: - 录制 @@ -150,6 +165,17 @@ final class PlayerBridge: ObservableObject { let item = queue[currentIndex] screenRecorder.startRecording(url: item.url) } + #elseif os(iOS) + if hlsRecorder.isRecording { + hlsRecorder.stopRecording() + } else { + guard !queue.isEmpty, currentIndex >= 0, currentIndex < queue.count else { + showToast("No media playing") + return + } + let item = queue[currentIndex] + hlsRecorder.startRecording(url: item.url) + } #endif } @@ -482,6 +508,8 @@ final class PlayerBridge: ObservableObject { addItem(url: url, name: url.lastPathComponent, type: "file") } } + #elseif os(iOS) + showFileImporter = true #endif } @@ -645,9 +673,23 @@ final class PlayerBridge: ObservableObject { } showToastMsg("\(L.imported) \(count) \(L.localFilesAdded)") } + #elseif os(iOS) + showFileImporter = true #endif } + /// 处理 iOS 文件选择器结果 + func handleFileImport(_ urls: [URL]) { + for url in urls { + // iOS: 需要 startAccessingSecurityScopedResource 获取沙盒文件访问权限 + let didStartAccessing = url.startAccessingSecurityScopedResource() + defer { + if didStartAccessing { url.stopAccessingSecurityScopedResource() } + } + addItem(url: url, name: url.lastPathComponent, type: "file") + } + } + // MARK: - 媒体库窗口 func toggleLibraryWindow() { @@ -668,6 +710,8 @@ final class PlayerBridge: ObservableObject { panel.contentView = NSHostingView(rootView: MediaLibraryView(library: library, bridge: self)) panel.makeKeyAndOrderFront(NSApp) libraryWindow = panel + #elseif os(iOS) + showLibrarySheet.toggle() #endif }