summaryrefslogtreecommitdiff
path: root/Sora/Data/Booru
diff options
context:
space:
mode:
Diffstat (limited to 'Sora/Data/Booru')
-rw-r--r--Sora/Data/Booru/BooruManager.swift40
-rw-r--r--Sora/Data/Booru/BooruNetworkImageLoader.swift61
-rw-r--r--Sora/Data/Booru/BooruRequestConfiguration.swift66
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
+ )
+ }
+}