tot_agent.covers¶
Book cover fetching with the Strategy design pattern.
Class diagram¶
classDiagram
class CoverSource {
<<abstract>>
+search(query: str, limit: int) list~BookCover~
}
class OpenLibrarySource {
-_timeout: int
+search(query, limit) list~BookCover~
}
class GoogleBooksSource {
-_timeout: int
+search(query, limit) list~BookCover~
}
class CoverFetcher {
-_sources: list~CoverSource~
+fetch(query, count) list~BookCover~
+fetch_random_pairs(pair_count) list
-_deduplicate(covers) list~BookCover~
}
class BookCover {
+title: str
+author: str
+cover_url: str
+source: str
+isbn: str
}
CoverSource <|-- OpenLibrarySource
CoverSource <|-- GoogleBooksSource
CoverFetcher --> CoverSource : uses
CoverFetcher ..> BookCover : produces
Fetch flow¶
flowchart TD
A[fetch query, count] --> B{OpenLibrary has enough?}
B -- yes --> D[deduplicate]
B -- no --> C[GoogleBooks fallback]
C --> D
D --> E[truncate to count]
E --> F[return BookCover list]
Image download¶
download_cover_image(url) downloads a cover image to a local temp file and
returns the path. The caller is responsible for deleting the file after use.
flowchart LR
A["download_cover_image(url)"] --> B["httpx.get(url)"]
B --> C{status ok?}
C -- no --> D["raise HTTPStatusError"]
C -- yes --> E["inspect Content-Type"]
E --> F["tempfile.mkstemp(suffix=ext)"]
F --> G["write bytes"]
G --> H["return path"]
The extension is derived from the Content-Type response header:
| Content-Type | Extension |
|---|---|
image/jpeg |
.jpg |
image/png |
.png |
image/gif |
.gif |
image/webp |
.webp |
image/bmp |
.bmp |
| (other) | .jpg |
Module reference¶
tot_agent.covers
¶
covers.py — Book cover fetching via the Strategy design pattern.
Two concrete sources are provided out of the box:
- :class:
OpenLibrarySource— primary source, free, no API key required. - :class:
GoogleBooksSource— fallback source, no API key required for low-volume requests.
The :class:CoverFetcher orchestrator accepts any list of
:class:CoverSource implementations, making it straightforward to add new
sources (e.g. Amazon, Goodreads) without touching existing code.
Example::
from tot_agent.covers import CoverFetcher
fetcher = CoverFetcher()
covers = fetcher.fetch("fantasy epic", count=4)
for cover in covers:
print(cover)
BookCover
dataclass
¶
Metadata and image URL for a single book cover.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
Book title. |
required |
author
|
str
|
Primary author name. |
required |
cover_url
|
str
|
Direct URL to the cover image (HTTPS). |
required |
source
|
str
|
Originating data source identifier (e.g. |
required |
isbn
|
str | None
|
ISBN-10 or ISBN-13, if available. |
None
|
Source code in src/tot_agent/covers.py
CoverSource
¶
Bases: ABC
Abstract base class for book cover data sources (Strategy pattern).
Subclasses implement :meth:search to query a specific upstream API and
return a normalised list of :class:BookCover objects.
Source code in src/tot_agent/covers.py
search(query, limit)
abstractmethod
¶
Search for book covers matching query.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Free-text search query (title, author, or genre). |
required |
limit
|
int
|
Maximum number of results to return. |
required |
Returns:
| Type | Description |
|---|---|
list[BookCover]
|
List of matching book covers (may be empty). |
Source code in src/tot_agent/covers.py
OpenLibrarySource
¶
Bases: CoverSource
Fetch book covers from the Open Library search API.
This is the primary source. No API key is required.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
int
|
HTTP request timeout in seconds. Defaults to |
10
|
Source code in src/tot_agent/covers.py
search(query, limit)
¶
Search Open Library and return books that have cover images.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query string. |
required |
limit
|
int
|
Maximum number of covers to return. |
required |
Returns:
| Type | Description |
|---|---|
list[BookCover]
|
List of :class: |
Source code in src/tot_agent/covers.py
GoogleBooksSource
¶
Bases: CoverSource
Fetch book covers from the Google Books API (fallback source).
No API key is required for low-volume requests.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
int
|
HTTP request timeout in seconds. Defaults to |
10
|
Source code in src/tot_agent/covers.py
search(query, limit)
¶
Search the Google Books API and return books that have cover images.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query string. |
required |
limit
|
int
|
Maximum number of covers to return. |
required |
Returns:
| Type | Description |
|---|---|
list[BookCover]
|
List of :class: |
Source code in src/tot_agent/covers.py
CoverFetcher
¶
Orchestrates cover fetching across one or more :class:CoverSource strategies.
Sources are queried in order until count unique covers have been gathered. Results are deduplicated by normalised title.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sources
|
list[CoverSource] | None
|
Ordered list of sources to query. Defaults to
Example:: |
None
|
Source code in src/tot_agent/covers.py
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | |
fetch(query, count=5)
¶
Fetch count book covers matching query.
Tries each configured source in order, deduplicates by title, and returns up to count results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Free-text search query. |
required |
count
|
int
|
Number of covers to return. |
5
|
Returns:
| Type | Description |
|---|---|
list[BookCover]
|
List of unique :class: |
Source code in src/tot_agent/covers.py
fetch_random_pairs(pair_count=5)
¶
Return pair_count pairs of (cover_a, cover_b) from random genres.
Useful for seeding A/B tests without repetition within a pair.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pair_count
|
int
|
Number of cover pairs to generate. |
5
|
Returns:
| Type | Description |
|---|---|
list[tuple[BookCover, BookCover]]
|
List of 2-tuples, each containing two distinct
:class: |
Source code in src/tot_agent/covers.py
download_cover_image(url, timeout=10)
¶
Download a cover image from url to a temporary local file.
The caller is responsible for deleting the file when it is no longer needed (e.g. after a browser file-upload completes).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Direct URL of the image to download. |
required |
timeout
|
int
|
HTTP timeout in seconds. Defaults to |
10
|
Returns:
| Type | Description |
|---|---|
str
|
Absolute path to the downloaded temporary file. |
Raises:
| Type | Description |
|---|---|
httpx.HTTPError
|
If the download fails or returns a non-2xx status. |
Source code in src/tot_agent/covers.py
verify_cover_url(url, timeout=5)
¶
Perform a HEAD request to verify that url resolves to a live image.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The image URL to check. |
required |
timeout
|
int
|
HTTP timeout in seconds. Defaults to |
5
|
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/tot_agent/covers.py
fetch_book_covers(query, count=5)
¶
Module-level convenience wrapper around :class:CoverFetcher.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query. |
required |
count
|
int
|
Number of covers to fetch. |
5
|
Returns:
| Type | Description |
|---|---|
list[BookCover]
|
List of :class: |
Source code in src/tot_agent/covers.py
fetch_random_cover_pairs(pair_count=5)
¶
Module-level convenience wrapper for random cover pairs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pair_count
|
int
|
Number of pairs to generate. |
5
|
Returns:
| Type | Description |
|---|---|
list[tuple[BookCover, BookCover]]
|
List of |