Mercurial > minori
comparison src/services/kitsu.cc @ 389:1e5d922fe82b
kitsu: implement UpdateAnimeEntry
yay... i guess
| author | Paper <paper@tflc.us> |
|---|---|
| date | Thu, 06 Nov 2025 12:21:35 -0500 |
| parents | 83aa0ddd1a46 |
| children | a72d6d7b3568 |
comparison
equal
deleted
inserted
replaced
| 388:83aa0ddd1a46 | 389:1e5d922fe82b |
|---|---|
| 137 } | 137 } |
| 138 | 138 |
| 139 /* ----------------------------------------------------------------------------- */ | 139 /* ----------------------------------------------------------------------------- */ |
| 140 | 140 |
| 141 static std::optional<nlohmann::json> SendJSONAPIRequest(const std::string &path, | 141 static std::optional<nlohmann::json> SendJSONAPIRequest(const std::string &path, |
| 142 const std::map<std::string, std::string> ¶ms = {}) | 142 const std::map<std::string, std::string> ¶ms = {}, |
| 143 const std::string &data = "", | |
| 144 HTTP::Type type = HTTP::Type::Get) | |
| 143 { | 145 { |
| 144 std::optional<std::string> token = AccountAccessToken(); | 146 std::optional<std::string> token = AccountAccessToken(); |
| 145 if (!token) | 147 if (!token) |
| 146 return std::nullopt; | 148 return std::nullopt; |
| 147 | 149 |
| 149 "Authorization: Bearer " + token.value(), | 151 "Authorization: Bearer " + token.value(), |
| 150 "Content-Type: application/vnd.api+json"}; | 152 "Content-Type: application/vnd.api+json"}; |
| 151 | 153 |
| 152 const std::string url = HTTP::EncodeParamsList(std::string(BASE_API_PATH) + path, params); | 154 const std::string url = HTTP::EncodeParamsList(std::string(BASE_API_PATH) + path, params); |
| 153 | 155 |
| 154 const std::string response = Strings::ToUtf8String(HTTP::Request(url, headers, "", HTTP::Type::Get)); | 156 const std::string response = Strings::ToUtf8String(HTTP::Request(url, headers, data, type)); |
| 155 if (response.empty()) | 157 if (response.empty()) |
| 156 return std::nullopt; | 158 return std::nullopt; |
| 157 | 159 |
| 158 nlohmann::json json; | 160 nlohmann::json json; |
| 159 try { | 161 try { |
| 163 fmt::format(Strings::Translate("Kitsu: Failed to parse response with error \"{}\""), ex.what())); | 165 fmt::format(Strings::Translate("Kitsu: Failed to parse response with error \"{}\""), ex.what())); |
| 164 return std::nullopt; | 166 return std::nullopt; |
| 165 } | 167 } |
| 166 | 168 |
| 167 if (json.contains("/errors"_json_pointer)) { | 169 if (json.contains("/errors"_json_pointer)) { |
| 170 std::cout << json["/errors"_json_pointer] << '\n'; | |
| 171 #if 0 | |
| 168 for (const auto &item : json["/errors"]) | 172 for (const auto &item : json["/errors"]) |
| 169 std::cerr << "Kitsu: API returned error \"" << json["/errors/title"_json_pointer] << "\" with detail \"" | 173 std::cerr << "Kitsu: API returned error \"" << json["/errors/title"_json_pointer] << "\" with detail \"" |
| 170 << json["/errors/detail"] << std::endl; | 174 << json["/errors/detail"] << std::endl; |
| 175 #endif | |
| 171 | 176 |
| 172 session.SetStatusBar(Strings::Translate("Kitsu: Request failed with errors!")); | 177 session.SetStatusBar(Strings::Translate("Kitsu: Request failed with errors!")); |
| 173 return std::nullopt; | 178 return std::nullopt; |
| 174 } | 179 } |
| 175 | 180 |
| 606 ParseAnimeJson(item); | 611 ParseAnimeJson(item); |
| 607 | 612 |
| 608 return true; | 613 return true; |
| 609 } | 614 } |
| 610 | 615 |
| 616 static std::string UserStatusToString(Anime::ListStatus status) | |
| 617 { | |
| 618 switch (status) { | |
| 619 case Anime::ListStatus::Planning: return "planned"; | |
| 620 case Anime::ListStatus::Completed: return "completed"; | |
| 621 case Anime::ListStatus::Dropped: return "dropped"; | |
| 622 case Anime::ListStatus::Paused: return "on_hold"; | |
| 623 default: break; | |
| 624 } | |
| 625 | |
| 626 return "current"; | |
| 627 } | |
| 628 | |
| 611 int UpdateAnimeEntry(int id) | 629 int UpdateAnimeEntry(int id) |
| 612 { | 630 { |
| 613 return 0; | 631 const Anime::Anime &anime = Anime::db.items[id]; |
| 632 int score; | |
| 633 | |
| 634 if (!anime.IsInUserList()) | |
| 635 return 0; /* WTF */ | |
| 636 | |
| 637 nlohmann::json json = { | |
| 638 {"data", { | |
| 639 {"type", "libraryEntries"}, | |
| 640 {"attributes", { | |
| 641 {"status", UserStatusToString(anime.GetUserStatus())}, | |
| 642 {"progress", anime.GetUserProgress()}, | |
| 643 {"reconsuming", anime.GetUserIsRewatching()}, | |
| 644 {"reconsumeCount", anime.GetUserRewatchedTimes()}, | |
| 645 {"notes", anime.GetUserNotes()}, | |
| 646 {"private", anime.GetUserIsPrivate()}, | |
| 647 // WTF is reactionSkipped? | |
| 648 {"startedAt", anime.GetUserDateStarted().GetAsISO8601()}, | |
| 649 {"finishedAt", anime.GetUserDateCompleted().GetAsISO8601()}, | |
| 650 }}, | |
| 651 {"relationships", { | |
| 652 {"anime", { | |
| 653 {"data", { | |
| 654 {"type", "anime"}, | |
| 655 {"id", anime.GetServiceId(Anime::Service::Kitsu)}, | |
| 656 }} | |
| 657 }}, | |
| 658 {"user", { | |
| 659 {"data", { | |
| 660 {"type", "users"}, | |
| 661 {"id", session.config.auth.kitsu.user_id}, | |
| 662 }} | |
| 663 }} | |
| 664 }} | |
| 665 }} | |
| 666 }; | |
| 667 | |
| 668 nlohmann::json &attributes = json["data"]["attributes"]; | |
| 669 | |
| 670 score = anime.GetUserScore() / 5; | |
| 671 if (score > 0) { | |
| 672 attributes["ratingTwenty"] = score; | |
| 673 } else { | |
| 674 attributes["ratingTwenty"] = nullptr; | |
| 675 } | |
| 676 | |
| 677 /* I really don't like this */ | |
| 678 std::string uid = anime.GetUserId(); | |
| 679 | |
| 680 std::string path = "/library-entries"; | |
| 681 HTTP::Type type; | |
| 682 | |
| 683 if (!uid.empty()) { | |
| 684 json["data"]["id"] = uid; | |
| 685 path = path + "/" + uid; | |
| 686 type = HTTP::Type::Patch; | |
| 687 } else { | |
| 688 type = HTTP::Type::Post; | |
| 689 } | |
| 690 | |
| 691 std::optional<nlohmann::json> res = SendJSONAPIRequest(path, {}, json.dump(), type); | |
| 692 if (!res) | |
| 693 return 0; | |
| 694 | |
| 695 /* TODO parse result; can reduces races */ | |
| 696 | |
| 697 session.SetStatusBar(Strings::Translate("Kitsu: Anime entry updated successfully!")); | |
| 698 | |
| 699 /* I guess? */ | |
| 700 return Strings::ToInt<int>(anime.GetUserId()); | |
| 614 } | 701 } |
| 615 | 702 |
| 616 bool AuthorizeUser(const std::string &email, const std::string &password) | 703 bool AuthorizeUser(const std::string &email, const std::string &password) |
| 617 { | 704 { |
| 618 const nlohmann::json body = { | 705 const nlohmann::json body = { |
