Files
calibre-web/cps
Jack Halford a32f2783c0 Google Books: retry transient errors, and tell a failed search from an empty one
A failed metadata search is reported to the user as "No Result(s) found!
Please try another keyword." — the same words as a genuine miss, so the user
goes looking for a keyword problem that is not there. search() swallows every
exception and returns [], which the route cannot tell from an empty result.

Three ways it happens:

1. No API key, which is the default. An unkeyed Books call is billed to a
   shared anonymous Google project whose daily quota is permanently spent:
   429 RESOURCE_EXHAUSTED, "Quota exceeded ... for consumer
   project_number:<redacted>". The same project number comes back from two
   unrelated networks, so it is shared, not per-IP. Every stock install with
   no key therefore gets nothing, and is told its keyword is wrong.

2. Transient 5xx with a valid key: 2 of 12 single-shot keyed requests
   answered 503. There is no retry anywhere in cps/.

3. No request timeout, so a stalled connection holds the search thread until
   the client gives up. /metadata/search waits on every provider.

Changes:

- google.py retries 500/502/503/504 (3 retries, 0.3 backoff) and passes a
  (5, 15) timeout. 429 is deliberately not retried: a spent daily quota does
  not clear, so it must fail fast.
- google.py returns None on failure and [] on an empty result. The signature
  is already Optional[List[MetaRecord]] and callers already handle None.
- search_metadata.py answers 502 when every provider it asked failed.
  get_meta.js already tells msg.search_error from msg.no_result; it had no
  way to know which had happened. "Search error!" is an existing string,
  translated in every locale — no new text, no JS change.
- search_metadata.py no longer lets one provider's exception fail the whole
  request, which used to discard every other provider's results.
- The API key is read per search instead of at import, so saving one in the
  admin page takes effect without a restart.

allowed_methods is left at the urllib3 default, which already covers GET;
naming it would need urllib3 >= 1.26 and requirements.txt allows 1.22. The
Session is built per call because requests.Session is not thread-safe and the
providers are searched in a thread pool.

Not included, happy to add: cps/admin.py still sets reboot_required for
config_googlebooks_api_key, now unnecessary; and the other five providers
still return [] on failure.
2026-09-02 01:03:22 +02:00
..
2024-11-04 21:20:34 +01:00
2026-08-08 20:10:55 +02:00
2022-05-22 12:49:00 +01:00
2026-08-08 10:46:44 +02:00
2024-09-07 20:07:45 +02:00
2026-08-08 18:18:32 +02:00
2025-03-23 09:18:48 +01:00
2026-05-17 10:21:19 +02:00
2026-08-08 10:46:44 +02:00
2026-04-14 22:12:57 +02:00
2026-08-08 20:10:55 +02:00
2026-04-14 22:12:57 +02:00
2026-08-08 10:46:44 +02:00
2024-07-02 20:53:08 +02:00
2026-08-09 12:06:52 +02:00
2026-08-07 21:10:58 +02:00
2026-04-14 22:10:30 +02:00
2024-12-13 18:03:14 +01:00
2025-03-23 15:12:06 +01:00
2026-07-05 10:57:52 +02:00
2024-07-29 20:08:17 +02:00
2026-04-14 22:28:06 +02:00
2025-03-30 12:03:12 +02:00
2025-11-22 15:06:40 +01:00
2026-08-01 18:47:25 +02:00
2026-08-08 20:10:55 +02:00
2026-08-01 18:47:25 +02:00
2024-11-11 18:46:07 +01:00
2026-08-08 10:46:44 +02:00