From b9e65f306a2289403d7f903d5a1d0065934488c0 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sat, 4 Jul 2026 18:10:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(iOS):=20copy=20SMB=20files=20to=20local=20D?= =?UTF-8?q?ocuments=20before=20playing=20=E2=80=94=20AVPlayer=20can't=20ha?= =?UTF-8?q?ndle=20smb://=20URLs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/NetworkFileBrowser.swift | 65 +++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/Sources/NetworkFileBrowser.swift b/Sources/NetworkFileBrowser.swift index c25c1bb..24ad0be 100644 --- a/Sources/NetworkFileBrowser.swift +++ b/Sources/NetworkFileBrowser.swift @@ -264,6 +264,8 @@ struct NetworkFileBrowserIOS: View { @State private var isLoading = false @State private var streamURL = "" @State private var showFileImporter = false + @State private var copyingFile = false + @State private var copyProgress = "" var body: some View { NavigationView { @@ -272,6 +274,15 @@ struct NetworkFileBrowserIOS: View { } .navigationTitle(browseURL != nil ? "Browsing" : "Network Files") .navigationBarTitleDisplayMode(.inline) + .overlay { + if copyingFile { + Color.black.opacity(0.6).ignoresSafeArea() + VStack(spacing: 12) { + ProgressView() + Text(copyProgress).foregroundColor(.white) + } + } + } .toolbar { if browseURL != nil { ToolbarItem(placement: .navigationBarLeading) { @@ -418,7 +429,59 @@ struct NetworkFileBrowserIOS: View { private func handleSelect(_ item: NetworkFileItem) { if item.isDirectory { if let cur = browseURL { pathStack.append(cur) }; navigateTo(item.url) } - else if item.isMediaFile { bridge.addItem(url: item.url, name: item.name, type: "network"); if item.hasMatchingASS { bridge.loadExternalSubtitle(url: item.url.deletingPathExtension().appendingPathExtension("ass")) }; isPresented = false } + else if item.isMediaFile { + // SMB 文件需要复制到本地,AVPlayer 不认 smb:// 协议 + if item.url.scheme == "smb" { + copyFromSMBAndPlay(item) + } else { + playMedia(item) + } + } + } + + private func playMedia(_ item: NetworkFileItem) { + bridge.addItem(url: item.url, name: item.name, type: "network") + if item.hasMatchingASS { bridge.loadExternalSubtitle(url: item.url.deletingPathExtension().appendingPathExtension("ass")) } + isPresented = false + } + + private func copyFromSMBAndPlay(_ item: NetworkFileItem) { + copyingFile = true + copyProgress = "Copying \(item.name)..." + + DispatchQueue.global(qos: .userInitiated).async { + let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + let localURL = docs.appendingPathComponent(item.name) + + // 删除旧副本 + try? FileManager.default.removeItem(at: localURL) + + do { + try FileManager.default.copyItem(at: item.url, to: localURL) + + // 同时复制同名 ASS + var assLocal: URL? + if item.hasMatchingASS { + let assURL = item.url.deletingPathExtension().appendingPathExtension("ass") + let assDest = docs.appendingPathComponent(assURL.lastPathComponent) + if (try? FileManager.default.copyItem(at: assURL, to: assDest)) != nil { + assLocal = assDest + } + } + + DispatchQueue.main.async { + self.copyingFile = false + bridge.addItem(url: localURL, name: item.name, type: "network") + if let al = assLocal { bridge.loadExternalSubtitle(url: al) } + isPresented = false + } + } catch { + DispatchQueue.main.async { + self.copyingFile = false + self.connectError = "Copy failed: \(error.localizedDescription)" + } + } + } } private func fmtSize(_ s: Int64) -> String {