Pace IGDB requests; rewrite the new help text

IGDB allows 4 requests a second, and an import searching three entries
at once (plus detail lookups) went over it. Every IGDB request now waits
at least 300 ms after the last one, and a 429 is retried after 1, 2 and
4 seconds before giving up.

The import dialog, ranking editor and their error messages are reworded
in plainer language, and talk about "spots" instead of "places".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Opus 5.5 committed 2026-10-08 14:51:09 -07:00
1 parent 62fcf6b45d
commit 1f3fa4fb2b
7 files changed
+60 -37

No files matched your search

+19 -3
View File
@@ -99,11 +99,15 @@ func (g *igdb) query(ctx context.Context, body string) ([]Result, error) {
Name string `json:"name"`
} `json:"platforms"`
}
for attempt := 0; ; attempt++ {
tok, err := g.token(ctx, attempt > 0)
refreshed, backoff := false, time.Second
for {
tok, err := g.token(ctx, refreshed)
if err != nil {
return nil, err
}
if err := g.r.igdbPace.wait(ctx); err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.igdb.com/v4/games", strings.NewReader(body))
if err != nil {
return nil, err
@@ -112,7 +116,19 @@ func (g *igdb) query(ctx context.Context, body string) ([]Result, error) {
req.Header.Set("Authorization", "Bearer "+tok)
req.Header.Set("Content-Type", "text/plain")
err = g.r.do(req, &games)
if isStatus(err, 401) && attempt == 0 {
if isStatus(err, 401) && !refreshed {
refreshed = true
continue
}
// Rate limited anyway (another app on the same key, say): back off
// and try again, up to about 7 seconds in all.
if isStatus(err, 429) && backoff <= 4*time.Second {
select {
case <-time.After(backoff):
case <-ctx.Done():
return nil, ctx.Err()
}
backoff *= 2
continue
}
if err != nil {
+9 -5
View File
@@ -74,15 +74,19 @@ type Registry struct {
tvdb *tvdbTokens
igdb *igdbTokens
mb *throttle
// IGDB allows 4 requests a second; stay under it so an import's
// searches and detail lookups don't get answered with 429.
igdbPace *throttle
}
func NewRegistry(creds func(context.Context) Credentials) *Registry {
return &Registry{
creds: creds,
client: &http.Client{Timeout: 20 * time.Second},
tvdb: &tvdbTokens{},
igdb: &igdbTokens{},
mb: &throttle{gap: 1100 * time.Millisecond},
creds: creds,
client: &http.Client{Timeout: 20 * time.Second},
tvdb: &tvdbTokens{},
igdb: &igdbTokens{},
mb: &throttle{gap: 1100 * time.Millisecond},
igdbPace: &throttle{gap: 300 * time.Millisecond},
}
}