From e23a8714f6cdc1b55ef96f6bc0f024af049ce299 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Thu, 20 Feb 2025 19:33:19 -0800 Subject: feat: Development commit --- Sora/Data/Danbooru/DanbooruManager.swift | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 Sora/Data/Danbooru/DanbooruManager.swift (limited to 'Sora/Data/Danbooru/DanbooruManager.swift') diff --git a/Sora/Data/Danbooru/DanbooruManager.swift b/Sora/Data/Danbooru/DanbooruManager.swift new file mode 100644 index 0000000..7bbb3a7 --- /dev/null +++ b/Sora/Data/Danbooru/DanbooruManager.swift @@ -0,0 +1,114 @@ +import SwiftUI + +@MainActor +class DanbooruManager: ObservableObject { + @Published var posts: [DanbooruPost] = [] + @Published var allTags: [BooruTag] = [] + @Published var isLoading: Bool = false + @Published var currentPage: Int = 1 + @Published var searchText = "" + @Published var endOfData: Bool = false + #if os(macOS) + @Published var selectedPost: MoebooruPost? + #endif + private var currentTask: Task? + var tags: [String] { + if searchText.isEmpty { + return [] + } + + return searchText + .split(separator: " ") + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .filter { !$0.isEmpty } + } + + init() { + fetchAllTags() + } + + func fetchPosts(page: Int = 1, limit: Int = 100, tags: [String] = [], replace: Bool = false) async { + guard !isLoading else { return } + + currentTask?.cancel() + + currentTask = Task { + isLoading = true + + defer { isLoading = false } + + if replace { + self.posts = [] + self.currentPage = 1 + } + + guard let url = URL(string: "https://safebooru.org/index.php?page=dapi&s=post&q=index&pid=\(page)&limit=\(limit)&tags=\(tags.joined(separator: "+"))") else { return } + + do { + let (data, _) = try await URLSession.shared.data(from: url) + + if Task.isCancelled { return } + + DispatchQueue.main.async { + let newPosts = Array(Set(DanbooruPostXMLParser(data: data).parse())).sorted { $0.id > $1.id } + + if newPosts == [] { + self.endOfData = true + } else { + self.posts += newPosts + } + } + } catch { + if (error as? URLError)?.code != .cancelled { + #if DEBUG + print(error) + #endif + } + } + } + } + + func performSearch() { + Task { + await fetchPosts( + page: 1, + tags: tags, + replace: true + ) + } + } + + func loadNextPage() { + guard !isLoading else { return } + + Task { + await fetchPosts(page: currentPage + 1, tags: tags) + + DispatchQueue.main.async { + self.currentPage += 1 + } + } + } + + func fetchAllTags(limit: Int = 100_000) { + Task { + guard let url = URL(string: "https://safebooru.org/index.php?page=dapi&s=tag&q=index&limit=\(limit)") else { return } + + do { + let (data, _) = try await URLSession.shared.data(from: url) + + if Task.isCancelled { return } + + DispatchQueue.main.async { + self.allTags = (BooruTagXMLParser(data: data).parse()).sorted { $0.count > $1.count } + } + } catch { + if (error as? URLError)?.code != .cancelled { + #if DEBUG + print(error) + #endif + } + } + } + } +} -- cgit v1.2.3