diff options
Diffstat (limited to 'Sora/Data')
| -rw-r--r-- | Sora/Data/Booru/BooruManager.swift | 28 | ||||
| -rw-r--r-- | Sora/Data/Booru/BooruProvider.swift | 1 | ||||
| -rw-r--r-- | Sora/Data/Booru/Post/BooruPostXMLParser.swift | 227 |
3 files changed, 179 insertions, 77 deletions
diff --git a/Sora/Data/Booru/BooruManager.swift b/Sora/Data/Booru/BooruManager.swift index 3c9c5ca..45a2240 100644 --- a/Sora/Data/Booru/BooruManager.swift +++ b/Sora/Data/Booru/BooruManager.swift @@ -9,7 +9,7 @@ class BooruManager: ObservableObject { @Published var searchText = "" @Published var endOfData: Bool = false private var currentTask: Task<Void, Never>? - let provider: BooruProvider? + let provider: BooruProvider var tags: [String] { if searchText.isEmpty { return [] @@ -26,7 +26,7 @@ class BooruManager: ObservableObject { @Published var selectedPost: BooruPost? #endif - init(_ provider: BooruProvider? = nil) { + init(_ provider: BooruProvider) { self.provider = provider fetchAllTags() @@ -35,7 +35,6 @@ class BooruManager: ObservableObject { func fetchPosts(page: Int = 1, limit: Int = 100, tags: [String] = [], replace: Bool = false) async { guard !isLoading else { return } - guard provider != nil else { return } currentTask?.cancel() @@ -63,7 +62,10 @@ class BooruManager: ObservableObject { if Task.isCancelled { return } DispatchQueue.main.async { - let newPosts = Array(Set(BooruPostXMLParser(data: data).parse())).sorted { $0.id > $1.id } + let newPosts = Array(Set(BooruPostXMLParser(data: data, provider: self.provider).parse())) + .sorted { lhs, rhs in + lhs.id > rhs.id + } if newPosts.isEmpty { self.endOfData = true @@ -102,8 +104,6 @@ class BooruManager: ObservableObject { } func fetchAllTags(limit: Int = 100_000) { - guard provider != nil else { return } - Task { guard let url = urlForTags(limit: limit) else { return } @@ -129,6 +129,13 @@ class BooruManager: ObservableObject { URL(string: "https://\(domain)/post.xml?page=\(page)&limit=\(limit)&tags=\(tagString)") } + private func gelbooruURL(domain: String, page: Int, limit: Int, tagString: String) -> URL? { + URL( + string: + "https://\(domain)/index.php?page=dapi&s=post&q=index&pid=\(page)&limit=\(limit)&tags=\(tagString)" + ) + } + private func urlForPosts(page: Int, limit: Int, tags: [String]) -> URL? { let tagString = tags.joined(separator: "+") @@ -143,13 +150,10 @@ class BooruManager: ObservableObject { return moebooruURL(domain: "sakugabooru.com", page: page, limit: limit, tagString: tagString) case .safebooru: - return URL( - string: - "https://safebooru.org/index.php?page=dapi&s=post&q=index&pid=\(page)&limit=\(limit)&tags=\(tagString)" - ) + return gelbooruURL(domain: "safebooru.org", page: page, limit: limit, tagString: tagString) - default: - return nil + case .gelbooru: + return gelbooruURL(domain: "gelbooru.com", page: page, limit: limit, tagString: tagString) } } diff --git a/Sora/Data/Booru/BooruProvider.swift b/Sora/Data/Booru/BooruProvider.swift index 2aa9238..3a743ec 100644 --- a/Sora/Data/Booru/BooruProvider.swift +++ b/Sora/Data/Booru/BooruProvider.swift @@ -1,4 +1,5 @@ enum BooruProvider: String, CaseIterable, Decodable, Encodable { + case gelbooru = "Gelbooru" case konachan = "Konachan.com" case safebooru = "Safebooru" case sakugabooru = "sakugabooru" diff --git a/Sora/Data/Booru/Post/BooruPostXMLParser.swift b/Sora/Data/Booru/Post/BooruPostXMLParser.swift index d316177..41ad25d 100644 --- a/Sora/Data/Booru/Post/BooruPostXMLParser.swift +++ b/Sora/Data/Booru/Post/BooruPostXMLParser.swift @@ -4,9 +4,14 @@ class BooruPostXMLParser: NSObject, XMLParserDelegate { private var posts: [BooruPost] = [] private var currentPost: BooruPost? private var parser: XMLParser + private let provider: BooruProvider + private var currentPostData: [String: String] = [:] + private var currentElementName: String? + private var currentText: String = "" - init(data: Data) { + init(data: Data, provider: BooruProvider) { parser = XMLParser(data: data) + self.provider = provider super.init() @@ -20,82 +25,174 @@ class BooruPostXMLParser: NSObject, XMLParserDelegate { } func parser( - _: XMLParser, + _ parser: XMLParser, // swiftlint:disable:this unused_parameter didStartElement elementName: String, - namespaceURI _: String?, - qualifiedName _: String?, + namespaceURI: String?, // swiftlint:disable:this unused_parameter + qualifiedName qName: String?, // swiftlint:disable:this unused_parameter attributes attributeDict: [String: String] = [:] ) { - if elementName == "post" { - guard let id = attributeDict["id"], - let heightStr = attributeDict["height"], - let height = Int(heightStr), - let score = attributeDict["score"], - let fileUrl = attributeDict["file_url"], - let parentId = attributeDict["parent_id"], - let sampleUrl = attributeDict["sample_url"], - let sampleWidthStr = attributeDict["sample_width"], - let sampleWidth = Int(sampleWidthStr), - let sampleHeightStr = attributeDict["sample_height"], - let sampleHeight = Int(sampleHeightStr), - let previewUrl = attributeDict["preview_url"], - let rating = attributeDict["rating"], - let tags = attributeDict["tags"], - let widthStr = attributeDict["width"], - let width = Int(widthStr), - 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"], - let previewWidthStr = attributeDict["preview_width"], - let previewWidth = Int(previewWidthStr), - let previewHeightStr = attributeDict["preview_height"], - let previewHeight = Int(previewHeightStr) - else { - return + if provider == .gelbooru { + if elementName == "post" { + currentPostData = [:] + } else { + currentElementName = elementName + currentText = "" } + } else { + if elementName == "post" { + guard let id = attributeDict["id"], + let heightStr = attributeDict["height"], + let height = Int(heightStr), + let score = attributeDict["score"], + let fileUrl = attributeDict["file_url"], + let parentId = attributeDict["parent_id"], + let sampleUrl = attributeDict["sample_url"], + let sampleWidthStr = attributeDict["sample_width"], + let sampleWidth = Int(sampleWidthStr), + let sampleHeightStr = attributeDict["sample_height"], + let sampleHeight = Int(sampleHeightStr), + let previewUrl = attributeDict["preview_url"], + let rating = attributeDict["rating"], + let tags = attributeDict["tags"], + let widthStr = attributeDict["width"], + let width = Int(widthStr), + 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"], + let previewWidthStr = attributeDict["preview_width"], + let previewWidth = Int(previewWidthStr), + let previewHeightStr = attributeDict["preview_height"], + let previewHeight = Int(previewHeightStr) + else { + return + } + + currentPost = BooruPost( + id: id, + height: height, + score: score, + fileURL: URL(string: fileUrl)!, + parentID: parentId, + sampleURL: URL(string: sampleUrl)!, + sampleWidth: sampleWidth, + sampleHeight: sampleHeight, + previewURL: URL(string: previewUrl)!, + rating: BooruRating(rating), + tags: tags.components(separatedBy: " ").filter { !$0.isEmpty }, + width: width, + change: change, + md5: md5, + creatorID: creatorId, + hasChildren: hasChildrenStr == "true", + createdAt: parseCreatedAt(createdAt)!, + status: status, + source: source, + hasNotes: (attributeDict["has_notes"] ?? "false") == "true", + hasComments: (attributeDict["has_comments"] ?? "false") == "true", + previewWidth: previewWidth, + previewHeight: previewHeight + ) + } + } + } - currentPost = BooruPost( - id: id, - height: height, - score: score, - fileURL: URL(string: fileUrl)!, - parentID: parentId, - sampleURL: URL(string: sampleUrl)!, - sampleWidth: sampleWidth, - sampleHeight: sampleHeight, - previewURL: URL(string: previewUrl)!, - rating: BooruRating(rating), - tags: tags.components(separatedBy: " ").filter { !$0.isEmpty }, - width: width, - change: change, - md5: md5, - creatorID: creatorId, - hasChildren: hasChildrenStr == "true", - createdAt: parseCreatedAt(createdAt)!, - status: status, - source: source, - hasNotes: (attributeDict["has_notes"] ?? "false") == "true", - hasComments: (attributeDict["has_comments"] ?? "false") == "true", - previewWidth: previewWidth, - previewHeight: previewHeight - ) + func parser( + _ parser: XMLParser, // swiftlint:disable:this unused_parameter + foundCharacters string: String + ) { + if provider == .gelbooru, currentElementName != nil { + currentText += string } } func parser( - _: XMLParser, + _ parser: XMLParser, // swiftlint:disable:this unused_parameter didEndElement elementName: String, - namespaceURI _: String?, - qualifiedName _: String? + namespaceURI: String?, // swiftlint:disable:this unused_parameter + qualifiedName qName: String? // swiftlint:disable:this unused_parameter ) { - if elementName == "post", let post = currentPost { - posts.append(post) + if provider == .gelbooru { + if elementName == "post" { + guard let id = currentPostData["id"], + let heightStr = currentPostData["height"], + let height = Int(heightStr), + let score = currentPostData["score"], + let fileUrlStr = currentPostData["file_url"], + let fileUrl = URL(string: fileUrlStr), + let parentId = currentPostData["parent_id"], + let sampleUrlStr = currentPostData["sample_url"], + let sampleUrl = URL(string: sampleUrlStr), + let sampleWidthStr = currentPostData["sample_width"], + let sampleWidth = Int(sampleWidthStr), + let sampleHeightStr = currentPostData["sample_height"], + let sampleHeight = Int(sampleHeightStr), + let previewUrlStr = currentPostData["preview_url"], + let previewUrl = URL(string: previewUrlStr), + let rating = currentPostData["rating"], + let tagsString = currentPostData["tags"], + let widthStr = currentPostData["width"], + let width = Int(widthStr), + let change = currentPostData["change"], + let md5 = currentPostData["md5"], + let creatorId = currentPostData["creator_id"], + let hasChildrenStr = currentPostData["has_children"], + let createdAtStr = currentPostData["created_at"], + let createdAt = parseCreatedAt(createdAtStr), + let status = currentPostData["status"], + let source = currentPostData["source"], + let previewWidthStr = currentPostData["preview_width"], + let previewWidth = Int(previewWidthStr), + let previewHeightStr = currentPostData["preview_height"], + let previewHeight = Int(previewHeightStr) + else { + return + } + + posts.append( + BooruPost( + id: id, + height: height, + score: score, + fileURL: fileUrl, + parentID: parentId, + sampleURL: sampleUrl, + sampleWidth: sampleWidth, + sampleHeight: sampleHeight, + previewURL: previewUrl, + rating: BooruRating(rating), + tags: tagsString.components(separatedBy: CharacterSet.whitespacesAndNewlines) + .filter { !$0.isEmpty }, + width: width, + change: change, + md5: md5, + creatorID: creatorId, + hasChildren: hasChildrenStr == "true", + createdAt: createdAt, + status: status, + source: source, + hasNotes: (currentPostData["has_notes"] ?? "false") == "true", + hasComments: (currentPostData["has_comments"] ?? "false") == "true", + previewWidth: previewWidth, + previewHeight: previewHeight + ) + ) + } else if let currentElement = currentElementName { + currentPostData[currentElement] = currentText.trimmingCharacters( + in: .whitespacesAndNewlines + ) + currentElementName = nil + currentText = "" + } + } else { + if elementName == "post", let post = currentPost { + posts.append(post) - currentPost = nil + currentPost = nil + } } } |