mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-20 20:50:21 +02:00
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.
This commit is contained in:
@@ -461,9 +461,12 @@ one; paste an index URL or its `owner/repo` and it is remembered in
|
|||||||
A feed author can publish per-mod release stats by adding three optional
|
A feed author can publish per-mod release stats by adding three optional
|
||||||
fields to an entry -- `downloads` (total across every release), and
|
fields to an entry -- `downloads` (total across every release), and
|
||||||
`first_release` / `last_release` (ISO days) -- which the listing shows in
|
`first_release` / `last_release` (ISO days) -- which the listing shows in
|
||||||
the same gold line the MODS tab uses. The fields are additive: feeds that
|
the same gold line the MODS tab uses. When a feed does not carry them,
|
||||||
carry them stay readable by every build that predates them, and feeds that
|
the row fetches the mod's own GitHub releases instead -- the same cached
|
||||||
do not render exactly as before.
|
`ModUpdate` fetch the MODS tab uses, one entry per frame -- so the stats
|
||||||
|
appear for any mod with a `github` field regardless of feed maintenance.
|
||||||
|
The fields are additive: feeds that carry them stay readable by every
|
||||||
|
build that predates them, and feeds that do not render exactly as before.
|
||||||
|
|
||||||
## Soft reset (all versions)
|
## Soft reset (all versions)
|
||||||
|
|
||||||
|
|||||||
@@ -1291,6 +1291,7 @@ end
|
|||||||
|
|
||||||
local function buildFindPanel(imp, parent, m)
|
local function buildFindPanel(imp, parent, m)
|
||||||
imp._findThumbFetched = false
|
imp._findThumbFetched = false
|
||||||
|
imp._findStatsFetched = false
|
||||||
imp:_ensureFind()
|
imp:_ensureFind()
|
||||||
imp:_ensureMods()
|
imp:_ensureMods()
|
||||||
local ModIndex = require("src.mods.ModIndex")
|
local ModIndex = require("src.mods.ModIndex")
|
||||||
@@ -1437,12 +1438,13 @@ local function buildFindPanel(imp, parent, m)
|
|||||||
local btnH = math.ceil(textHeight(chipSize)) + 14
|
local btnH = math.ceil(textHeight(chipSize)) + 14
|
||||||
for _, entry in ipairs(rows) do
|
for _, entry in ipairs(rows) do
|
||||||
local action, note = findActionFor(entry, installed[entry.id])
|
local action, note = findActionFor(entry, installed[entry.id])
|
||||||
-- Feed-published release stats (downloads, first/last release date) in
|
-- Release stats for the row: feed-published when the feed carries
|
||||||
-- the same gold line the MODS tab uses; absent until a feed carries them.
|
-- them, otherwise fetched from the mod's GitHub repo (one per frame,
|
||||||
|
-- cached six hours) exactly like the MODS tab does.
|
||||||
|
local stats = imp:_findStats(entry)
|
||||||
local statsLine
|
local statsLine
|
||||||
if entry.downloads ~= nil or entry.first_release or entry.last_release then
|
if stats and (stats.total ~= nil or stats.first or stats.latest) then
|
||||||
statsLine = ModUpdate.statsLine(entry.downloads,
|
statsLine = ModUpdate.statsLine(stats.total, stats.first, stats.latest)
|
||||||
entry.first_release, entry.last_release)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local bodyH = math.ceil(textHeight(titleSize))
|
local bodyH = math.ceil(textHeight(titleSize))
|
||||||
|
|||||||
@@ -3025,6 +3025,44 @@ function RomImporter:_findThumb(entry)
|
|||||||
return ok and image or nil
|
return ok and image or nil
|
||||||
end
|
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._findStats = self._findStats or {}
|
||||||
|
local cached = self._findStats[entry.id]
|
||||||
|
if cached then return cached 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._findStats[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._findStats[entry.id] = cached
|
||||||
|
return cached
|
||||||
|
end
|
||||||
|
self._findStatsFetched = true
|
||||||
|
local ModUpdate = require("src.mods.ModUpdate")
|
||||||
|
local ok, releases = pcall(function()
|
||||||
|
local list, err = ModUpdate.fetchReleases(entry.github, entry.id, {})
|
||||||
|
if not list then error(tostring(err), 0) end
|
||||||
|
return list
|
||||||
|
end)
|
||||||
|
local stats = ok and ModUpdate.statsForReleases(releases) or nil
|
||||||
|
cached = { total = stats and stats.total, first = stats and stats.first,
|
||||||
|
latest = stats and stats.latest, done = true }
|
||||||
|
self._findStats[entry.id] = cached
|
||||||
|
return cached
|
||||||
|
end
|
||||||
|
|
||||||
-- Open the "add an index" text prompt. Deliberately a typed URL rather than a
|
-- 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
|
-- picked-from-a-list affair: there is no blessed index, and presenting one
|
||||||
-- would make the launcher's choice look like an endorsement.
|
-- would make the launcher's choice look like an endorsement.
|
||||||
|
|||||||
@@ -229,6 +229,20 @@ function ModUpdate.releaseDates(releases)
|
|||||||
return { first = first, latest = latest }
|
return { first = first, latest = latest }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- One resolver over a release list: { total, first, latest } or nil when
|
||||||
|
-- the list carries neither counts nor dates. The FIND MODS rows use this
|
||||||
|
-- on the repo's fetched releases, the same source the MODS tab trusts.
|
||||||
|
function ModUpdate.statsForReleases(releases)
|
||||||
|
local dl = ModUpdate.totalDownloads(releases)
|
||||||
|
local d = ModUpdate.releaseDates(releases)
|
||||||
|
if not dl and not d then return nil end
|
||||||
|
return {
|
||||||
|
total = dl and dl.total or nil,
|
||||||
|
first = d and d.first or nil,
|
||||||
|
latest = d and d.latest or nil,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
-- Thousands-separated count for the launcher ("12,345"), plain for small
|
-- Thousands-separated count for the launcher ("12,345"), plain for small
|
||||||
-- numbers. Never throws; garbage in, "0" out.
|
-- numbers. Never throws; garbage in, "0" out.
|
||||||
function ModUpdate.formatCount(n)
|
function ModUpdate.formatCount(n)
|
||||||
|
|||||||
@@ -253,4 +253,23 @@ do
|
|||||||
HostShell.canFetch, HostShell.httpGet = realCanFetch, realHttpGet
|
HostShell.canFetch, HostShell.httpGet = realCanFetch, realHttpGet
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- statsForReleases: one resolver over a release list, the FIND MODS path
|
||||||
|
do
|
||||||
|
local stats = ModUpdate.statsForReleases({
|
||||||
|
{ version = "1.0.0", downloads = 41, published = "2024-05-31" },
|
||||||
|
{ version = "1.1.0", downloads = 9, published = "2025-11-02" },
|
||||||
|
})
|
||||||
|
eq(stats.total, 50, "total downloads across releases")
|
||||||
|
eq(stats.first, "2024-05-31", "first release date")
|
||||||
|
eq(stats.latest, "2025-11-02", "latest release date")
|
||||||
|
check(ModUpdate.statsForReleases({ { version = "1.0.0" } }) == nil,
|
||||||
|
"a list with neither counts nor dates resolves to nil")
|
||||||
|
check(ModUpdate.statsForReleases(nil) == nil, "nil resolves to nil")
|
||||||
|
local datesOnly = ModUpdate.statsForReleases({
|
||||||
|
{ version = "1.0.0", published = "2024-05-31" },
|
||||||
|
})
|
||||||
|
eq(datesOnly.total, nil, "dates without counts keep total nil")
|
||||||
|
eq(datesOnly.first, "2024-05-31", "but keep the date")
|
||||||
|
end
|
||||||
|
|
||||||
print("ok mod_update_tests")
|
print("ok mod_update_tests")
|
||||||
|
|||||||
Reference in New Issue
Block a user