Mercurial > minori
changeset 409:8d06825d96d1
library: refresh in a separate thread
this is fugly but it works
| author | Paper <paper@tflc.us> |
|---|---|
| date | Thu, 02 Apr 2026 00:18:56 -0400 |
| parents | 9323153786dc |
| children | eb554255ea5f |
| files | include/library/library.h src/core/filesystem.cc src/gui/pages/anime_list.cc src/library/library.cc |
| diffstat | 4 files changed, 67 insertions(+), 39 deletions(-) [+] |
line wrap: on
line diff
--- 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 <filesystem> #include <optional> #include <unordered_map> +#include <thread> +#include <mutex> namespace Library { @@ -26,16 +28,27 @@ void Refresh(); void Refresh(int id); - // Anime episodes. Indexed as `folders[id][episode]' - std::unordered_map<int, std::unordered_map<int, std::filesystem::path>> items; + std::optional<std::filesystem::path> GetAnimeEpisodePath(int anime, int episode); private: void Refresh(std::optional<int> find_id); + /* refresh_thread entry point */ + void RefreshThread(); Filesystem::PathMap<std::unique_ptr<Filesystem::IWatcher>> 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<int> find_id_; + + /* Anime episodes. Indexed as `folders[id][episode]' */ + std::unordered_map<int, std::unordered_map<int, std::filesystem::path>> 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;
--- 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; }
--- 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<std::filesystem::path> 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<std::filesystem::path> 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<int> 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<std::filesystem::path> 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());
--- 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<std::recursive_mutex> 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<std::recursive_mutex> 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<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(); - 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<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;
