diff options
Diffstat (limited to 'Sora/Data/Booru')
| -rw-r--r-- | Sora/Data/Booru/BooruManager.swift | 40 | ||||
| -rw-r--r-- | Sora/Data/Booru/BooruNetworkImageLoader.swift | 61 | ||||
| -rw-r--r-- | Sora/Data/Booru/BooruRequestConfiguration.swift | 66 |
3 files changed, 135 insertions, 32 deletions
diff --git a/Sora/Data/Booru/BooruManager.swift b/Sora/Data/Booru/BooruManager.swift index f5fcf89..6333453 100644 --- a/Sora/Data/Booru/BooruManager.swift +++ b/Sora/Data/Booru/BooruManager.swift @@ -62,8 +62,8 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng pageCache.countLimit = 50 pageCache.totalCostLimit = 50 * 1_024 * 1_024 - self.referer = Self.baseReferer(for: provider.domain) - self.userAgent = Self.resolvedUserAgent( + self.referer = BooruRequestConfiguration.baseReferer(for: provider.domain) + self.userAgent = BooruRequestConfiguration.resolvedUserAgent( sendUserAgent: sendUserAgent, customUserAgent: customUserAgent ) @@ -202,30 +202,6 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng return components.url } - private static func resolvedUserAgent( - sendUserAgent: Bool, - customUserAgent: String - ) -> String? { - guard sendUserAgent else { return nil } - - let trimmedCustomUserAgent = customUserAgent.trimmingCharacters( - in: .whitespacesAndNewlines - ) - - guard trimmedCustomUserAgent.isEmpty else { - return trimmedCustomUserAgent - } - - let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0" - let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1" - - return "Sora/\(version) (Build \(buildNumber))" - } - - private static func baseReferer(for domain: String) -> String { - "https://\(domain)/" - } - func clearCachedPages() { pageCache.removeAllObjects() urlCache.removeAll() @@ -599,13 +575,13 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng } func requestURL(_ url: URL) async throws -> Data { - var headers = HTTPHeaders([HTTPHeader(name: "Referer", value: referer)]) - - if let userAgent { - headers.add(name: "User-Agent", value: userAgent) - } + let request = BooruRequestConfiguration.request( + url: url, + referer: referer, + userAgent: userAgent + ) - return try await AF.request(url, headers: headers) + return try await AF.request(request) .validate(statusCode: 200..<300) .serializingData() .value diff --git a/Sora/Data/Booru/BooruNetworkImageLoader.swift b/Sora/Data/Booru/BooruNetworkImageLoader.swift new file mode 100644 index 0000000..c2a0f8a --- /dev/null +++ b/Sora/Data/Booru/BooruNetworkImageLoader.swift @@ -0,0 +1,61 @@ +import CoreGraphics +import Foundation +import ImageIO +import NetworkImage + +actor BooruNetworkImageLoader: NetworkImageLoader { + private let domain: String + private let sendUserAgent: Bool + private let customUserAgent: String + private var ongoingTasks: [URL: Task<CGImage, Error>] = [:] + + init( + domain: String, + sendUserAgent: Bool, + customUserAgent: String + ) { + self.domain = domain + self.sendUserAgent = sendUserAgent + self.customUserAgent = customUserAgent + } + + func image(from url: URL) async throws -> CGImage { + if let task = ongoingTasks[url] { + return try await task.value + } + + let domain = self.domain + let sendUserAgent = self.sendUserAgent + let customUserAgent = self.customUserAgent + + let task = Task<CGImage, Error> { + guard let data = await ImageCacheManager.shared.loadImageData( + for: url, + domain: domain, + sendUserAgent: sendUserAgent, + customUserAgent: customUserAgent + ) else { + throw URLError(.badServerResponse) + } + + guard + let source = CGImageSourceCreateWithData(data as CFData, nil), + let image = CGImageSourceCreateImageAtIndex( + source, + 0, + [kCGImageSourceShouldCache: true] as CFDictionary + ) + else { + throw URLError(.cannotDecodeContentData) + } + + return image + } + + ongoingTasks[url] = task + + defer { ongoingTasks.removeValue(forKey: url) } + + return try await task.value + } +} diff --git a/Sora/Data/Booru/BooruRequestConfiguration.swift b/Sora/Data/Booru/BooruRequestConfiguration.swift new file mode 100644 index 0000000..2b786cf --- /dev/null +++ b/Sora/Data/Booru/BooruRequestConfiguration.swift @@ -0,0 +1,66 @@ +import Foundation + +enum BooruRequestConfiguration { + static func resolvedUserAgent( + sendUserAgent: Bool, + customUserAgent: String + ) -> String? { + guard sendUserAgent else { return nil } + + let trimmedCustomUserAgent = customUserAgent.trimmingCharacters( + in: .whitespacesAndNewlines + ) + + guard trimmedCustomUserAgent.isEmpty else { + return trimmedCustomUserAgent + } + + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0" + let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1" + + return "Sora/\(version) (Build \(buildNumber))" + } + + static func baseReferer(for domain: String) -> String { + "https://\(domain)/" + } + + static func request( + url: URL, + referer: String, + userAgent: String?, + accept: String? = nil + ) -> URLRequest { + var request = URLRequest(url: url) + + request.setValue(referer, forHTTPHeaderField: "Referer") + + if let userAgent { + request.setValue(userAgent, forHTTPHeaderField: "User-Agent") + } + + if let accept { + request.setValue(accept, forHTTPHeaderField: "Accept") + } + + return request + } + + static func request( + url: URL, + domain: String, + sendUserAgent: Bool, + customUserAgent: String, + accept: String? = nil + ) -> URLRequest { + request( + url: url, + referer: baseReferer(for: domain), + userAgent: resolvedUserAgent( + sendUserAgent: sendUserAgent, + customUserAgent: customUserAgent + ), + accept: accept + ) + } +} |