diff --git a/backend/latest_sites.go b/backend/latest_sites.go index 5a4feb3..d503dbf 100644 --- a/backend/latest_sites.go +++ b/backend/latest_sites.go @@ -14,7 +14,9 @@ type latestChapter struct { // asuraSlugRe pulls the series slug out of a stored series_url. // Shape verified live 2026-07-26: https://asurascans.com/comics/, where -// the slug carries a trailing hash-like suffix (e.g. "-f886a8af"). +// the slug carries a trailing build-hash suffix (e.g. "-f886a8af") that +// rotates on every site redeploy — callers must strip it (asuraBuildHash) +// before using the slug to scope anything. var asuraSlugRe = regexp.MustCompile(`/comics/([^/?#]+)`) // demonicChapterRe matches the pre-redirect anchors demonic series pages link @@ -47,9 +49,14 @@ func latestChapterFrom(site, seriesURL, body string) (latestChapter, bool) { if m == nil { return latestChapter{}, false } + // Stored URLs predating a redeploy may carry a stale build hash; + // chapter hrefs in the fetched body carry the current one. Strip to + // the stable ID (same rule as migrateAsuraKeys) and make the hash + // optional in the pattern, so scoping survives rotations. + slug := asuraBuildHash.ReplaceAllString(m[1], "") // Compiled per call rather than cached: this runs once per fetch, which // is at most a few times a minute, and the slug varies per series. - re = regexp.MustCompile(`/comics/` + regexp.QuoteMeta(m[1]) + `/chapter/([0-9.]+)`) + re = regexp.MustCompile(`/comics/` + regexp.QuoteMeta(slug) + `(?:-[0-9a-f]{8})?/chapter/([0-9.]+)`) case "demonic": re = demonicChapterRe default: diff --git a/backend/latest_sites_test.go b/backend/latest_sites_test.go index a65b346..443653d 100644 --- a/backend/latest_sites_test.go +++ b/backend/latest_sites_test.go @@ -57,6 +57,13 @@ func TestLatestChapterFrom(t *testing.T) { site: "asura", seriesURL: asuraURL, body: asuraCrossSeriesFixture, wantOK: true, wantNum: 181, wantLabel: "Chapter 181", }, + { + name: "asura scoping survives a build-hash rotation", + site: "asura", + seriesURL: "https://asurascans.com/comics/chronicles-of-the-demon-faction-059befe1", + body: asuraCrossSeriesFixture, + wantOK: true, wantNum: 181, wantLabel: "Chapter 181", + }, { name: "asura with an unparseable series url", site: "asura", seriesURL: "https://asurascans.com/", body: asuraSeriesFixture,