# HG changeset patch # User Paper # Date 1775103536 14400 # Node ID 8d06825d96d179455691ba6dec89dc608a82f8be # Parent 9323153786dcf092035da6e7c39dd7dd68b6071b library: refresh in a separate thread this is fugly but it works diff -r 9323153786dc -r 8d06825d96d1 include/library/library.h --- a/include/library/library.h Wed Jan 21 11:35:32 2026 -0500 +++ b/include/library/library.h Thu Apr 02 00:18:56 2026 -0400 @@ -7,6 +7,8 @@ #include #include #include +#include +#include namespace Library { @@ -26,16 +28,27 @@ void Refresh(); void Refresh(int id); - // Anime episodes. Indexed as `folders[id][episode]' - std::unordered_map> items; + std::optional GetAnimeEpisodePath(int anime, int episode); private: void Refresh(std::optional find_id); + /* refresh_thread entry point */ + void RefreshThread(); Filesystem::PathMap> watchers_; - /* ID we're looking for */ + /* ID we're looking for (This is terrible, but other ways are also terrible in their own unique ways) */ std::optional find_id_; + + /* Anime episodes. Indexed as `folders[id][episode]' */ + std::unordered_map> items; + std::recursive_mutex items_mutex_; + + /* Thread that runs the refreshing. + * When Refresh() is called, if this is a valid thread, then it will be + * join()'d. This stinks, and a better method would be to have the + * thread running in the background waiting on a queue. Oh well. */ + std::thread refresh_thread_; }; extern Database db; diff -r 9323153786dc -r 8d06825d96d1 src/core/filesystem.cc --- a/src/core/filesystem.cc Wed Jan 21 11:35:32 2026 -0500 +++ b/src/core/filesystem.cc Thu Apr 02 00:18:56 2026 -0400 @@ -260,6 +260,7 @@ Win32WatcherVista(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) : Win32Watcher(opaque, path, handler, recursive), dirhandle_(INVALID_HANDLE_VALUE) { + ZeroMemory(&overlapped_, sizeof(overlapped_)); overlapped_.hEvent = nullptr; } diff -r 9323153786dc -r 8d06825d96d1 src/gui/pages/anime_list.cc --- a/src/gui/pages/anime_list.cc Wed Jan 21 11:35:32 2026 -0500 +++ b/src/gui/pages/anime_list.cc Thu Apr 02 00:18:56 2026 -0400 @@ -468,6 +468,7 @@ QKeySequence(QKeySequence::Refresh)); if (path) { + /* This is really dumb. */ menu->addSeparator(); { @@ -482,52 +483,36 @@ const int progress = anime->GetUserProgress(); const int episodes = anime->GetEpisodes(); - // I think this is right? + /* I think this is right? */ if (progress > 0) { - menu->addAction(tr("Play last episode (#%1)").arg(progress), [this, anime, progress] { - const int id = anime->GetId(); - - if (Library::db.items.find(id) == Library::db.items.end() || - Library::db.items[id].find(progress) == Library::db.items[id].end()) - return; - - QDesktopServices::openUrl( - QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string()))); - }); + std::optional x = Library::db.GetAnimeEpisodePath(anime->GetId(), progress - 1); + if (x) + menu->addAction(tr("Play last episode (#%1)").arg(progress - 1), [this, x] { + QDesktopServices::openUrl( + QUrl::fromLocalFile(Strings::ToQString(x.value().u8string()))); + }); } if (progress < episodes) { - menu->addAction( - tr("Play next episode (#%1)").arg(progress + 1), - [this, anime, progress] { - const int id = anime->GetId(); - - if (Library::db.items.find(id) == Library::db.items.end() || - Library::db.items[id].find(progress + 1) == Library::db.items[id].end()) - return; - - QDesktopServices::openUrl( - QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string()))); - }, - QKeySequence(Qt::CTRL | Qt::Key_N)); + std::optional x = Library::db.GetAnimeEpisodePath(anime->GetId(), progress + 1); + if (x) + menu->addAction(tr("Play next episode (#%1)").arg(progress + 1), [this, x] { + QDesktopServices::openUrl( + QUrl::fromLocalFile(Strings::ToQString(x.value().u8string()))); + }, QKeySequence(Qt::CTRL | Qt::Key_N)); } menu->addAction( tr("Play random episode"), [this, anime, episodes] { - const int id = anime->GetId(); - std::uniform_int_distribution distrib(1, episodes); const int episode = distrib(session.gen); - if (Library::db.items.find(id) == Library::db.items.end() || - Library::db.items[id].find(episode) == Library::db.items[id].end()) - return; + std::optional x = Library::db.GetAnimeEpisodePath(anime->GetId(), episode); - QDesktopServices::openUrl( - QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string()))); - }, - QKeySequence(Qt::CTRL | Qt::Key_R)); + if (x) + QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(x.value().u8string()))); + }, QKeySequence(Qt::CTRL | Qt::Key_R)); } menu->popup(QCursor::pos()); diff -r 9323153786dc -r 8d06825d96d1 src/library/library.cc --- a/src/library/library.cc Wed Jan 21 11:35:32 2026 -0500 +++ b/src/library/library.cc Thu Apr 02 00:18:56 2026 -0400 @@ -19,6 +19,9 @@ /* 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. */ } void Database::UpdateWatchers() @@ -62,6 +65,9 @@ if (!GetPathAnimeAndEpisode(bname, &aid, &ep)) return; + /* Lock the items mutex */ + const std::lock_guard lock(items_mutex_); + switch (event) { case Filesystem::IWatcher::Created: items[aid][ep] = path; @@ -91,6 +97,9 @@ // 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 lock(items_mutex_); + for (const auto &[anime_id, episodes] : items) { if (id != anime_id) continue; @@ -104,15 +113,25 @@ return std::nullopt; } -/* TODO shove this into a separate thread; currently it blocks */ +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 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(); - for (const auto &w : watchers_) - w.second->Process(); + refresh_thread_ = std::thread(&Database::RefreshThread, this); } void Database::Refresh() @@ -125,6 +144,16 @@ Refresh(std::optional(id)); } +std::optional Database::GetAnimeEpisodePath(int anime, int episode) +{ + const std::lock_guard 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;