Mercurial > minori
view src/library/library.cc @ 410:eb554255ea5f default tip
*: no crash
| author | Paper <paper@tflc.us> |
|---|---|
| date | Thu, 02 Apr 2026 01:09:09 -0400 |
| parents | 8d06825d96d1 |
| children |
line wrap: on
line source
#include "library/library.h" #include "core/anime_db.h" #include "core/session.h" #include "core/strings.h" #include "anitomy/anitomy.h" #include <filesystem> #include <string> #include <unordered_map> #include <iostream> namespace Library { Database::Database() { /* Do this immediately :) */ /* Nevermind, this causes an immediate segfault on Windows UpdateWatchers(); */ /* Also, this would be completely useless as the paths in * the config are not filled until after main() is * called. */ } Database::~Database() { /* Wait */ if (refresh_thread_.joinable()) refresh_thread_.join(); } void Database::UpdateWatchers() { /* TODO also need to remove unused watchers */ for (const auto &p : session.config.library.paths) { if (watchers_.count(p)) continue; watchers_[p].reset( Filesystem::GetRecursiveFilesystemWatcher(reinterpret_cast<void *>(this), p, Database::StaticEventHandler)); } } bool Database::GetPathAnimeAndEpisode(const std::string &basename, int *aid, int *ep) { anitomy::Anitomy anitomy; anitomy.Parse(basename); const auto &elements = anitomy.elements(); const std::string title = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle)); const int id = Anime::db.LookupAnimeTitle(title); if (id <= 0 || (find_id_ && find_id_.value() != id)) return false; const int episode = Strings::ToInt<int>(Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber))); *aid = id; *ep = episode; return true; } void Database::EventHandler(const std::filesystem::path &path, Filesystem::IWatcher::Event event) { std::string bname = path.filename().u8string(); int aid, ep; if (!GetPathAnimeAndEpisode(bname, &aid, &ep)) return; /* Lock the items mutex */ const std::lock_guard<std::recursive_mutex> lock(items_mutex_); switch (event) { case Filesystem::IWatcher::Created: items[aid][ep] = path; break; case Filesystem::IWatcher::Deleted: /* kill it off */ items[aid].erase(ep); if (items[aid].empty()) items.erase(aid); break; } } void Database::StaticEventHandler(void *opaque, const std::filesystem::path &path, Filesystem::IWatcher::Event event) { /* Forward to class function */ reinterpret_cast<Database *>(opaque)->EventHandler(path, event); } std::optional<std::filesystem::path> Database::GetAnimeFolder(int id) { // this function sucks, but it's the most I can really do for now. // // in the future the Refresh() function should look for directories // as well that fit the anime name and *also* have episodes in them. // it should give each of these directories a rating by how many // episodes are contained in them. whichever directory has more episodes // wins, or the first found if there is an equal amount. /* TODO use a shared lock instead */ const std::lock_guard<std::recursive_mutex> lock(items_mutex_); for (const auto &[anime_id, episodes] : items) { if (id != anime_id) continue; for (const auto &[episode, path] : episodes) return path.parent_path(); break; } return std::nullopt; } void Database::RefreshThread() { for (const auto &w : watchers_) w.second->Process(); } /* TODO the threading code for this could be better */ void Database::Refresh(std::optional<int> find_id) { /* Join the thread if it was already run */ if (refresh_thread_.joinable()) refresh_thread_.join(); /* This is terrible */ find_id_ = find_id; UpdateWatchers(); refresh_thread_ = std::thread(&Database::RefreshThread, this); } void Database::Refresh() { Refresh(std::nullopt); } void Database::Refresh(int id) { Refresh(std::optional<int>(id)); } std::optional<std::filesystem::path> Database::GetAnimeEpisodePath(int anime, int episode) { const std::lock_guard<std::recursive_mutex> lock(items_mutex_); if (items.find(anime) == items.end() || items[anime].find(episode) == items[anime].end()) return std::nullopt; return items[anime][episode]; } // TODO export to JSON on exit Database db; } // namespace Library
