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