Mercurial > minori
comparison src/core/anime_db.cc @ 319:d928ec7b6a0d
services/kitsu: implement GetAnimeList()
it finally works!
| author | Paper <paper@paper.us.eu.org> |
|---|---|
| date | Wed, 12 Jun 2024 17:52:26 -0400 |
| parents | b1f4d1867ab1 |
| children | 1b5c04268d6a |
comparison
equal
deleted
inserted
replaced
| 318:3b355fa948c7 | 319:d928ec7b6a0d |
|---|---|
| 1 #include "core/anime_db.h" | 1 #include "core/anime_db.h" |
| 2 #include "core/anime.h" | 2 #include "core/anime.h" |
| 3 #include "core/filesystem.h" | 3 #include "core/filesystem.h" |
| 4 #include "core/json.h" | 4 #include "core/json.h" |
| 5 #include "core/session.h" | |
| 5 #include "core/strings.h" | 6 #include "core/strings.h" |
| 6 | 7 |
| 7 #include "gui/translate/anilist.h" | 8 #include "gui/translate/anilist.h" |
| 8 #include "gui/translate/anime.h" | 9 #include "gui/translate/anime.h" |
| 9 | 10 |
| 16 #include <iostream> | 17 #include <iostream> |
| 17 #include <random> | 18 #include <random> |
| 18 | 19 |
| 19 namespace Anime { | 20 namespace Anime { |
| 20 | 21 |
| 21 size_t Database::GetTotalAnimeAmount() { | 22 size_t Database::GetTotalAnimeAmount() const { |
| 22 size_t total = 0; | 23 size_t total = 0; |
| 23 | 24 |
| 24 for (const auto& [id, anime] : items) | 25 for (const auto& [id, anime] : items) |
| 25 if (anime.IsInUserList()) | 26 if (anime.IsInUserList()) |
| 26 total++; | 27 total++; |
| 27 | 28 |
| 28 return total; | 29 return total; |
| 29 } | 30 } |
| 30 | 31 |
| 31 size_t Database::GetListsAnimeAmount(ListStatus status) { | 32 size_t Database::GetListsAnimeAmount(ListStatus status) const { |
| 32 if (status == ListStatus::NotInList) | 33 if (status == ListStatus::NotInList) |
| 33 return 0; | 34 return 0; |
| 34 | 35 |
| 35 size_t total = 0; | 36 size_t total = 0; |
| 36 | 37 |
| 39 total++; | 40 total++; |
| 40 | 41 |
| 41 return total; | 42 return total; |
| 42 } | 43 } |
| 43 | 44 |
| 44 size_t Database::GetTotalEpisodeAmount() { | 45 size_t Database::GetTotalEpisodeAmount() const { |
| 45 size_t total = 0; | 46 size_t total = 0; |
| 46 | 47 |
| 47 for (const auto& [id, anime] : items) | 48 for (const auto& [id, anime] : items) |
| 48 if (anime.IsInUserList()) | 49 if (anime.IsInUserList()) |
| 49 total += anime.GetUserRewatchedTimes() * anime.GetEpisodes() + anime.GetUserProgress(); | 50 total += anime.GetUserRewatchedTimes() * anime.GetEpisodes() + anime.GetUserProgress(); |
| 50 | 51 |
| 51 return total; | 52 return total; |
| 52 } | 53 } |
| 53 | 54 |
| 54 /* Returns the total watched amount in minutes. */ | 55 /* Returns the total watched amount in minutes. */ |
| 55 size_t Database::GetTotalWatchedAmount() { | 56 size_t Database::GetTotalWatchedAmount() const { |
| 56 size_t total = 0; | 57 size_t total = 0; |
| 57 | 58 |
| 58 for (const auto& [id, anime] : items) | 59 for (const auto& [id, anime] : items) |
| 59 if (anime.IsInUserList()) | 60 if (anime.IsInUserList()) |
| 60 total += anime.GetDuration() * anime.GetUserProgress() + | 61 total += anime.GetDuration() * anime.GetUserProgress() + |
| 66 /* Returns the total planned amount in minutes. | 67 /* Returns the total planned amount in minutes. |
| 67 Note that we should probably limit progress to the | 68 Note that we should probably limit progress to the |
| 68 amount of episodes, as AniList will let you | 69 amount of episodes, as AniList will let you |
| 69 set episode counts up to 32768. But that should | 70 set episode counts up to 32768. But that should |
| 70 rather be handled elsewhere. */ | 71 rather be handled elsewhere. */ |
| 71 size_t Database::GetTotalPlannedAmount() { | 72 size_t Database::GetTotalPlannedAmount() const { |
| 72 size_t total = 0; | 73 size_t total = 0; |
| 73 | 74 |
| 74 for (const auto& [id, anime] : items) | 75 for (const auto& [id, anime] : items) |
| 75 if (anime.IsInUserList()) | 76 if (anime.IsInUserList()) |
| 76 total += anime.GetDuration() * (anime.GetEpisodes() - anime.GetUserProgress()); | 77 total += anime.GetDuration() * (anime.GetEpisodes() - anime.GetUserProgress()); |
| 79 } | 80 } |
| 80 | 81 |
| 81 /* In Taiga this is called the mean, but "average" is | 82 /* In Taiga this is called the mean, but "average" is |
| 82 what's primarily used in conversation, at least | 83 what's primarily used in conversation, at least |
| 83 in the U.S. */ | 84 in the U.S. */ |
| 84 double Database::GetAverageScore() { | 85 double Database::GetAverageScore() const { |
| 85 double avg = 0; | 86 double avg = 0; |
| 86 size_t amt = 0; | 87 size_t amt = 0; |
| 87 | 88 |
| 88 for (const auto& [id, anime] : items) { | 89 for (const auto& [id, anime] : items) { |
| 89 if (anime.IsInUserList() && anime.GetUserScore()) { | 90 if (anime.IsInUserList() && anime.GetUserScore()) { |
| 92 } | 93 } |
| 93 } | 94 } |
| 94 return avg / amt; | 95 return avg / amt; |
| 95 } | 96 } |
| 96 | 97 |
| 97 double Database::GetScoreDeviation() { | 98 double Database::GetScoreDeviation() const { |
| 98 double squares_sum = 0, avg = GetAverageScore(); | 99 double squares_sum = 0, avg = GetAverageScore(); |
| 99 size_t amt = 0; | 100 size_t amt = 0; |
| 100 | 101 |
| 101 for (const auto& [id, anime] : items) { | 102 for (const auto& [id, anime] : items) { |
| 102 if (anime.IsInUserList() && anime.GetUserScore()) { | 103 if (anime.IsInUserList() && anime.GetUserScore()) { |
| 106 } | 107 } |
| 107 | 108 |
| 108 return (amt > 0) ? std::sqrt(squares_sum / amt) : 0; | 109 return (amt > 0) ? std::sqrt(squares_sum / amt) : 0; |
| 109 } | 110 } |
| 110 | 111 |
| 111 /* | 112 int Database::LookupAnimeTitle(const std::string& title) const { |
| 112 * TODO: separate this from the anime DB, | |
| 113 * provide *some* sort of normalization | |
| 114 */ | |
| 115 int Database::GetAnimeFromTitle(const std::string& title) { | |
| 116 if (title.empty()) | 113 if (title.empty()) |
| 117 return 0; | 114 return 0; |
| 118 | 115 |
| 119 std::string title_n(title); | 116 std::string title_n(title); |
| 120 Strings::NormalizeAnimeTitle(title_n); | 117 Strings::NormalizeAnimeTitle(title_n); |
| 192 json.push_back({"list_data", user}); | 189 json.push_back({"list_data", user}); |
| 193 | 190 |
| 194 return true; | 191 return true; |
| 195 } | 192 } |
| 196 | 193 |
| 197 bool Database::GetDatabaseAsJSON(nlohmann::json& json) { | 194 bool Database::GetDatabaseAsJSON(nlohmann::json& json) const { |
| 198 for (const auto& [id, anime] : items) { | 195 for (const auto& [id, anime] : items) { |
| 199 nlohmann::json anime_json = {}; | 196 nlohmann::json anime_json = {}; |
| 200 GetAnimeAsJSON(anime, anime_json); | 197 GetAnimeAsJSON(anime, anime_json); |
| 201 json.push_back(anime_json); | 198 json.push_back(anime_json); |
| 202 } | 199 } |
| 203 | 200 |
| 204 return true; | 201 return true; |
| 205 } | 202 } |
| 206 | 203 |
| 207 bool Database::SaveDatabaseToDisk() { | 204 bool Database::SaveDatabaseToDisk() const { |
| 208 std::filesystem::path db_path = Filesystem::GetAnimeDBPath(); | 205 std::filesystem::path db_path = Filesystem::GetAnimeDBPath(); |
| 209 Filesystem::CreateDirectories(db_path); | 206 Filesystem::CreateDirectories(db_path); |
| 210 | 207 |
| 211 std::ofstream db_file(db_path); | 208 std::ofstream db_file(db_path); |
| 212 if (!db_file) | 209 if (!db_file) |
| 300 return false; | 297 return false; |
| 301 | 298 |
| 302 return true; | 299 return true; |
| 303 } | 300 } |
| 304 | 301 |
| 305 int Database::GetUnusedId() { | 302 int Database::GetUnusedId() const { |
| 306 /* TODO: move these out of here */ | |
| 307 | |
| 308 std::random_device rd; | |
| 309 std::mt19937 gen(rd()); | |
| 310 std::uniform_int_distribution<int> distrib(1, INT_MAX); | 303 std::uniform_int_distribution<int> distrib(1, INT_MAX); |
| 311 int res; | 304 int res; |
| 312 | 305 |
| 313 do { | 306 do { |
| 314 res = distrib(gen); | 307 res = distrib(session.gen); |
| 315 } while (items.count(res)); | 308 } while (items.count(res) && !res); |
| 316 | 309 |
| 317 return res; | 310 return res; |
| 318 } | 311 } |
| 319 | 312 |
| 320 int Database::LookupServiceId(Service service, const std::string& id_to_find) { | 313 int Database::LookupServiceId(Service service, const std::string& id_to_find) const { |
| 321 for (const auto& [id, anime] : items) { | 314 for (const auto& [id, anime] : items) { |
| 322 std::optional<std::string> service_id = anime.GetServiceId(service); | 315 std::optional<std::string> service_id = anime.GetServiceId(service); |
| 323 if (!service_id) | 316 if (!service_id) |
| 324 continue; | 317 continue; |
| 325 | 318 |
| 328 } | 321 } |
| 329 | 322 |
| 330 return 0; | 323 return 0; |
| 331 } | 324 } |
| 332 | 325 |
| 333 int Database::LookupServiceIdOrUnused(Service service, const std::string& id_to_find) { | 326 int Database::LookupServiceIdOrUnused(Service service, const std::string& id_to_find) const { |
| 334 int id = LookupServiceId(service, id_to_find); | 327 int id = LookupServiceId(service, id_to_find); |
| 335 if (id) | 328 if (id) |
| 336 return id; | 329 return id; |
| 337 | 330 |
| 338 return GetUnusedId(); | 331 return GetUnusedId(); |
| 339 } | 332 } |
| 340 | 333 |
| 334 void Database::RemoveAllUserData() { | |
| 335 for (auto& [id, anime] : items) { | |
| 336 if (anime.IsInUserList()) | |
| 337 anime.RemoveFromUserList(); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 Database db; | 341 Database db; |
| 342 | 342 |
| 343 } // namespace Anime | 343 } // namespace Anime |
