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 |