diff --git a/backend/store.go b/backend/store.go index a6b7b61..39e40d9 100644 --- a/backend/store.go +++ b/backend/store.go @@ -327,10 +327,15 @@ func (s *Store) Delete(key string) error { // // Bookmarks with no series_url are skipped — there is nothing to fetch, which // is the same filter the userscript applies at L452. +// +// Finished series are excluded: nothing more is coming, so fetching them only +// burns requests. Archived ones are deliberately still polled — knowing what a +// shelved series is up to is the whole reason for archiving instead of deleting. func (s *Store) DueForLatestCheck(cutoffMs int64, limit int) ([]Bookmark, error) { rows, err := s.db.Query(`SELECT `+bookmarkColumns+` FROM bookmarks WHERE series_url IS NOT NULL AND series_url <> '' + AND status IS NOT 'finished' AND latest_checked_at <= ? ORDER BY latest_checked_at ASC LIMIT ?`, cutoffMs, limit) diff --git a/backend/store_test.go b/backend/store_test.go index 3cc3792..c1eddac 100644 --- a/backend/store_test.go +++ b/backend/store_test.go @@ -838,3 +838,37 @@ func TestMigrationAddsStatusToLegacyDatabase(t *testing.T) { t.Fatalf("Status = %q, want %q", b.Status, statusReading) } } + +// Archiving is the reason to keep polling — the point is to come back to a +// series that has moved on. A finished series has nothing left to publish. +func TestDueForLatestCheckSkipsFinishedKeepsArchived(t *testing.T) { + store := newTestStore(t) + for _, tc := range []struct{ key, status string }{ + {"asura:reading", statusReading}, + {"asura:archived", statusArchived}, + {"asura:finished", statusFinished}, + } { + if _, err := store.Upsert(Bookmark{ + Key: tc.key, Site: "asura", SeriesID: tc.key, + SeriesURL: "https://asurascans.com/comics/" + tc.key, + Status: tc.status, UpdatedAt: time.Now().UnixMilli(), + }); err != nil { + t.Fatalf("seed %s: %v", tc.key, err) + } + } + + due, err := store.DueForLatestCheck(time.Now().UnixMilli(), 10) + if err != nil { + t.Fatalf("DueForLatestCheck: %v", err) + } + got := map[string]bool{} + for _, b := range due { + got[b.Key] = true + } + if !got["asura:reading"] || !got["asura:archived"] { + t.Fatalf("due = %v, want reading and archived present", got) + } + if got["asura:finished"] { + t.Fatal("due included a finished series") + } +}