Show mod downloads and release dates in the launcher MODS and Find Mods panels (#793)

* Resolve Find Mods stats from each mod's GitHub repo when the feed lacks them

A FIND MODS row now shows download/date stats even when its feed publishes
none: the row fetches the mod's own GitHub releases through the same
cached ModUpdate.fetchReleases the MODS tab uses (six-hour options cache,
so an installed mod's repo is instant). Feed-published stats still win
when present; otherwise one repo is fetched per frame -- the thumbnail
budget pattern -- so opening the tab never stalls for the whole listing.
ModUpdate.statsForReleases is the shared resolver.

* Fix crash opening the Find Mods tab: rename the stats cache field

The resolver stored results in self._findStats, which collides with the
method of the same name: self._findStats resolves through the metatable to
the function, so the or {} guard never fired and indexing it crashed the
launcher the moment the panel built. State now lives in _findStatsCache.

* Fix Find Mods crash: require ModUpdate in the find panel

buildFindPanel called ModUpdate.statsLine without a local require --
only buildModsPanel had one -- so opening the tab indexed a nil global.

* Retry Find Mods stats after failed repo fetches

A failed repo fetch (hourly GitHub API rate limit, transient network error)
was memoized as resolved, so a rate-limited first visit left those rows
empty for the whole session. Failures now schedule a 60s retry; a 404 is
still permanent so a renamed or vanished repo is fetched once.

* Add the MODS tab sort options to the Find Mods tab
This commit is contained in:
Shane McGovern
2026-08-04 15:16:44 +01:00
committed by GitHub
parent f56e82de81
commit 626080d228
5 changed files with 171 additions and 8 deletions
+50
View File
@@ -3075,6 +3075,56 @@ function RomImporter:_findThumb(entry)
return ok and image or nil
end
-- Release stats for a FIND MODS row, resolved the same way the MODS tab
-- does it: the mod's own GitHub releases through ModUpdate's cached fetch,
-- so an installed mod's repo is instant and every result lands in
-- options.modUpdateCache for six hours. A feed that publishes stats wins
-- outright (fresher, zero network); otherwise the repo is fetched, one
-- entry per frame so opening the tab cannot stall for the whole listing.
-- The result is memoized per id for the session; a repo with no releases
-- or a failed fetch resolves to an empty table so it is tried once.
function RomImporter:_findStats(entry)
self._findStatsCache = self._findStatsCache or {}
local cached = self._findStatsCache[entry.id]
if cached then
if cached.done or (cached.retryAt and os.time() < cached.retryAt) then
return cached
end
self._findStatsCache[entry.id] = nil -- retry window open, refetch
end
if entry.downloads ~= nil or entry.first_release or entry.last_release then
cached = { total = entry.downloads, first = entry.first_release,
latest = entry.last_release, done = true }
self._findStatsCache[entry.id] = cached
return cached
end
if self._findStatsFetched then return nil end -- budget spent this frame
if not entry.github or entry.github == "" then
cached = { done = true }
self._findStatsCache[entry.id] = cached
return cached
end
self._findStatsFetched = true
local ModUpdate = require("src.mods.ModUpdate")
local list, fetchErr
local ok = pcall(function()
list, fetchErr = ModUpdate.fetchReleases(entry.github, entry.id, {})
end)
local stats = list and ModUpdate.statsForReleases(list) or nil
if stats then
cached = { total = stats.total, first = stats.first,
latest = stats.latest, done = true }
else
-- A repo that does not exist is permanent; every other failure (the
-- hourly API rate limit, a hiccup) is retried in a minute so rows can
-- recover without restarting the launcher.
local permanent = tostring(fetchErr):find("Not Found", 1, true) ~= nil
cached = { done = permanent, retryAt = os.time() + 60 }
end
self._findStatsCache[entry.id] = cached
return cached
end
-- Open the "add an index" text prompt. Deliberately a typed URL rather than a
-- picked-from-a-list affair: there is no blessed index, and presenting one
-- would make the launcher's choice look like an endorsement.