Mercurial > minori
comparison src/services/anilist.cc @ 369:47c9f8502269
*: clang-format all the things
I've edited the formatting a bit. Now pointer asterisks (and reference
ampersands) are on the variable instead of the type, as well as having
newlines for function braces (but nothing else)
author | Paper <paper@tflc.us> |
---|---|
date | Fri, 25 Jul 2025 10:16:02 -0400 |
parents | f81bed4e04ac |
children |
comparison
equal
deleted
inserted
replaced
368:6d37a998cf91 | 369:47c9f8502269 |
---|---|
1 #include "services/anilist.h" | 1 #include "services/anilist.h" |
2 #include "core/anime.h" | 2 #include "core/anime.h" |
3 #include "core/anime_db.h" | 3 #include "core/anime_db.h" |
4 #include "core/config.h" | |
4 #include "core/date.h" | 5 #include "core/date.h" |
5 #include "core/config.h" | |
6 #include "core/http.h" | 6 #include "core/http.h" |
7 #include "core/json.h" | 7 #include "core/json.h" |
8 #include "core/session.h" | 8 #include "core/session.h" |
9 #include "core/strings.h" | 9 #include "core/strings.h" |
10 #include "gui/translate/anilist.h" | 10 #include "gui/translate/anilist.h" |
68 "synonyms\n" \ | 68 "synonyms\n" \ |
69 "description(asHtml: false)\n" | 69 "description(asHtml: false)\n" |
70 | 70 |
71 /* FIXME: why is this here */ | 71 /* FIXME: why is this here */ |
72 | 72 |
73 static std::optional<nlohmann::json> SendJSONRequest(const nlohmann::json& data) { | 73 static std::optional<nlohmann::json> SendJSONRequest(const nlohmann::json &data) |
74 { | |
74 std::vector<std::string> headers = { | 75 std::vector<std::string> headers = { |
75 "Accept: application/json", | 76 "Accept: application/json", |
76 "Content-Type: application/json", | 77 "Content-Type: application/json", |
77 }; | 78 }; |
78 | 79 |
79 if (!session.config.auth.anilist.auth_token.empty()) | 80 if (!session.config.auth.anilist.auth_token.empty()) |
80 headers.push_back("Authorization: Bearer " + session.config.auth.anilist.auth_token); | 81 headers.push_back("Authorization: Bearer " + session.config.auth.anilist.auth_token); |
81 | 82 |
82 const std::string response = Strings::ToUtf8String(HTTP::Request("https://graphql.anilist.co", headers, data.dump(), HTTP::Type::Post)); | 83 const std::string response = |
84 Strings::ToUtf8String(HTTP::Request("https://graphql.anilist.co", headers, data.dump(), HTTP::Type::Post)); | |
83 if (response.empty()) { | 85 if (response.empty()) { |
84 session.SetStatusBar(Strings::Translate("AniList: JSON request returned an empty result!")); | 86 session.SetStatusBar(Strings::Translate("AniList: JSON request returned an empty result!")); |
85 return std::nullopt; | 87 return std::nullopt; |
86 } | 88 } |
87 | 89 |
88 nlohmann::json out; | 90 nlohmann::json out; |
89 | 91 |
90 try { | 92 try { |
91 out = nlohmann::json::parse(response); | 93 out = nlohmann::json::parse(response); |
92 } catch (const std::exception& ex) { | 94 } catch (const std::exception &ex) { |
93 session.SetStatusBar(fmt::format(Strings::Translate("AniList: Failed to parse request JSON with error \"{}\"!"), ex.what())); | 95 session.SetStatusBar( |
96 fmt::format(Strings::Translate("AniList: Failed to parse request JSON with error \"{}\"!"), ex.what())); | |
94 return std::nullopt; | 97 return std::nullopt; |
95 } | 98 } |
96 | 99 |
97 if (out.contains("/errors"_json_pointer) && out.at("/errors"_json_pointer).is_array()) { | 100 if (out.contains("/errors"_json_pointer) && out.at("/errors"_json_pointer).is_array()) { |
98 for (const auto& error : out.at("/errors"_json_pointer)) | 101 for (const auto &error : out.at("/errors"_json_pointer)) |
99 std::cerr << "AniList: Received an error in response: " | 102 std::cerr << "AniList: Received an error in response: " |
100 << JSON::GetString<std::string>(error, "/message"_json_pointer, "") << std::endl; | 103 << JSON::GetString<std::string>(error, "/message"_json_pointer, "") << std::endl; |
101 | 104 |
102 session.SetStatusBar(Strings::Translate("AniList: Received an error in response!")); | 105 session.SetStatusBar(Strings::Translate("AniList: Received an error in response!")); |
103 return std::nullopt; | 106 return std::nullopt; |
104 } | 107 } |
105 | 108 |
106 return out; | 109 return out; |
107 } | 110 } |
108 | 111 |
109 static void ParseListStatus(std::string status, Anime::Anime& anime) { | 112 static void ParseListStatus(std::string status, Anime::Anime &anime) |
113 { | |
110 static const std::unordered_map<std::string, Anime::ListStatus> map = { | 114 static const std::unordered_map<std::string, Anime::ListStatus> map = { |
111 {"CURRENT", Anime::ListStatus::Current }, | 115 {"CURRENT", Anime::ListStatus::Current }, |
112 {"PLANNING", Anime::ListStatus::Planning }, | 116 {"PLANNING", Anime::ListStatus::Planning }, |
113 {"COMPLETED", Anime::ListStatus::Completed}, | 117 {"COMPLETED", Anime::ListStatus::Completed}, |
114 {"DROPPED", Anime::ListStatus::Dropped }, | 118 {"DROPPED", Anime::ListStatus::Dropped }, |
115 {"PAUSED", Anime::ListStatus::Paused } | 119 {"PAUSED", Anime::ListStatus::Paused } |
116 }; | 120 }; |
117 | 121 |
118 if (status == "REPEATING") { | 122 if (status == "REPEATING") { |
119 anime.SetUserIsRewatching(true); | 123 anime.SetUserIsRewatching(true); |
120 anime.SetUserStatus(Anime::ListStatus::Current); | 124 anime.SetUserStatus(Anime::ListStatus::Current); |
121 return; | 125 return; |
127 } | 131 } |
128 | 132 |
129 anime.SetUserStatus(map.at(status)); | 133 anime.SetUserStatus(map.at(status)); |
130 } | 134 } |
131 | 135 |
132 static std::string ListStatusToString(const Anime::Anime& anime) { | 136 static std::string ListStatusToString(const Anime::Anime &anime) |
137 { | |
133 if (anime.GetUserIsRewatching() && anime.GetUserStatus() == Anime::ListStatus::Current) | 138 if (anime.GetUserIsRewatching() && anime.GetUserStatus() == Anime::ListStatus::Current) |
134 return "REWATCHING"; | 139 return "REWATCHING"; |
135 | 140 |
136 switch (anime.GetUserStatus()) { | 141 switch (anime.GetUserStatus()) { |
137 case Anime::ListStatus::Planning: return "PLANNING"; | 142 case Anime::ListStatus::Planning: return "PLANNING"; |
141 default: break; | 146 default: break; |
142 } | 147 } |
143 return "CURRENT"; | 148 return "CURRENT"; |
144 } | 149 } |
145 | 150 |
146 static void ParseTitle(const nlohmann::json& json, Anime::Anime& anime) { | 151 static void ParseTitle(const nlohmann::json &json, Anime::Anime &anime) |
152 { | |
147 static const std::unordered_map<Anime::TitleLanguage, nlohmann::json::json_pointer> map = { | 153 static const std::unordered_map<Anime::TitleLanguage, nlohmann::json::json_pointer> map = { |
148 {Anime::TitleLanguage::Native, "/native"_json_pointer}, | 154 {Anime::TitleLanguage::Native, "/native"_json_pointer }, |
149 {Anime::TitleLanguage::English, "/english"_json_pointer}, | 155 {Anime::TitleLanguage::English, "/english"_json_pointer}, |
150 {Anime::TitleLanguage::Romaji, "/romaji"_json_pointer}, | 156 {Anime::TitleLanguage::Romaji, "/romaji"_json_pointer }, |
151 }; | 157 }; |
152 | 158 |
153 for (const auto& [language, ptr] : map) | 159 for (const auto &[language, ptr] : map) |
154 if (json.contains(ptr) && json[ptr].is_string()) | 160 if (json.contains(ptr) && json[ptr].is_string()) |
155 anime.SetTitle(language, json[ptr]); | 161 anime.SetTitle(language, json[ptr]); |
156 } | 162 } |
157 | 163 |
158 static int ParseMediaJson(const nlohmann::json& json) { | 164 static int ParseMediaJson(const nlohmann::json &json) |
165 { | |
159 if (!json.contains("/id"_json_pointer) || !json["/id"_json_pointer].is_number()) { | 166 if (!json.contains("/id"_json_pointer) || !json["/id"_json_pointer].is_number()) { |
160 session.SetStatusBar(Strings::Translate("AniList: Failed to parse anime object!")); | 167 session.SetStatusBar(Strings::Translate("AniList: Failed to parse anime object!")); |
161 return 0; | 168 return 0; |
162 } | 169 } |
163 | 170 |
167 if (!id) { | 174 if (!id) { |
168 session.SetStatusBar(Strings::Translate("AniList: Failed to parse anime object!")); | 175 session.SetStatusBar(Strings::Translate("AniList: Failed to parse anime object!")); |
169 return 0; | 176 return 0; |
170 } | 177 } |
171 | 178 |
172 Anime::Anime& anime = Anime::db.items[id]; | 179 Anime::Anime &anime = Anime::db.items[id]; |
173 anime.SetId(id); | 180 anime.SetId(id); |
174 anime.SetServiceId(Anime::Service::AniList, service_id); | 181 anime.SetServiceId(Anime::Service::AniList, service_id); |
175 | 182 |
176 if (json.contains("/idMal"_json_pointer) && json["/idMal"_json_pointer].is_number()) | 183 if (json.contains("/idMal"_json_pointer) && json["/idMal"_json_pointer].is_number()) |
177 anime.SetServiceId(Anime::Service::MyAnimeList, Strings::ToUtf8String(json["/idMal"_json_pointer].get<int>())); | 184 anime.SetServiceId(Anime::Service::MyAnimeList, Strings::ToUtf8String(json["/idMal"_json_pointer].get<int>())); |
180 | 187 |
181 anime.SetEpisodes(JSON::GetNumber(json, "/episodes"_json_pointer, 0)); | 188 anime.SetEpisodes(JSON::GetNumber(json, "/episodes"_json_pointer, 0)); |
182 anime.SetFormat(Translate::AniList::ToSeriesFormat(JSON::GetString<std::string>(json, "/format"_json_pointer, ""))); | 189 anime.SetFormat(Translate::AniList::ToSeriesFormat(JSON::GetString<std::string>(json, "/format"_json_pointer, ""))); |
183 | 190 |
184 anime.SetAiringStatus( | 191 anime.SetAiringStatus( |
185 Translate::AniList::ToSeriesStatus(JSON::GetString<std::string>(json, "/status"_json_pointer, ""))); | 192 Translate::AniList::ToSeriesStatus(JSON::GetString<std::string>(json, "/status"_json_pointer, ""))); |
186 | 193 |
187 if (json.contains("/startDate"_json_pointer) && json["/startDate"_json_pointer].is_object()) | 194 if (json.contains("/startDate"_json_pointer) && json["/startDate"_json_pointer].is_object()) |
188 anime.SetStartedDate(Date(json["/startDate"_json_pointer])); | 195 anime.SetStartedDate(Date(json["/startDate"_json_pointer])); |
189 | 196 |
190 anime.SetCompletedDate(json.contains("/endDate"_json_pointer) && json["/endDate"_json_pointer].is_object() | 197 anime.SetCompletedDate(json.contains("/endDate"_json_pointer) && json["/endDate"_json_pointer].is_object() |
191 ? Date(json["/endDate"_json_pointer]) | 198 ? Date(json["/endDate"_json_pointer]) |
192 : anime.GetStartedDate()); | 199 : anime.GetStartedDate()); |
193 | 200 |
194 anime.SetPosterUrl(JSON::GetString<std::string>(json, "/coverImage/large"_json_pointer, "")); | 201 anime.SetPosterUrl(JSON::GetString<std::string>(json, "/coverImage/large"_json_pointer, "")); |
195 | 202 |
196 anime.SetAudienceScore(JSON::GetNumber(json, "/averageScore"_json_pointer, 0)); | 203 anime.SetAudienceScore(JSON::GetNumber(json, "/averageScore"_json_pointer, 0)); |
197 // anime.SetSeason(Translate::AniList::ToSeriesSeason(JSON::GetString<std::string>(json, "/season"_json_pointer, ""))); | 204 // anime.SetSeason(Translate::AniList::ToSeriesSeason(JSON::GetString<std::string>(json, "/season"_json_pointer, |
205 // ""))); | |
198 anime.SetDuration(JSON::GetNumber(json, "/duration"_json_pointer, 0)); | 206 anime.SetDuration(JSON::GetNumber(json, "/duration"_json_pointer, 0)); |
199 | 207 |
200 std::string synopsis = JSON::GetString<std::string>(json, "/description"_json_pointer, ""); | 208 std::string synopsis = JSON::GetString<std::string>(json, "/description"_json_pointer, ""); |
201 Strings::TextifySynopsis(synopsis); | 209 Strings::TextifySynopsis(synopsis); |
202 anime.SetSynopsis(synopsis); | 210 anime.SetSynopsis(synopsis); |
206 | 214 |
207 { | 215 { |
208 std::vector<std::string> producers; | 216 std::vector<std::string> producers; |
209 | 217 |
210 if (json.contains("/studios/edges"_json_pointer) && json["/studios/edges"_json_pointer].is_array()) | 218 if (json.contains("/studios/edges"_json_pointer) && json["/studios/edges"_json_pointer].is_array()) |
211 for (const auto& edge : json["/studios/edges"_json_pointer]) | 219 for (const auto &edge : json["/studios/edges"_json_pointer]) |
212 if (edge.contains("/node/name"_json_pointer) && edge["/node/name"_json_pointer].is_string()) | 220 if (edge.contains("/node/name"_json_pointer) && edge["/node/name"_json_pointer].is_string()) |
213 producers.push_back(edge["/node/name"_json_pointer].get<std::string>()); | 221 producers.push_back(edge["/node/name"_json_pointer].get<std::string>()); |
214 | 222 |
215 anime.SetProducers(producers); | 223 anime.SetProducers(producers); |
216 } | 224 } |
217 | 225 |
218 return id; | 226 return id; |
219 } | 227 } |
220 | 228 |
221 static int ParseListItem(const nlohmann::json& json) { | 229 static int ParseListItem(const nlohmann::json &json) |
230 { | |
222 int id = ParseMediaJson(json["/media"_json_pointer]); | 231 int id = ParseMediaJson(json["/media"_json_pointer]); |
223 if (!id) | 232 if (!id) |
224 return 0; | 233 return 0; |
225 | 234 |
226 Anime::Anime& anime = Anime::db.items[id]; | 235 Anime::Anime &anime = Anime::db.items[id]; |
227 | 236 |
228 anime.AddToUserList(); | 237 anime.AddToUserList(); |
229 | 238 |
230 anime.SetUserScore(JSON::GetNumber(json, "/score"_json_pointer, 0)); | 239 anime.SetUserScore(JSON::GetNumber(json, "/score"_json_pointer, 0)); |
231 anime.SetUserProgress(JSON::GetNumber(json, "/progress"_json_pointer, 0)); | 240 anime.SetUserProgress(JSON::GetNumber(json, "/progress"_json_pointer, 0)); |
238 anime.SetUserTimeUpdated(JSON::GetNumber(json, "/updatedAt"_json_pointer, 0)); | 247 anime.SetUserTimeUpdated(JSON::GetNumber(json, "/updatedAt"_json_pointer, 0)); |
239 | 248 |
240 return id; | 249 return id; |
241 } | 250 } |
242 | 251 |
243 static bool ParseList(const nlohmann::json& json) { | 252 static bool ParseList(const nlohmann::json &json) |
253 { | |
244 bool success = true; | 254 bool success = true; |
245 | 255 |
246 for (const auto& entry : json["entries"].items()) | 256 for (const auto &entry : json["entries"].items()) |
247 if (!ParseListItem(entry.value())) | 257 if (!ParseListItem(entry.value())) |
248 success = false; | 258 success = false; |
249 | 259 |
250 return success; | 260 return success; |
251 } | 261 } |
252 | 262 |
253 int GetAnimeList() { | 263 int GetAnimeList() |
254 auto& auth = session.config.auth.anilist; | 264 { |
255 | 265 auto &auth = session.config.auth.anilist; |
256 static constexpr std::string_view query = | 266 |
257 "query ($id: Int) {\n" | 267 static constexpr std::string_view query = "query ($id: Int) {\n" |
258 " MediaListCollection (userId: $id, type: ANIME) {\n" | 268 " MediaListCollection (userId: $id, type: ANIME) {\n" |
259 " lists {\n" | 269 " lists {\n" |
260 " name\n" | 270 " name\n" |
261 " entries {\n" | 271 " entries {\n" |
262 " score\n" | 272 " score\n" |
263 " notes\n" | 273 " notes\n" |
264 " status\n" | 274 " status\n" |
265 " progress\n" | 275 " progress\n" |
266 " startedAt {\n" | 276 " startedAt {\n" |
267 " year\n" | 277 " year\n" |
268 " month\n" | 278 " month\n" |
269 " day\n" | 279 " day\n" |
270 " }\n" | 280 " }\n" |
271 " completedAt {\n" | 281 " completedAt {\n" |
272 " year\n" | 282 " year\n" |
273 " month\n" | 283 " month\n" |
274 " day\n" | 284 " day\n" |
275 " }\n" | 285 " }\n" |
276 " updatedAt\n" | 286 " updatedAt\n" |
277 " media {\n" | 287 " media {\n" MEDIA_FIELDS " }\n" |
278 MEDIA_FIELDS | 288 " }\n" |
279 " }\n" | 289 " }\n" |
280 " }\n" | 290 " }\n" |
281 " }\n" | 291 "}\n"; |
282 " }\n" | |
283 "}\n"; | |
284 | 292 |
285 // clang-format off | 293 // clang-format off |
286 nlohmann::json request = { | 294 nlohmann::json request = { |
287 {"query", query}, | 295 {"query", query}, |
288 {"variables", { | 296 {"variables", { |
297 if (!response) | 305 if (!response) |
298 return 0; | 306 return 0; |
299 | 307 |
300 Anime::db.RemoveAllUserData(); | 308 Anime::db.RemoveAllUserData(); |
301 | 309 |
302 const nlohmann::json& json = response.value(); | 310 const nlohmann::json &json = response.value(); |
303 | 311 |
304 bool success = true; | 312 bool success = true; |
305 | 313 |
306 for (const auto& list : json["data"]["MediaListCollection"]["lists"].items()) | 314 for (const auto &list : json["data"]["MediaListCollection"]["lists"].items()) |
307 if (!ParseList(list.value())) | 315 if (!ParseList(list.value())) |
308 success = false; | 316 success = false; |
309 | 317 |
310 if (success) | 318 if (success) |
311 session.SetStatusBar(Strings::Translate("AniList: Retrieved anime list successfully!")); | 319 session.SetStatusBar(Strings::Translate("AniList: Retrieved anime list successfully!")); |
312 | 320 |
313 return 1; | 321 return 1; |
314 } | 322 } |
315 | 323 |
316 /* return is a vector of anime ids */ | 324 /* return is a vector of anime ids */ |
317 std::vector<int> Search(const std::string& search) { | 325 std::vector<int> Search(const std::string &search) |
318 static constexpr std::string_view query = | 326 { |
319 "query ($search: String) {\n" | 327 static constexpr std::string_view query = "query ($search: String) {\n" |
320 " Page (page: 1, perPage: 50) {\n" | 328 " Page (page: 1, perPage: 50) {\n" |
321 " media (search: $search, type: ANIME) {\n" | 329 " media (search: $search, type: ANIME) {\n" MEDIA_FIELDS " }\n" |
322 MEDIA_FIELDS | 330 " }\n" |
323 " }\n" | 331 "}\n"; |
324 " }\n" | |
325 "}\n"; | |
326 | 332 |
327 // clang-format off | 333 // clang-format off |
328 nlohmann::json json = { | 334 nlohmann::json json = { |
329 {"query", query}, | 335 {"query", query}, |
330 {"variables", { | 336 {"variables", { |
335 | 341 |
336 const std::optional<nlohmann::json> response = SendJSONRequest(json); | 342 const std::optional<nlohmann::json> response = SendJSONRequest(json); |
337 if (!response) | 343 if (!response) |
338 return {}; | 344 return {}; |
339 | 345 |
340 const nlohmann::json& result = response.value(); | 346 const nlohmann::json &result = response.value(); |
341 | 347 |
342 /* FIXME: error handling here */ | 348 /* FIXME: error handling here */ |
343 std::vector<int> ret; | 349 std::vector<int> ret; |
344 ret.reserve(result["/data/Page/media"_json_pointer].size()); | 350 ret.reserve(result["/data/Page/media"_json_pointer].size()); |
345 | 351 |
346 for (const auto& media : result["/data/Page/media"_json_pointer].items()) | 352 for (const auto &media : result["/data/Page/media"_json_pointer].items()) |
347 ret.push_back(ParseMediaJson(media.value())); | 353 ret.push_back(ParseMediaJson(media.value())); |
348 | 354 |
349 return ret; | 355 return ret; |
350 } | 356 } |
351 | 357 |
352 bool GetSeason(Anime::Season season) { | 358 bool GetSeason(Anime::Season season) |
359 { | |
353 static constexpr std::string_view query = | 360 static constexpr std::string_view query = |
354 "query ($season: MediaSeason!, $season_year: Int!, $page: Int) {\n" | 361 "query ($season: MediaSeason!, $season_year: Int!, $page: Int) {\n" |
355 " Page(page: $page) {\n" | 362 " Page(page: $page) {\n" |
356 " media(season: $season, seasonYear: $season_year, type: ANIME, sort: START_DATE) {\n" | 363 " media(season: $season, seasonYear: $season_year, type: ANIME, sort: START_DATE) {\n" MEDIA_FIELDS " }\n" |
357 MEDIA_FIELDS | 364 " pageInfo {\n" |
358 " }\n" | 365 " total\n" |
359 " pageInfo {\n" | 366 " perPage\n" |
360 " total\n" | 367 " currentPage\n" |
361 " perPage\n" | 368 " lastPage\n" |
362 " currentPage\n" | 369 " hasNextPage\n" |
363 " lastPage\n" | 370 " }\n" |
364 " hasNextPage\n" | 371 " }\n" |
365 " }\n" | 372 "}\n"; |
366 " }\n" | |
367 "}\n"; | |
368 | 373 |
369 int page = 0; | 374 int page = 0; |
370 bool has_next_page = true; | 375 bool has_next_page = true; |
371 | 376 |
372 while (has_next_page) { | 377 while (has_next_page) { |
373 nlohmann::json json = { | 378 nlohmann::json json = { |
374 {"query", query}, | 379 {"query", query}, |
375 {"variables", { | 380 {"variables", |
376 {"season", Translate::AniList::ToString(season.season)}, | 381 { |
377 {"season_year", Strings::ToUtf8String(season.year)}, | 382 {"season", Translate::AniList::ToString(season.season)}, |
378 {"page", page}, | 383 {"season_year", Strings::ToUtf8String(season.year)}, |
379 }}, | 384 {"page", page}, |
385 } }, | |
380 }; | 386 }; |
381 | 387 |
382 const std::optional<nlohmann::json> res = SendJSONRequest(json); | 388 const std::optional<nlohmann::json> res = SendJSONRequest(json); |
383 if (!res) | 389 if (!res) |
384 return false; | 390 return false; |
385 | 391 |
386 const nlohmann::json& result = res.value(); | 392 const nlohmann::json &result = res.value(); |
387 | 393 |
388 for (const auto& media : result["/data/Page/media"_json_pointer].items()) | 394 for (const auto &media : result["/data/Page/media"_json_pointer].items()) |
389 ParseMediaJson(media.value()); | 395 ParseMediaJson(media.value()); |
390 | 396 |
391 has_next_page = JSON::GetBoolean(result, "/data/Page/pageInfo/hasNextPage"_json_pointer, false); | 397 has_next_page = JSON::GetBoolean(result, "/data/Page/pageInfo/hasNextPage"_json_pointer, false); |
392 if (has_next_page) | 398 if (has_next_page) |
393 page++; | 399 page++; |
394 } | 400 } |
395 | 401 |
396 return true; | 402 return true; |
397 } | 403 } |
398 | 404 |
399 int UpdateAnimeEntry(int id) { | 405 int UpdateAnimeEntry(int id) |
400 Anime::Anime& anime = Anime::db.items[id]; | 406 { |
407 Anime::Anime &anime = Anime::db.items[id]; | |
401 if (!anime.IsInUserList()) | 408 if (!anime.IsInUserList()) |
402 return 0; | 409 return 0; |
403 | 410 |
404 std::optional<std::string> service_id = anime.GetServiceId(Anime::Service::AniList); | 411 std::optional<std::string> service_id = anime.GetServiceId(Anime::Service::AniList); |
405 if (!service_id) | 412 if (!service_id) |
406 return 0; | 413 return 0; |
407 | 414 |
408 static constexpr std::string_view query = | 415 static constexpr std::string_view query = |
409 "mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String," | 416 "mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String," |
410 " $start: FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n" | 417 " $start: FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n" |
411 " SaveMediaListEntry (mediaId: $media_id, progress: $progress, status: $status, scoreRaw: $score," | 418 " SaveMediaListEntry (mediaId: $media_id, progress: $progress, status: $status, scoreRaw: $score," |
412 " notes: $notes, startedAt: $start, completedAt: $comp, repeat: $repeat) {\n" | 419 " notes: $notes, startedAt: $start, completedAt: $comp, repeat: $repeat) {\n" |
413 " id\n" | 420 " id\n" |
414 " }\n" | 421 " }\n" |
415 "}\n"; | 422 "}\n"; |
416 // clang-format off | 423 // clang-format off |
417 nlohmann::json json = { | 424 nlohmann::json json = { |
418 {"query", query}, | 425 {"query", query}, |
419 {"variables", { | 426 {"variables", { |
420 {"media_id", Strings::ToInt<int64_t>(service_id.value())}, | 427 {"media_id", Strings::ToInt<int64_t>(service_id.value())}, |
431 | 438 |
432 const std::optional<nlohmann::json> res = SendJSONRequest(json); | 439 const std::optional<nlohmann::json> res = SendJSONRequest(json); |
433 if (!res) | 440 if (!res) |
434 return 0; | 441 return 0; |
435 | 442 |
436 const nlohmann::json& result = res.value(); | 443 const nlohmann::json &result = res.value(); |
437 | 444 |
438 session.SetStatusBar(Strings::Translate("AniList: Anime entry updated successfully!")); | 445 session.SetStatusBar(Strings::Translate("AniList: Anime entry updated successfully!")); |
439 | 446 |
440 return JSON::GetNumber(result, "/data/SaveMediaListEntry/id"_json_pointer, 0); | 447 return JSON::GetNumber(result, "/data/SaveMediaListEntry/id"_json_pointer, 0); |
441 } | 448 } |
442 | 449 |
443 static int ParseUser(const nlohmann::json& json) { | 450 static int ParseUser(const nlohmann::json &json) |
444 auto& auth = session.config.auth.anilist; | 451 { |
452 auto &auth = session.config.auth.anilist; | |
445 | 453 |
446 return auth.user_id = JSON::GetNumber(json, "/id"_json_pointer, 0); | 454 return auth.user_id = JSON::GetNumber(json, "/id"_json_pointer, 0); |
447 } | 455 } |
448 | 456 |
449 bool AuthorizeUser() { | 457 bool AuthorizeUser() |
450 auto& auth = session.config.auth.anilist; | 458 { |
459 auto &auth = session.config.auth.anilist; | |
451 | 460 |
452 /* Prompt for PIN */ | 461 /* Prompt for PIN */ |
453 QDesktopServices::openUrl(QUrl(Strings::ToQString("https://anilist.co/api/v2/oauth/authorize?client_id=" + | 462 QDesktopServices::openUrl(QUrl(Strings::ToQString( |
454 std::string(CLIENT_ID) + "&response_type=token"))); | 463 "https://anilist.co/api/v2/oauth/authorize?client_id=" + std::string(CLIENT_ID) + "&response_type=token"))); |
455 | 464 |
456 bool ok; | 465 bool ok; |
457 QString token = QInputDialog::getText( | 466 QString token = QInputDialog::getText( |
458 0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal, | 467 0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal, |
459 "", &ok); | 468 "", &ok); |
460 | 469 |
461 if (!ok || token.isEmpty()) | 470 if (!ok || token.isEmpty()) |
462 return false; | 471 return false; |
463 | 472 |
464 auth.auth_token = Strings::ToUtf8String(token); | 473 auth.auth_token = Strings::ToUtf8String(token); |
465 | 474 |
466 session.SetStatusBar(Strings::Translate("AniList: Requesting user ID...")); | 475 session.SetStatusBar(Strings::Translate("AniList: Requesting user ID...")); |
467 | 476 |
468 static constexpr std::string_view query = | 477 static constexpr std::string_view query = "query {\n" |
469 "query {\n" | 478 " Viewer {\n" |
470 " Viewer {\n" | 479 " id\n" |
471 " id\n" | 480 " }\n" |
472 " }\n" | 481 "}\n"; |
473 "}\n"; | |
474 | 482 |
475 nlohmann::json json = { | 483 nlohmann::json json = { |
476 {"query", query} | 484 {"query", query} |
477 }; | 485 }; |
478 | 486 |
479 /* SendJSONRequest handles status errors */ | 487 /* SendJSONRequest handles status errors */ |
480 const std::optional<nlohmann::json> ret = SendJSONRequest(json); | 488 const std::optional<nlohmann::json> ret = SendJSONRequest(json); |
481 if (!ret) | 489 if (!ret) |
482 return 0; | 490 return 0; |
483 | 491 |
484 const nlohmann::json& result = ret.value(); | 492 const nlohmann::json &result = ret.value(); |
485 | 493 |
486 if (ParseUser(result["data"]["Viewer"])) | 494 if (ParseUser(result["data"]["Viewer"])) |
487 session.SetStatusBar(Strings::Translate("AniList: Successfully retrieved user data!")); | 495 session.SetStatusBar(Strings::Translate("AniList: Successfully retrieved user data!")); |
488 else | 496 else |
489 session.SetStatusBar(Strings::Translate("AniList: Failed to retrieve user ID!")); | 497 session.SetStatusBar(Strings::Translate("AniList: Failed to retrieve user ID!")); |
490 | 498 |
491 return true; | 499 return true; |
492 } | 500 } |
493 | 501 |
494 } // namespace AniList | 502 } // namespace AniList |
495 } // namespace Services | 503 } // namespace Services |