From 691ede2882c16abe81d653da3b4f18719a6357c3 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Wed, 14 Jan 2026 01:30:08 +0100 Subject: [PATCH] add threaded download and mutex, else it blocks the start for to long when in sync --- .../hle/service/bcat/news/builtin_news.cpp | 86 ++++++++++++++++--- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/bcat/news/builtin_news.cpp b/src/core/hle/service/bcat/news/builtin_news.cpp index b58911656d..852f93cdee 100644 --- a/src/core/hle/service/bcat/news/builtin_news.cpp +++ b/src/core/hle/service/bcat/news/builtin_news.cpp @@ -20,10 +20,12 @@ #include #include #include +#include #include #include #include #include +#include #ifdef YUZU_BUNDLED_OPENSSL #include @@ -41,6 +43,8 @@ bool default_logos_loaded = false; std::unordered_map> news_images_small; std::unordered_map> news_images_large; +std::mutex images_mutex; + std::filesystem::path GetCachePath() { return Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "news" / "github_releases.json"; @@ -147,16 +151,19 @@ void LoadDefaultLogos() { std::vector GetNewsImage(std::string_view news_id, bool large) { const std::string id_str{news_id}; - auto& cache = large ? news_images_large : news_images_small; - if (auto it = cache.find(id_str); it != cache.end()) { - return it->second; + { + std::lock_guard lock{images_mutex}; + auto& cache = large ? news_images_large : news_images_small; + if (auto it = cache.find(id_str); it != cache.end()) { + return it->second; + } } const auto cache_path = GetNewsImagePath(news_id, large); auto data = TryLoadFromDisk(cache_path); if (data.empty()) { - const std::string url = fmt::format("/news/{}_{}.jpg", news_id, large ? "large" : "small"); + const std::string url = fmt::format("/news/{}_{}.jpg", id_str, large ? "large" : "small"); data = DownloadImage(url, cache_path); } @@ -164,9 +171,48 @@ std::vector GetNewsImage(std::string_view news_id, bool large) { data = large ? default_logo_large : default_logo_small; } + { + std::lock_guard lock{images_mutex}; + auto& cache = large ? news_images_large : news_images_small; + cache[id_str] = data; + } + return data; } +void PreloadNewsImages(const std::vector& news_ids) { + std::vector> futures; + futures.reserve(news_ids.size() * 2); + + for (const u32 id : news_ids) { + const std::string id_str = fmt::format("{}", id); + + { + std::lock_guard lock{images_mutex}; + if (news_images_small.contains(id_str) && news_images_large.contains(id_str)) { + continue; + } + } + + const auto path_small = GetNewsImagePath(id_str, false); + const auto path_large = GetNewsImagePath(id_str, true); + if (std::filesystem::exists(path_small) && std::filesystem::exists(path_large)) { + continue; + } + + futures.push_back(std::async(std::launch::async, [id_str]() { + GetNewsImage(id_str, false); + })); + futures.push_back(std::async(std::launch::async, [id_str]() { + GetNewsImage(id_str, true); + })); + } + + for (auto& f : futures) { + f.wait(); + } +} + std::optional ReadCachedJson() { const auto path = GetCachePath(); if (!std::filesystem::exists(path)) return std::nullopt; @@ -307,6 +353,19 @@ void ImportReleases(std::string_view json_text) { if (!root.is_array()) return; + std::vector news_ids; + for (const auto& rel : root) { + if (!rel.is_object()) continue; + std::string title = rel.value("name", rel.value("tag_name", std::string{})); + if (title.empty()) continue; + + const u64 release_id = rel.value("id", 0); + const u32 news_id = release_id ? static_cast(release_id & 0x7FFFFFFF) : HashToNewsId(title); + news_ids.push_back(news_id); + } + + PreloadNewsImages(news_ids); + for (const auto& rel : root) { if (!rel.is_object()) continue; @@ -472,19 +531,18 @@ void EnsureBuiltinNewsLoaded() { std::call_once(once, [] { LoadDefaultLogos(); - if (const auto fresh = DownloadReleasesJson()) { - WriteCachedJson(*fresh); - ImportReleases(*fresh); - LOG_DEBUG(Service_BCAT, ", {} entries, downloaded", NewsStorage::Instance().ListAll().size()); - return; - } - - // Fallback to cached JSON if download failed if (const auto cached = ReadCachedJson()) { ImportReleases(*cached); - LOG_DEBUG(Service_BCAT, ", {} entries, cached", NewsStorage::Instance().ListAll().size()); - return; + LOG_DEBUG(Service_BCAT, "news: {} entries loaded from cache", NewsStorage::Instance().ListAll().size()); } + + std::thread([] { + if (const auto fresh = DownloadReleasesJson()) { + WriteCachedJson(*fresh); + ImportReleases(*fresh); + LOG_DEBUG(Service_BCAT, "news: {} entries updated from GitHub", NewsStorage::Instance().ListAll().size()); + } + }).detach(); }); }