Mercurial > minori
annotate src/anilist.cpp @ 6:1d82f6e04d7d
Update: add first parts to the settings dialog
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Wed, 16 Aug 2023 00:49:17 -0400 |
parents | 5af270662505 |
children | 07a9095eaeed |
rev | line source |
---|---|
2 | 1 #include "window.h" |
2 #include "json.h" | |
3 #include <curl/curl.h> | |
4 #include <chrono> | |
5 #include <exception> | |
6 #include <format> | |
7 #include "anilist.h" | |
8 #include "anime.h" | |
9 #include "config.h" | |
10 #include "string_utils.h" | |
11 #define CLIENT_ID "13706" | |
12 | |
13 size_t AniList::CurlWriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) { | |
14 ((std::string*)userdata)->append((char*)contents, size * nmemb); | |
15 return size * nmemb; | |
16 } | |
17 | |
18 std::string AniList::SendRequest(std::string data) { | |
19 struct curl_slist *list = NULL; | |
20 std::string userdata; | |
21 curl = curl_easy_init(); | |
22 if (curl) { | |
23 list = curl_slist_append(list, "Accept: application/json"); | |
24 list = curl_slist_append(list, "Content-Type: application/json"); | |
25 std::string bearer = "Authorization: Bearer " + session.config.anilist.auth_token; | |
26 list = curl_slist_append(list, bearer.c_str()); | |
27 curl_easy_setopt(curl, CURLOPT_URL, "https://graphql.anilist.co"); | |
28 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); | |
29 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); | |
30 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata); | |
31 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlWriteCallback); | |
32 /* FIXME: This sucks. When using HTTPS, we should ALWAYS make sure that our peer | |
33 is actually valid. I assume the best way to go about this would be to bundle a | |
34 certificate file, and if it's not found we should *prompt the user* and ask them | |
35 if it's okay to contact AniList WITHOUT verification. If so, we're golden, and this | |
36 flag will be set. If not, we should abort mission. | |
37 | |
38 For this program, it's probably fine to just contact AniList without | |
39 HTTPS verification. However it should still be in the list of things to do... */ | |
4
5af270662505
Set override functions as override
Paper <mrpapersonic@gmail.com>
parents:
3
diff
changeset
|
40 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); |
2 | 41 res = curl_easy_perform(curl); |
42 curl_slist_free_all(list); | |
43 if (res != CURLE_OK) { | |
44 QMessageBox box(QMessageBox::Icon::Critical, "", QString("curl_easy_perform(curl) failed!: ") + QString(curl_easy_strerror(res))); | |
45 box.exec(); | |
46 curl_easy_cleanup(curl); | |
47 return ""; | |
48 } | |
49 curl_easy_cleanup(curl); | |
50 return userdata; | |
51 } | |
52 return ""; | |
53 } | |
54 | |
55 int AniList::GetUserId(std::string name) { | |
56 #define QUERY "query ($name: String) {\n" \ | |
57 " User (name: $name) {\n" \ | |
58 " id\n" \ | |
59 " }\n" \ | |
60 "}\n" | |
61 nlohmann::json json = { | |
62 {"query", QUERY}, | |
63 {"variables", { | |
64 {"name", name} | |
65 }} | |
66 }; | |
67 auto ret = nlohmann::json::parse(SendRequest(json.dump())); | |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
68 return JSON::GetInt(ret, "/data/User/id"_json_pointer); |
2 | 69 #undef QUERY |
70 } | |
71 | |
72 /* Maps to convert string forms to our internal enums */ | |
73 | |
74 std::map<std::string, enum AnimeWatchingStatus> StringToAnimeWatchingMap = { | |
75 {"CURRENT", CURRENT}, | |
76 {"PLANNING", PLANNING}, | |
77 {"COMPLETED", COMPLETED}, | |
78 {"DROPPED", DROPPED}, | |
79 {"PAUSED", PAUSED}, | |
80 {"REPEATING", REPEATING} | |
81 }; | |
82 | |
83 std::map<std::string, enum AnimeAiringStatus> StringToAnimeAiringMap = { | |
84 {"FINISHED", FINISHED}, | |
85 {"RELEASING", RELEASING}, | |
86 {"NOT_YET_RELEASED", NOT_YET_RELEASED}, | |
87 {"CANCELLED", CANCELLED}, | |
88 {"HIATUS", HIATUS} | |
89 }; | |
90 | |
91 std::map<std::string, enum AnimeSeason> StringToAnimeSeasonMap = { | |
92 {"WINTER", WINTER}, | |
93 {"SPRING", SPRING}, | |
94 {"SUMMER", SUMMER}, | |
95 {"FALL", FALL} | |
96 }; | |
97 | |
98 std::map<std::string, enum AnimeFormat> StringToAnimeFormatMap = { | |
99 {"TV", TV}, | |
100 {"TV_SHORT", TV_SHORT}, | |
101 {"MOVIE", MOVIE}, | |
102 {"SPECIAL", SPECIAL}, | |
103 {"OVA", OVA}, | |
104 {"ONA", ONA}, | |
105 {"MUSIC", MUSIC}, | |
106 {"MANGA", MANGA}, | |
107 {"NOVEL", NOVEL}, | |
108 {"ONE_SHOT", ONE_SHOT} | |
109 }; | |
110 | |
111 int AniList::UpdateAnimeList(std::vector<AnimeList>* anime_lists, int id) { | |
112 /* NOTE: these should be in the qrc file */ | |
113 #define QUERY "query ($id: Int) {\n" \ | |
114 " MediaListCollection (userId: $id, type: ANIME) {\n" \ | |
115 " lists {\n" \ | |
116 " name\n" \ | |
117 " entries {\n" \ | |
118 " score\n" \ | |
119 " notes\n" \ | |
120 " progress\n" \ | |
121 " startedAt {\n" \ | |
122 " year\n" \ | |
123 " month\n" \ | |
124 " day\n" \ | |
125 " }\n" \ | |
126 " completedAt {\n" \ | |
127 " year\n" \ | |
128 " month\n" \ | |
129 " day\n" \ | |
130 " }\n" \ | |
131 " updatedAt\n" \ | |
132 " media {\n" \ | |
133 " id\n" \ | |
134 " title {\n" \ | |
135 " romaji\n" \ | |
136 " english\n" \ | |
137 " native\n" \ | |
138 " }\n" \ | |
139 " format\n" \ | |
140 " status\n" \ | |
141 " averageScore\n" \ | |
142 " season\n" \ | |
143 " startDate {\n" \ | |
144 " year\n" \ | |
145 " month\n" \ | |
146 " day\n" \ | |
147 " }\n" \ | |
148 " genres\n" \ | |
149 " episodes\n" \ | |
150 " duration\n" \ | |
151 " synonyms\n" \ | |
152 " description(asHtml: false)\n" \ | |
153 " }\n" \ | |
154 " }\n" \ | |
155 " }\n" \ | |
156 " }\n" \ | |
157 "}\n" | |
158 nlohmann::json json = { | |
159 {"query", QUERY}, | |
160 {"variables", { | |
161 {"id", id} | |
162 }} | |
163 }; | |
164 /* TODO: do a try catch here, catch any json errors and then call | |
165 Authorize() if needed */ | |
166 auto res = nlohmann::json::parse(SendRequest(json.dump())); | |
167 /* TODO: make sure that we actually need the wstring converter and see | |
168 if we can just get wide strings back from nlohmann::json */ | |
169 for (const auto& list : res["data"]["MediaListCollection"]["lists"].items()) { | |
170 /* why are the .key() values strings?? */ | |
171 AnimeList anime_list; | |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
172 anime_list.name = JSON::GetString(list.value(), "/name"_json_pointer); |
2 | 173 for (const auto& entry : list.value()["entries"].items()) { |
174 Anime anime; | |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
175 anime.score = JSON::GetInt(entry.value(), "/score"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
176 anime.progress = JSON::GetInt(entry.value(), "/progress"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
177 anime.status = StringToAnimeWatchingMap[JSON::GetString(entry.value(), "/status"_json_pointer)]; |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
178 anime.notes = JSON::GetString(entry.value(), "/notes"_json_pointer); |
2 | 179 |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
180 anime.started.SetYear(JSON::GetInt(entry.value(), "/startedAt/year"_json_pointer)); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
181 anime.started.SetMonth(JSON::GetInt(entry.value(), "/startedAt/month"_json_pointer)); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
182 anime.started.SetDay(JSON::GetInt(entry.value(), "/startedAt/day"_json_pointer)); |
2 | 183 |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
184 anime.completed.SetYear(JSON::GetInt(entry.value(), "/completedAt/year"_json_pointer)); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
185 anime.completed.SetMonth(JSON::GetInt(entry.value(), "/completedAt/month"_json_pointer)); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
186 anime.completed.SetDay(JSON::GetInt(entry.value(), "/completedAt/day"_json_pointer)); |
2 | 187 |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
188 anime.updated = JSON::GetInt(entry.value(), "/updatedAt"_json_pointer); |
2 | 189 |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
190 anime.title.native = JSON::GetString(entry.value(), "/media/title/native"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
191 anime.title.english = JSON::GetString(entry.value(), "/media/title/english"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
192 anime.title.romaji = JSON::GetString(entry.value(), "/media/title/romaji"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
193 /* fallback to romaji if english is not available |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
194 note that this takes up more space in memory and is stinky */ |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
195 if (anime.title.english.empty()) |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
196 anime.title.english = anime.title.romaji; |
2 | 197 |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
198 anime.id = JSON::GetInt(entry.value(), "/media/id"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
199 anime.episodes = JSON::GetInt(entry.value(), "/media/episodes"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
200 anime.type = StringToAnimeFormatMap[JSON::GetString(entry.value()["media"], "/media/format"_json_pointer)]; |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
201 |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
202 anime.airing = StringToAnimeAiringMap[JSON::GetString(entry.value()["media"], "/media/status"_json_pointer)]; |
2 | 203 |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
204 anime.air_date.SetYear(JSON::GetInt(entry.value(), "/media/startDate/year"_json_pointer)); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
205 anime.air_date.SetMonth(JSON::GetInt(entry.value(), "/media/startDate/month"_json_pointer)); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
206 anime.air_date.SetDay(JSON::GetInt(entry.value(), "/media/startDate/day"_json_pointer)); |
2 | 207 |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
208 anime.audience_score = JSON::GetInt(entry.value(), "/media/averageScore"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
209 anime.season = StringToAnimeSeasonMap[JSON::GetString(entry.value(), "/media/season"_json_pointer)]; |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
210 anime.duration = JSON::GetInt(entry.value(), "/media/duration"_json_pointer); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
211 anime.synopsis = StringUtils::TextifySynopsis(JSON::GetString(entry.value(), "/media/description"_json_pointer)); |
2 | 212 |
213 if (entry.value()["media"]["genres"].is_array()) | |
214 anime.genres = entry.value()["media"]["genres"].get<std::vector<std::string>>(); | |
215 anime_list.Add(anime); | |
216 } | |
217 anime_lists->push_back(anime_list); | |
218 } | |
219 return 1; | |
220 #undef QUERY | |
221 } | |
222 | |
223 int AniList::Authorize() { | |
6
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
224 /* Prompt for PIN */ |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
225 QDesktopServices::openUrl(QUrl("https://anilist.co/api/v2/oauth/authorize?client_id=" CLIENT_ID "&response_type=token")); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
226 bool ok; |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
227 QString token = QInputDialog::getText(0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal, "", &ok); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
228 if (ok && !token.isEmpty()) { |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
229 session.config.anilist.auth_token = token.toStdString(); |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
230 } else { // fail |
1d82f6e04d7d
Update: add first parts to the settings dialog
Paper <mrpapersonic@gmail.com>
parents:
4
diff
changeset
|
231 return 0; |
2 | 232 } |
233 return 1; | |
234 } |