summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.swiftlint.yml1
-rw-r--r--Sora/Data/Booru/BooruManager.swift25
-rw-r--r--Sora/Data/Booru/BooruProvider.swift1
-rw-r--r--Sora/Data/Booru/BooruProviderFlavor.swift4
-rw-r--r--Sora/Data/Booru/Post/BooruPost.swift2
-rw-r--r--Sora/Data/Booru/Post/BooruPostXMLParser.swift1
-rw-r--r--Sora/Data/Danbooru/DanbooruMediaAsset.swift3
-rw-r--r--Sora/Data/Danbooru/DanbooruMediaAssetVariant.swift7
-rw-r--r--Sora/Data/Danbooru/DanbooruPost.swift77
-rw-r--r--Sora/Data/Danbooru/DanbooruPostParser.swift52
-rw-r--r--Sora/Views/Post/Details/PostDetailsImageView.swift3
11 files changed, 170 insertions, 6 deletions
diff --git a/.swiftlint.yml b/.swiftlint.yml
index 0b28a12..430b4a1 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -24,4 +24,5 @@ disabled_rules:
- trailing_comma
- inert_defer
- unused_capture_list
+ - type_contents_order
diff --git a/Sora/Data/Booru/BooruManager.swift b/Sora/Data/Booru/BooruManager.swift
index 8cf1ac6..0fb0256 100644
--- a/Sora/Data/Booru/BooruManager.swift
+++ b/Sora/Data/Booru/BooruManager.swift
@@ -56,6 +56,9 @@ class BooruManager: ObservableObject {
case .gelbooru:
domain = "gelbooru.com"
+
+ case .danbooru:
+ domain = "danbooru.donmai.us"
}
}
@@ -95,10 +98,16 @@ class BooruManager: ObservableObject {
if Task.isCancelled { return }
DispatchQueue.main.async {
- let newPosts = Array(Set(BooruPostXMLParser(data: data, provider: self.provider).parse()))
- .sorted { lhs, rhs in
- lhs.id > rhs.id
- }
+ let newPosts = Array(
+ Set(
+ self.flavor == .danbooru
+ ? DanbooruPostParser(data: data).parse()
+ : BooruPostXMLParser(data: data, provider: self.provider).parse()
+ )
+ )
+ .sorted { lhs, rhs in
+ lhs.id > rhs.id
+ }
if newPosts.isEmpty {
self.endOfData = true
@@ -166,6 +175,11 @@ class BooruManager: ObservableObject {
let tagString = tags.joined(separator: "+")
switch flavor {
+ case .danbooru:
+ return URL(
+ string: "https://\(domain)/posts.json?page=\(page)&tags=\(tagString)"
+ )
+
case .moebooru:
return URL(string: "https://\(domain)/post.xml?page=\(page)&limit=\(limit)&tags=\(tagString)")
@@ -184,6 +198,9 @@ class BooruManager: ObservableObject {
case .gelbooru:
URL(string: "https://\(domain)/index.php?page=dapi&s=tag&q=index&limit=\(limit)")
+
+ case .danbooru:
+ nil
}
}
diff --git a/Sora/Data/Booru/BooruProvider.swift b/Sora/Data/Booru/BooruProvider.swift
index 3331fd9..5458223 100644
--- a/Sora/Data/Booru/BooruProvider.swift
+++ b/Sora/Data/Booru/BooruProvider.swift
@@ -1,4 +1,5 @@
enum BooruProvider: String, CaseIterable, Decodable, Encodable {
+ case danbooru = "Danbooru"
case gelbooru = "Gelbooru"
case konachan = "Konachan.com"
case safebooru = "Safebooru"
diff --git a/Sora/Data/Booru/BooruProviderFlavor.swift b/Sora/Data/Booru/BooruProviderFlavor.swift
index 69e3d1c..8917ab1 100644
--- a/Sora/Data/Booru/BooruProviderFlavor.swift
+++ b/Sora/Data/Booru/BooruProviderFlavor.swift
@@ -1,9 +1,13 @@
enum BooruProviderFlavor: String, CaseIterable, Decodable, Encodable {
+ case danbooru = "Danbooru"
case gelbooru = "Gelbooru"
case moebooru = "Moebooru"
init(provider: BooruProvider) {
switch provider {
+ case .danbooru:
+ self = .danbooru
+
case .yandere, .konachan, .sakugabooru:
self = .moebooru
diff --git a/Sora/Data/Booru/Post/BooruPost.swift b/Sora/Data/Booru/Post/BooruPost.swift
index 09ba38b..458faa6 100644
--- a/Sora/Data/Booru/Post/BooruPost.swift
+++ b/Sora/Data/Booru/Post/BooruPost.swift
@@ -13,7 +13,7 @@ struct BooruPost: Identifiable, Hashable {
let rating: BooruRating
let tags: [String]
let width: Int
- let change: String
+ let change: String?
let md5: String
let creatorID: String
let authorID: String?
diff --git a/Sora/Data/Booru/Post/BooruPostXMLParser.swift b/Sora/Data/Booru/Post/BooruPostXMLParser.swift
index fe14b7d..4960a79 100644
--- a/Sora/Data/Booru/Post/BooruPostXMLParser.swift
+++ b/Sora/Data/Booru/Post/BooruPostXMLParser.swift
@@ -59,7 +59,6 @@ class BooruPostXMLParser: NSObject, XMLParserDelegate {
let change = attributeDict["change"],
let md5 = attributeDict["md5"],
let creatorId = attributeDict["creator_id"],
- let hasChildrenStr = attributeDict["has_children"],
let createdAt = attributeDict["created_at"],
let status = attributeDict["status"],
let source = attributeDict["source"],
diff --git a/Sora/Data/Danbooru/DanbooruMediaAsset.swift b/Sora/Data/Danbooru/DanbooruMediaAsset.swift
new file mode 100644
index 0000000..3b34c2e
--- /dev/null
+++ b/Sora/Data/Danbooru/DanbooruMediaAsset.swift
@@ -0,0 +1,3 @@
+struct DanbooruMediaAsset: Decodable {
+ let variants: [DanbooruMediaAssetVariant]
+}
diff --git a/Sora/Data/Danbooru/DanbooruMediaAssetVariant.swift b/Sora/Data/Danbooru/DanbooruMediaAssetVariant.swift
new file mode 100644
index 0000000..2151314
--- /dev/null
+++ b/Sora/Data/Danbooru/DanbooruMediaAssetVariant.swift
@@ -0,0 +1,7 @@
+struct DanbooruMediaAssetVariant: Decodable {
+ let type: String
+ let url: String
+ let width: Int
+ let height: Int
+ let file_ext: String // swiftlint:disable:this identifier_name
+}
diff --git a/Sora/Data/Danbooru/DanbooruPost.swift b/Sora/Data/Danbooru/DanbooruPost.swift
new file mode 100644
index 0000000..216816e
--- /dev/null
+++ b/Sora/Data/Danbooru/DanbooruPost.swift
@@ -0,0 +1,77 @@
+import Foundation
+
+struct DanbooruPost: Decodable {
+ let id: Int
+ let createdAt: Date
+ let uploaderId: Int
+ let score: Int
+ let source: String
+ let md5: String
+ let rating: String
+ let imageWidth: Int
+ let imageHeight: Int
+ let tagString: String
+ let parentId: Int?
+ let mediaAsset: DanbooruMediaAsset
+ let fileURL: String
+ let largeFileURL: String
+ let previewFileURL: String
+ let isDeleted: Bool
+
+ enum CodingKeys: String, CodingKey {
+ case id = "id"
+ case createdAt = "created_at"
+ case uploaderId = "uploader_id"
+ case score = "score"
+ case source = "source"
+ case md5 = "md5"
+ case rating = "rating"
+ case imageWidth = "image_width"
+ case imageHeight = "image_height"
+ case tagString = "tag_string"
+ case parentId = "parent_id"
+ case mediaAsset = "media_asset"
+ case fileURL = "file_url"
+ case largeFileURL = "large_file_url"
+ case previewFileURL = "preview_file_url"
+ case isDeleted = "is_deleted"
+ }
+
+ func toBooruPost() -> BooruPost? {
+ guard let fileURL = URL(string: fileURL),
+ let sampleURL = URL(string: largeFileURL),
+ let previewURL = URL(string: previewFileURL)
+ else {
+ return nil
+ }
+
+ let previewVariant = mediaAsset.variants.first { $0.type == "180x180" }
+ let sampleVariant =
+ mediaAsset.variants.first { $0.type == "sample" }
+ ?? mediaAsset.variants.first { $0.type == "original" }
+
+ return BooruPost(
+ id: String(id),
+ height: imageHeight,
+ score: String(score),
+ fileURL: fileURL,
+ parentID: parentId != nil ? String(parentId!) : "",
+ sampleURL: sampleURL,
+ sampleWidth: sampleVariant?.width ?? imageWidth,
+ sampleHeight: sampleVariant?.height ?? imageHeight,
+ previewURL: previewURL,
+ rating: BooruRating(rating),
+ tags: tagString.components(separatedBy: " ").filter { !$0.isEmpty },
+ width: imageWidth,
+ change: nil,
+ md5: md5,
+ creatorID: String(uploaderId),
+ authorID: nil,
+ createdAt: createdAt,
+ status: isDeleted ? "deleted" : "active",
+ source: source,
+ previewWidth: previewVariant?.width ?? 180,
+ previewHeight: previewVariant?.height ?? 180
+ )
+ }
+}
diff --git a/Sora/Data/Danbooru/DanbooruPostParser.swift b/Sora/Data/Danbooru/DanbooruPostParser.swift
new file mode 100644
index 0000000..fa62b5b
--- /dev/null
+++ b/Sora/Data/Danbooru/DanbooruPostParser.swift
@@ -0,0 +1,52 @@
+import Foundation
+
+class DanbooruPostParser {
+ private let data: Data
+
+ init(data: Data) {
+ self.data = data
+ }
+
+ func parse() -> [BooruPost] {
+ let decoder = JSONDecoder()
+
+ decoder.dateDecodingStrategy = .custom { decoder in
+ self.parseDate(
+ try (try decoder.singleValueContainer()).decode(String.self)
+ ) ?? Date()
+ }
+
+ do {
+ return try decoder.decode([DanbooruPost].self, from: data).compactMap { post in
+ post.toBooruPost()
+ }
+ } catch {
+ return []
+ }
+ }
+
+ private func parseDate(_ input: String) -> Date? {
+ let isoFormatter = ISO8601DateFormatter()
+
+ isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
+
+ if let date = isoFormatter.date(from: input) {
+ return date
+ }
+
+ let alternativeFormatter = DateFormatter()
+
+ alternativeFormatter.dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
+ alternativeFormatter.locale = Locale(identifier: "en_US_POSIX")
+
+ if let date = alternativeFormatter.date(from: input) {
+ return date
+ }
+
+ if let timestamp = Double(input) {
+ return Date(timeIntervalSince1970: timestamp)
+ }
+
+ return nil
+ }
+}
diff --git a/Sora/Views/Post/Details/PostDetailsImageView.swift b/Sora/Views/Post/Details/PostDetailsImageView.swift
index a46871c..702d0a0 100644
--- a/Sora/Views/Post/Details/PostDetailsImageView.swift
+++ b/Sora/Views/Post/Details/PostDetailsImageView.swift
@@ -141,6 +141,9 @@ struct PostDetailsImageView<Placeholder: View>: View {
case .gelbooru:
return URL(string: "https://\(manager.domain)/index.php?page=post&s=view&id=\(id)")!
+
+ case .danbooru:
+ return URL(string: "https://\(manager.domain)/posts/\(id)")!
}
}