summaryrefslogtreecommitdiff
path: root/Sora/Data/Moebooru/MoebooruManager.swift
blob: fd8d3370df4d44bbfcf7277e2fc9d0fa680b6588 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import SwiftUI

@MainActor
class MoebooruManager: ObservableObject {
    @Published var posts: [MoebooruPost] = []
    @Published var allTags: [MoebooruTag] = []
    @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<Void, Never>?
    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://yande.re/post.xml?page=\(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(MoebooruPostXMLParser().parse(data: data))).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://yande.re/tag.xml?limit=\(limit)") else { return }

            do {
                let (data, _) = try await URLSession.shared.data(from: url)

                if Task.isCancelled { return }

                DispatchQueue.main.async {
                    self.allTags = (MoebooruTagXMLParser().parse(data: data)).sorted { $0.count > $1.count }
                }
            } catch {
                if (error as? URLError)?.code != .cancelled {
                    #if DEBUG
                        print(error)
                    #endif
                }
            }
        }
    }
}