Mercurial > minori
view src/core/anime_db.cc @ 114:ab191e28e69d
*: add initial torrent stuff
WOAH!
these checkboxes are a pain in my fucking ass
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Tue, 07 Nov 2023 08:03:42 -0500 |
parents | d02fdf1d6708 |
children | de0a8d2f28b3 |
line wrap: on
line source
#include "core/anime_db.h" #include "core/anime.h" #include "core/strings.h" #include <QDebug> namespace Anime { int Database::GetTotalAnimeAmount() { int total = 0; for (const auto& a : items) { if (a.second.IsInUserList()) total++; } return total; } int Database::GetListsAnimeAmount(ListStatus status) { if (status == ListStatus::NOT_IN_LIST) return 0; int total = 0; for (const auto& a : items) { if (a.second.IsInUserList() && a.second.GetUserStatus() == status) total++; } return total; } int Database::GetTotalEpisodeAmount() { int total = 0; for (const auto& a : items) { if (a.second.IsInUserList()) { total += a.second.GetUserRewatchedTimes() * a.second.GetEpisodes(); total += a.second.GetUserProgress(); } } return total; } /* Returns the total watched amount in minutes. */ int Database::GetTotalWatchedAmount() { int total = 0; for (const auto& a : items) { if (a.second.IsInUserList()) { total += a.second.GetDuration() * a.second.GetUserProgress(); total += a.second.GetEpisodes() * a.second.GetDuration() * a.second.GetUserRewatchedTimes(); } } return total; } /* Returns the total planned amount in minutes. Note that we should probably limit progress to the amount of episodes, as AniList will let you set episode counts up to 32768. But that should rather be handled elsewhere. */ int Database::GetTotalPlannedAmount() { int total = 0; for (const auto& a : items) { if (a.second.IsInUserList()) total += a.second.GetDuration() * (a.second.GetEpisodes() - a.second.GetUserProgress()); } return total; } /* In Taiga this is called a mean, but "average" is what's primarily used in conversation, at least in the U.S. */ double Database::GetAverageScore() { double avg = 0; int amt = 0; for (const auto& a : items) { if (a.second.IsInUserList() && a.second.GetUserScore()) { avg += a.second.GetUserScore(); amt++; } } return avg / amt; } double Database::GetScoreDeviation() { double squares_sum = 0, avg = GetAverageScore(); int amt = 0; for (const auto& a : items) { if (a.second.IsInUserList() && a.second.GetUserScore()) { squares_sum += std::pow(static_cast<double>(a.second.GetUserScore()) - avg, 2); amt++; } } return (amt > 0) ? std::sqrt(squares_sum / amt) : 0; } template <typename T, typename U> static T get_lowest_in_map(const std::unordered_map<T, U>& map) { if (map.size() <= 0) return 0; T id; U ret = std::numeric_limits<U>::max(); for (const auto& t : map) { if (t.second < ret) { ret = t.second; id = t.first; } } return id; } /* This is really fugly but WHO CARES :P This sort of ""advanced"" algorithm is only in effect because there are some special cases, e.g. Another and Re:ZERO, where we get the wrong match, so we have to create Advanced Techniques to solve this This algorithm: 1. searches each anime item for a match to the preferred title AND all synonyms and marks those matches with `synonym.length() - (synonym.find(needle) + needle.length());` which, on a title that exactly matches, will be 0 2. returns the id of the match that is the lowest, which will most definitely match anything that exactly matches the title of the filename */ int Database::GetAnimeFromTitle(const std::string& title) { if (title.empty()) return 0; std::unordered_map<int, long long> map; for (const auto& a : items) { long long ret = a.second.GetUserPreferredTitle().find(title); if (ret != static_cast<long long>(std::string::npos)) { map[a.second.GetId()] = a.second.GetUserPreferredTitle().length() - (ret + title.length()); continue; } for (const auto& synonym : a.second.GetTitleSynonyms()) { ret = synonym.find(title); if (ret != static_cast<long long>(std::string::npos)) { map[a.second.GetId()] = synonym.length() - (ret + title.length()); continue; } } } return get_lowest_in_map(map); } Database db; } // namespace Anime