summaryrefslogtreecommitdiff
path: root/Sora/Data/Danbooru
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-02-27 16:46:41 -0800
committerFuwn <[email protected]>2025-02-27 16:46:41 -0800
commitec2feb28f80d1e4667fe8b0ea489e4d65339934f (patch)
treed1ea7ad4f91ee36cea0f4ff704ec03ca2702f41d /Sora/Data/Danbooru
parentfeat: Development commit (diff)
downloadsora-testing-ec2feb28f80d1e4667fe8b0ea489e4d65339934f.tar.xz
sora-testing-ec2feb28f80d1e4667fe8b0ea489e4d65339934f.zip
feat: Development commit
Diffstat (limited to 'Sora/Data/Danbooru')
-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
4 files changed, 139 insertions, 0 deletions
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
+ }
+}