diff --git a/Sources/Firelink/AddDownloadsView.swift b/Sources/Firelink/AddDownloadsView.swift index 8fc2a52..9033f93 100644 --- a/Sources/Firelink/AddDownloadsView.swift +++ b/Sources/Firelink/AddDownloadsView.swift @@ -83,7 +83,7 @@ struct AddDownloadsView: View { .foregroundStyle(.secondary) Spacer() Button { - refreshMetadata(for: linkText) + refreshMetadata(for: linkText, isAutoFetch: false) } label: { Label("Refresh Metadata", systemImage: "arrow.clockwise") } @@ -422,7 +422,7 @@ struct AddDownloadsView: View { try? await Task.sleep(for: .milliseconds(350)) guard !Task.isCancelled else { return } await MainActor.run { - refreshMetadata(for: text) + refreshMetadata(for: text, isAutoFetch: true) } } } @@ -445,7 +445,7 @@ struct AddDownloadsView: View { controller.pendingReferer = nil } - private func refreshMetadata(for text: String) { + private func refreshMetadata(for text: String, isAutoFetch: Bool) { let urls = DownloadURLParser.parse(text) metadataTask?.cancel() @@ -476,7 +476,8 @@ struct AddDownloadsView: View { for: url, settings: settings, credentials: metadataCredentials(for: url), - transferOptions: transferOptions + transferOptions: transferOptions, + isAutoFetch: isAutoFetch ) loaded.append(item) await MainActor.run { diff --git a/Sources/Firelink/DownloadMetadataFetcher.swift b/Sources/Firelink/DownloadMetadataFetcher.swift index bccd326..d06365a 100644 --- a/Sources/Firelink/DownloadMetadataFetcher.swift +++ b/Sources/Firelink/DownloadMetadataFetcher.swift @@ -35,7 +35,8 @@ enum DownloadMetadataFetcher { for url: URL, settings: AppSettings, credentials: DownloadCredentials? = nil, - transferOptions: DownloadTransferOptions = DownloadTransferOptions() + transferOptions: DownloadTransferOptions = DownloadTransferOptions(), + isAutoFetch: Bool = false ) async -> PendingDownload { let initialName = FileClassifier.fileName(from: url) let initialCategory = FileClassifier.category(forFileName: initialName) @@ -53,6 +54,11 @@ enum DownloadMetadataFetcher { return pending } + if isAutoFetch, let host = url.host, isPrivateHost(host) { + pending.state = .loaded + return pending + } + var request = URLRequest(url: url) request.httpMethod = "HEAD" request.timeoutInterval = 12 @@ -122,6 +128,32 @@ enum DownloadMetadataFetcher { } return nil } + + private static func isPrivateHost(_ host: String) -> Bool { + let h = host.lowercased() + if h == "localhost" || h.hasSuffix(".local") { return true } + if !h.contains(".") && !h.contains(":") { return true } + + let parts = h.split(separator: ".") + if parts.count == 4, let first = Int(parts[0]), let second = Int(parts[1]) { + if first == 127 || first == 10 || (first == 192 && second == 168) { + return true + } + if first == 172 && (16...31).contains(second) { + return true + } + if first == 169 && second == 254 { + return true + } + } + + if h.contains(":") { + if h == "[::1]" || h.hasPrefix("[fc") || h.hasPrefix("[fd") || h.hasPrefix("[fe8") || h.hasPrefix("[fe9") || h.hasPrefix("[fea") || h.hasPrefix("[feb") { + return true + } + } + return false + } } enum ByteFormatter {