summaryrefslogtreecommitdiff
path: root/Sora/Data/Booru/BooruManager.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sora/Data/Booru/BooruManager.swift')
-rw-r--r--Sora/Data/Booru/BooruManager.swift105
1 files changed, 75 insertions, 30 deletions
diff --git a/Sora/Data/Booru/BooruManager.swift b/Sora/Data/Booru/BooruManager.swift
index 3c4374e..b70ad5b 100644
--- a/Sora/Data/Booru/BooruManager.swift
+++ b/Sora/Data/Booru/BooruManager.swift
@@ -26,10 +26,12 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
private let pageCache = NSCache<NSString, BooruPageCacheEntry>() // swiftlint:disable:this legacy_objc_type
private let cacheDuration: TimeInterval
private let credentials: BooruProviderCredentials?
- private let userAgent: String
+ private let referer: String
+ private let userAgent: String?
private let showHeldMoebooruPosts: Bool
private var urlCache: [String: URL] = [:]
private var lastPostCount = 0
+ private var cachedMinimumPostID: Int?
// MARK: - Computed Properties
var tags: [String] {
@@ -47,6 +49,8 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
_ provider: BooruProvider,
credentials: BooruProviderCredentials? = nil,
cacheDuration: TimeInterval = BooruPageCacheEntry.defaultExpiration,
+ sendUserAgent: Bool = true,
+ customUserAgent: String = "",
showHeldMoebooruPosts: Bool = false
) {
self.provider = provider
@@ -58,10 +62,11 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
pageCache.countLimit = 50
pageCache.totalCostLimit = 50 * 1_024 * 1_024
- let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0"
- let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1"
-
- self.userAgent = "Sora/\(version) (Build \(buildNumber))"
+ self.referer = BooruRequestConfiguration.baseReferer(for: provider.domain)
+ self.userAgent = BooruRequestConfiguration.resolvedUserAgent(
+ sendUserAgent: sendUserAgent,
+ customUserAgent: customUserAgent
+ )
let rootQuery = BooruSearchQuery(
provider: provider,
@@ -80,7 +85,7 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
let pageValue = flavor == .gelbooru ? page - 1 : page
guard let url = url(forPosts: pageValue, limit: limit, tags: tags) else { return }
- let cacheKey = "\(url.absoluteString.hashValue)_\(replace)" as NSString // swiftlint:disable:this legacy_objc_type
+ let cacheKey = pageCacheKey(for: url, replace: replace)
if let cachedEntry = pageCache.object(forKey: cacheKey),
!cachedEntry.isExpired
@@ -303,7 +308,16 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
guard !Task.isCancelled else { return [] }
- return BooruTagXMLParser(data: data).parse().sorted { $0.count > $1.count }
+ let parsedTags =
+ if flavor == .danbooru {
+ DanbooruTagParser(data: data).parse()
+ } else if flavor == .gelbooru, provider != .safebooru {
+ GelbooruAutocompleteTagParser(data: data).parse()
+ } else {
+ BooruTagXMLParser(data: data).parse()
+ }
+
+ return parsedTags.sorted { $0.count > $1.count }
} catch {
if (error as? URLError)?.code != .cancelled {
debugPrint("BooruManager.searchTags: \(error)")
@@ -340,9 +354,7 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
guard page > 1 else { return "1" }
guard !hasExplicitSortTag(in: tags) else { return String(page) }
- guard let minimumPostID = posts.lazy.compactMap({ Int($0.id) }).min() else {
- return String(page)
- }
+ guard let minimumPostID = cachedMinimumPostID else { return String(page) }
return "b\(minimumPostID)"
}
@@ -351,7 +363,11 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
let tagString = tags.joined(separator: "+")
let pageCacheValue =
flavor == .danbooru ? danbooruPageToken(for: page, tags: tags) : String(page)
- let cacheKey = "posts_\(pageCacheValue)_\(limit)_\(tagString.hashValue)"
+ let cacheKey = postsURLCacheKey(
+ page: pageCacheValue,
+ limit: limit,
+ tagString: tagString
+ )
if let cachedURL = urlCache[cacheKey] {
return cachedURL
@@ -434,6 +450,16 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
return url
}
+ // swiftlint:disable:next legacy_objc_type
+ private func pageCacheKey(for url: URL, replace: Bool) -> NSString {
+ // swiftlint:disable:next legacy_objc_type
+ "posts|\(url.absoluteString)|replace:\(replace)" as NSString
+ }
+
+ private func postsURLCacheKey(page: String, limit: Int, tagString: String) -> String {
+ "posts|\(page)|limit:\(limit)|tags:\(tagString)"
+ }
+
private func url(forTags limit: Int, order: String = "count") -> URL? {
switch flavor {
case .moebooru:
@@ -479,7 +505,7 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
components.host = domain
components.path = "/tag.xml"
components.queryItems = [
- URLQueryItem(name: "name_pattern", value: name),
+ URLQueryItem(name: "name", value: name),
URLQueryItem(name: "order", value: "count"),
]
@@ -492,28 +518,36 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
components.host = domain
components.path = "/index.php"
- var queryItems = [
- URLQueryItem(name: "page", value: "dapi"),
- URLQueryItem(name: "s", value: "tag"),
- URLQueryItem(name: "q", value: "index"),
- URLQueryItem(name: "name_pattern", value: "%\(name)%"),
- URLQueryItem(name: "orderby", value: "count"),
- ]
-
- if let validCredentials = credentials,
- !validCredentials.apiKey.isEmpty,
- validCredentials.userID != 0
- {
- queryItems.append(URLQueryItem(name: "api_key", value: validCredentials.apiKey))
- queryItems.append(URLQueryItem(name: "user_id", value: String(validCredentials.userID)))
+ if provider == .safebooru {
+ components.queryItems = [
+ URLQueryItem(name: "page", value: "dapi"),
+ URLQueryItem(name: "s", value: "tag"),
+ URLQueryItem(name: "q", value: "index"),
+ URLQueryItem(name: "name_pattern", value: "%\(name)%"),
+ URLQueryItem(name: "orderby", value: "count"),
+ ]
+ } else {
+ components.queryItems = [
+ URLQueryItem(name: "page", value: "autocomplete2"),
+ URLQueryItem(name: "type", value: "tag_query"),
+ URLQueryItem(name: "term", value: name),
+ ]
}
- components.queryItems = queryItems
-
return components.url
case .danbooru:
- return nil
+ var components = URLComponents()
+
+ components.scheme = "https"
+ components.host = domain
+ components.path = "/tags.json"
+ components.queryItems = [
+ URLQueryItem(name: "search[name_matches]", value: "\(name)*"),
+ URLQueryItem(name: "limit", value: "50"),
+ ]
+
+ return components.url
}
}
@@ -545,12 +579,17 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
postIndexMap.removeAll()
lastPostCount = 0
+ cachedMinimumPostID = nil
}
endOfData = newPosts.isEmpty
guard !endOfData else { return }
+ if let nextMinimumPostID = newPosts.lazy.compactMap({ Int($0.id) }).min() {
+ cachedMinimumPostID = min(cachedMinimumPostID ?? nextMinimumPostID, nextMinimumPostID)
+ }
+
withTransaction(Transaction(animation: nil)) {
let oldCount = self.posts.count
@@ -567,7 +606,13 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
}
func requestURL(_ url: URL) async throws -> Data {
- try await AF.request(url, headers: ["User-Agent": userAgent])
+ let request = BooruRequestConfiguration.request(
+ url: url,
+ referer: referer,
+ userAgent: userAgent
+ )
+
+ return try await AF.request(request)
.validate(statusCode: 200..<300)
.serializingData()
.value