Mercurial > minori
comparison src/services/anilist.cc @ 413:192da585a0a8 default tip
*: fix formatting
the clang-format file was broken andjust using LLVM defaults.
now it's fixed and is more in line with what I actually prefer my
code to look like -- esp. with regards to tabulators
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sat, 25 Jul 2026 14:54:32 -0400 |
| parents | 02a670a8e1c4 |
| children |
comparison
equal
deleted
inserted
replaced
| 412:05aed03e0111 | 413:192da585a0a8 |
|---|---|
| 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 { |
| 75 std::vector<std::string> headers = { | 75 std::vector<std::string> headers = { |
| 76 "Accept: application/json", | 76 "Accept: application/json", |
| 77 "Content-Type: application/json", | 77 "Content-Type: application/json", |
| 78 }; | 78 }; |
| 79 | 79 |
| 80 if (!session.config.auth.anilist.auth_token.empty()) | 80 if (!session.config.auth.anilist.auth_token.empty()) |
| 81 headers.push_back("Authorization: Bearer " + session.config.auth.anilist.auth_token); | 81 headers.push_back("Authorization: Bearer " + session.config.auth.anilist.auth_token); |
| 82 | 82 |
| 83 const std::string response = | 83 const std::string response = |
| 84 Strings::ToUtf8String(HTTP::Request("https://graphql.anilist.co", headers, data.dump(), HTTP::Type::Post)); | 84 Strings::ToUtf8String(HTTP::Request("https://graphql.anilist.co", headers, data.dump(), HTTP::Type::Post)); |
| 85 if (response.empty()) { | 85 if (response.empty()) { |
| 86 session.SetStatusBar(Strings::Translate("AniList: JSON request returned an empty result!")); | 86 session.SetStatusBar(Strings::Translate("AniList: JSON request returned an empty result!")); |
| 87 return std::nullopt; | 87 return std::nullopt; |
| 88 } | 88 } |
| 89 | 89 |
| 91 | 91 |
| 92 try { | 92 try { |
| 93 out = nlohmann::json::parse(response); | 93 out = nlohmann::json::parse(response); |
| 94 } catch (const std::exception &ex) { | 94 } catch (const std::exception &ex) { |
| 95 session.SetStatusBar( | 95 session.SetStatusBar( |
| 96 fmt::format(Strings::Translate("AniList: Failed to parse request JSON with error \"{}\"!"), ex.what())); | 96 fmt::format(Strings::Translate("AniList: Failed to parse request JSON with error \"{}\"!"), ex.what())); |
| 97 return std::nullopt; | 97 return std::nullopt; |
| 98 } | 98 } |
| 99 | 99 |
| 100 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()) { |
| 101 for (const auto &error : out.at("/errors"_json_pointer)) | 101 for (const auto &error : out.at("/errors"_json_pointer)) |
| 102 std::cerr << "AniList: Received an error in response: " | 102 std::cerr << "AniList: Received an error in response: " |
| 103 << JSON::GetString<std::string>(error, "/message"_json_pointer, "") << std::endl; | 103 << JSON::GetString<std::string>(error, "/message"_json_pointer, "") << std::endl; |
| 104 | 104 |
| 105 session.SetStatusBar(Strings::Translate("AniList: Received an error in response!")); | 105 session.SetStatusBar(Strings::Translate("AniList: Received an error in response!")); |
| 106 return std::nullopt; | 106 return std::nullopt; |
| 107 } | 107 } |
| 108 | 108 |
| 110 } | 110 } |
| 111 | 111 |
| 112 static void ParseListStatus(std::string status, Anime::Anime &anime) | 112 static void ParseListStatus(std::string status, Anime::Anime &anime) |
| 113 { | 113 { |
| 114 static const std::unordered_map<std::string, Anime::ListStatus> map = { | 114 static const std::unordered_map<std::string, Anime::ListStatus> map = { |
| 115 {"CURRENT", Anime::ListStatus::Current }, | 115 {"CURRENT", Anime::ListStatus::Current }, |
| 116 {"PLANNING", Anime::ListStatus::Planning }, | 116 {"PLANNING", Anime::ListStatus::Planning }, |
| 117 {"COMPLETED", Anime::ListStatus::Completed}, | 117 {"COMPLETED", Anime::ListStatus::Completed}, |
| 118 {"DROPPED", Anime::ListStatus::Dropped }, | 118 {"DROPPED", Anime::ListStatus::Dropped }, |
| 119 {"PAUSED", Anime::ListStatus::Paused } | 119 {"PAUSED", Anime::ListStatus::Paused } |
| 120 }; | 120 }; |
| 121 | 121 |
| 122 if (status == "REPEATING") { | 122 if (status == "REPEATING") { |
| 123 anime.SetUserIsRewatching(true); | 123 anime.SetUserIsRewatching(true); |
| 124 anime.SetUserStatus(Anime::ListStatus::Current); | 124 anime.SetUserStatus(Anime::ListStatus::Current); |
| 154 } | 154 } |
| 155 | 155 |
| 156 static void ParseTitle(const nlohmann::json &json, Anime::Anime &anime) | 156 static void ParseTitle(const nlohmann::json &json, Anime::Anime &anime) |
| 157 { | 157 { |
| 158 static const std::unordered_map<Anime::TitleLanguage, nlohmann::json::json_pointer> map = { | 158 static const std::unordered_map<Anime::TitleLanguage, nlohmann::json::json_pointer> map = { |
| 159 {Anime::TitleLanguage::Native, "/native"_json_pointer }, | 159 {Anime::TitleLanguage::Native, "/native"_json_pointer }, |
| 160 {Anime::TitleLanguage::English, "/english"_json_pointer}, | 160 {Anime::TitleLanguage::English, "/english"_json_pointer}, |
| 161 {Anime::TitleLanguage::Romaji, "/romaji"_json_pointer }, | 161 {Anime::TitleLanguage::Romaji, "/romaji"_json_pointer }, |
| 162 }; | 162 }; |
| 163 | 163 |
| 164 for (const auto &[language, ptr] : map) | 164 for (const auto &[language, ptr] : map) |
| 165 if (json.contains(ptr) && json[ptr].is_string()) | 165 if (json.contains(ptr) && json[ptr].is_string()) |
| 166 anime.SetTitle(language, json[ptr]); | 166 anime.SetTitle(language, json[ptr]); |
| 192 | 192 |
| 193 anime.SetEpisodes(JSON::GetNumber(json, "/episodes"_json_pointer, 0)); | 193 anime.SetEpisodes(JSON::GetNumber(json, "/episodes"_json_pointer, 0)); |
| 194 anime.SetFormat(Translate::AniList::ToSeriesFormat(JSON::GetString<std::string>(json, "/format"_json_pointer, ""))); | 194 anime.SetFormat(Translate::AniList::ToSeriesFormat(JSON::GetString<std::string>(json, "/format"_json_pointer, ""))); |
| 195 | 195 |
| 196 anime.SetAiringStatus( | 196 anime.SetAiringStatus( |
| 197 Translate::AniList::ToSeriesStatus(JSON::GetString<std::string>(json, "/status"_json_pointer, ""))); | 197 Translate::AniList::ToSeriesStatus(JSON::GetString<std::string>(json, "/status"_json_pointer, ""))); |
| 198 | 198 |
| 199 if (json.contains("/startDate"_json_pointer) && json["/startDate"_json_pointer].is_object()) | 199 if (json.contains("/startDate"_json_pointer) && json["/startDate"_json_pointer].is_object()) |
| 200 anime.SetStartedDate(Date(json["/startDate"_json_pointer])); | 200 anime.SetStartedDate(Date(json["/startDate"_json_pointer])); |
| 201 | 201 |
| 202 anime.SetCompletedDate(json.contains("/endDate"_json_pointer) && json["/endDate"_json_pointer].is_object() | 202 anime.SetCompletedDate(json.contains("/endDate"_json_pointer) && json["/endDate"_json_pointer].is_object() |
| 203 ? Date(json["/endDate"_json_pointer]) | 203 ? Date(json["/endDate"_json_pointer]) |
| 204 : anime.GetStartedDate()); | 204 : anime.GetStartedDate()); |
| 205 | 205 |
| 206 anime.SetPosterUrl(JSON::GetString<std::string>(json, "/coverImage/large"_json_pointer, "")); | 206 anime.SetPosterUrl(JSON::GetString<std::string>(json, "/coverImage/large"_json_pointer, "")); |
| 207 | 207 |
| 208 anime.SetAudienceScore(JSON::GetNumber(json, "/averageScore"_json_pointer, 0)); | 208 anime.SetAudienceScore(JSON::GetNumber(json, "/averageScore"_json_pointer, 0)); |
| 209 // anime.SetSeason(Translate::AniList::ToSeriesSeason(JSON::GetString<std::string>(json, "/season"_json_pointer, | 209 // anime.SetSeason(Translate::AniList::ToSeriesSeason(JSON::GetString<std::string>(json, "/season"_json_pointer, |
| 268 int GetAnimeList() | 268 int GetAnimeList() |
| 269 { | 269 { |
| 270 auto &auth = session.config.auth.anilist; | 270 auto &auth = session.config.auth.anilist; |
| 271 | 271 |
| 272 static constexpr std::string_view query = "query ($id: Int) {\n" | 272 static constexpr std::string_view query = "query ($id: Int) {\n" |
| 273 " MediaListCollection (userId: $id, type: ANIME) {\n" | 273 " MediaListCollection (userId: $id, type: ANIME) {\n" |
| 274 " lists {\n" | 274 " lists {\n" |
| 275 " name\n" | 275 " name\n" |
| 276 " entries {\n" | 276 " entries {\n" |
| 277 " score\n" | 277 " score\n" |
| 278 " notes\n" | 278 " notes\n" |
| 279 " status\n" | 279 " status\n" |
| 280 " progress\n" | 280 " progress\n" |
| 281 " startedAt {\n" | 281 " startedAt {\n" |
| 282 " year\n" | 282 " year\n" |
| 283 " month\n" | 283 " month\n" |
| 284 " day\n" | 284 " day\n" |
| 285 " }\n" | 285 " }\n" |
| 286 " completedAt {\n" | 286 " completedAt {\n" |
| 287 " year\n" | 287 " year\n" |
| 288 " month\n" | 288 " month\n" |
| 289 " day\n" | 289 " day\n" |
| 290 " }\n" | 290 " }\n" |
| 291 " updatedAt\n" | 291 " updatedAt\n" |
| 292 " media {\n" MEDIA_FIELDS " }\n" | 292 " media {\n" MEDIA_FIELDS " }\n" |
| 293 " }\n" | 293 " }\n" |
| 294 " }\n" | 294 " }\n" |
| 295 " }\n" | 295 " }\n" |
| 296 "}\n"; | 296 "}\n"; |
| 297 | 297 |
| 298 // clang-format off | 298 // clang-format off |
| 299 nlohmann::json request = { | 299 nlohmann::json request = { |
| 300 {"query", query}, | 300 {"query", query}, |
| 301 {"variables", { | 301 {"variables", { |
| 328 | 328 |
| 329 /* return is a vector of anime ids */ | 329 /* return is a vector of anime ids */ |
| 330 std::vector<int> Search(const std::string &search) | 330 std::vector<int> Search(const std::string &search) |
| 331 { | 331 { |
| 332 static constexpr std::string_view query = "query ($search: String) {\n" | 332 static constexpr std::string_view query = "query ($search: String) {\n" |
| 333 " Page (page: 1, perPage: 50) {\n" | 333 " Page (page: 1, perPage: 50) {\n" |
| 334 " media (search: $search, type: ANIME) {\n" MEDIA_FIELDS " }\n" | 334 " media (search: $search, type: ANIME) {\n" MEDIA_FIELDS " }\n" |
| 335 " }\n" | 335 " }\n" |
| 336 "}\n"; | 336 "}\n"; |
| 337 | 337 |
| 338 // clang-format off | 338 // clang-format off |
| 339 nlohmann::json json = { | 339 nlohmann::json json = { |
| 340 {"query", query}, | 340 {"query", query}, |
| 341 {"variables", { | 341 {"variables", { |
| 361 } | 361 } |
| 362 | 362 |
| 363 bool GetSeason(Anime::Season season) | 363 bool GetSeason(Anime::Season season) |
| 364 { | 364 { |
| 365 static constexpr std::string_view query = | 365 static constexpr std::string_view query = |
| 366 "query ($season: MediaSeason!, $season_year: Int!, $page: Int) {\n" | 366 "query ($season: MediaSeason!, $season_year: Int!, $page: Int) {\n" |
| 367 " Page(page: $page) {\n" | 367 " Page(page: $page) {\n" |
| 368 " media(season: $season, seasonYear: $season_year, type: ANIME, sort: START_DATE) {\n" MEDIA_FIELDS " }\n" | 368 " media(season: $season, seasonYear: $season_year, type: ANIME, sort: START_DATE) {\n" MEDIA_FIELDS " }\n" |
| 369 " pageInfo {\n" | 369 " pageInfo {\n" |
| 370 " total\n" | 370 " total\n" |
| 371 " perPage\n" | 371 " perPage\n" |
| 372 " currentPage\n" | 372 " currentPage\n" |
| 373 " lastPage\n" | 373 " lastPage\n" |
| 374 " hasNextPage\n" | 374 " hasNextPage\n" |
| 375 " }\n" | 375 " }\n" |
| 376 " }\n" | 376 " }\n" |
| 377 "}\n"; | 377 "}\n"; |
| 378 | 378 |
| 379 int page = 0; | 379 int page = 0; |
| 380 bool has_next_page = true; | 380 bool has_next_page = true; |
| 381 | 381 |
| 382 while (has_next_page) { | 382 while (has_next_page) { |
| 383 nlohmann::json json = { | 383 nlohmann::json json = { |
| 384 {"query", query}, | 384 {"query", query}, |
| 385 {"variables", | 385 {"variables", |
| 386 { | 386 { |
| 387 {"season", Translate::AniList::ToString(season.season)}, | 387 {"season", Translate::AniList::ToString(season.season)}, |
| 388 {"season_year", Strings::ToUtf8String(season.year)}, | 388 {"season_year", Strings::ToUtf8String(season.year)}, |
| 389 {"page", page}, | 389 {"page", page}, |
| 390 } }, | 390 } }, |
| 391 }; | 391 }; |
| 392 | 392 |
| 393 const std::optional<nlohmann::json> res = SendJSONRequest(json); | 393 const std::optional<nlohmann::json> res = SendJSONRequest(json); |
| 394 if (!res) | 394 if (!res) |
| 395 return false; | 395 return false; |
| 416 std::optional<std::string> service_id = anime.GetServiceId(Anime::Service::AniList); | 416 std::optional<std::string> service_id = anime.GetServiceId(Anime::Service::AniList); |
| 417 if (!service_id) | 417 if (!service_id) |
| 418 return 0; | 418 return 0; |
| 419 | 419 |
| 420 static constexpr std::string_view query = | 420 static constexpr std::string_view query = |
| 421 "mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String," | 421 "mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String," |
| 422 " $start: FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n" | 422 " $start: FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n" |
| 423 " SaveMediaListEntry (mediaId: $media_id, progress: $progress, status: $status, scoreRaw: $score," | 423 " SaveMediaListEntry (mediaId: $media_id, progress: $progress, status: $status, scoreRaw: $score," |
| 424 " notes: $notes, startedAt: $start, completedAt: $comp, repeat: $repeat) {\n" | 424 " notes: $notes, startedAt: $start, completedAt: $comp, repeat: $repeat) {\n" |
| 425 " id\n" | 425 " id\n" |
| 426 " }\n" | 426 " }\n" |
| 427 "}\n"; | 427 "}\n"; |
| 428 // clang-format off | 428 // clang-format off |
| 429 nlohmann::json json = { | 429 nlohmann::json json = { |
| 430 {"query", query}, | 430 {"query", query}, |
| 431 {"variables", { | 431 {"variables", { |
| 432 {"media_id", Strings::ToInt<int64_t>(service_id.value())}, | 432 {"media_id", Strings::ToInt<int64_t>(service_id.value())}, |
| 463 { | 463 { |
| 464 auto &auth = session.config.auth.anilist; | 464 auto &auth = session.config.auth.anilist; |
| 465 | 465 |
| 466 /* Prompt for PIN */ | 466 /* Prompt for PIN */ |
| 467 QDesktopServices::openUrl(QUrl(Strings::ToQString( | 467 QDesktopServices::openUrl(QUrl(Strings::ToQString( |
| 468 "https://anilist.co/api/v2/oauth/authorize?client_id=" + std::string(CLIENT_ID) + "&response_type=token"))); | 468 "https://anilist.co/api/v2/oauth/authorize?client_id=" + std::string(CLIENT_ID) + "&response_type=token"))); |
| 469 | 469 |
| 470 bool ok; | 470 bool ok; |
| 471 QString token = QInputDialog::getText( | 471 QString token = QInputDialog::getText(0, "Credentials needed!", |
| 472 0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal, | 472 "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal, "", &ok); |
| 473 "", &ok); | |
| 474 | 473 |
| 475 if (!ok || token.isEmpty()) | 474 if (!ok || token.isEmpty()) |
| 476 return false; | 475 return false; |
| 477 | 476 |
| 478 auth.auth_token = Strings::ToUtf8String(token); | 477 auth.auth_token = Strings::ToUtf8String(token); |
| 479 | 478 |
| 480 session.SetStatusBar(Strings::Translate("AniList: Requesting user ID...")); | 479 session.SetStatusBar(Strings::Translate("AniList: Requesting user ID...")); |
| 481 | 480 |
| 482 static constexpr std::string_view query = "query {\n" | 481 static constexpr std::string_view query = "query {\n" |
| 483 " Viewer {\n" | 482 " Viewer {\n" |
| 484 " id\n" | 483 " id\n" |
| 485 " }\n" | 484 " }\n" |
| 486 "}\n"; | 485 "}\n"; |
| 487 | 486 |
| 488 nlohmann::json json = { | 487 nlohmann::json json = { |
| 489 {"query", query} | 488 {"query", query} |
| 490 }; | 489 }; |
| 491 | 490 |
| 492 /* SendJSONRequest handles status errors */ | 491 /* SendJSONRequest handles status errors */ |
| 493 const std::optional<nlohmann::json> ret = SendJSONRequest(json); | 492 const std::optional<nlohmann::json> ret = SendJSONRequest(json); |
| 494 if (!ret) | 493 if (!ret) |
