In-page catalog search and library account management for Bibliocommons library systems. Supports Santa Clara City/County, San Jose, Sunnyvale, and Palo Alto libraries.
git clone https://github.com/diaorui/bibliorecs.git
cd bibliorecs
pip install -r requirements.txt
python app.py
Open http://localhost:5050.
Edit config.py if needed. The defaults work for the 5 supported libraries:
| Variable | Default | Description |
|---|---|---|
HALF_LIFE_DAYS | 90 | Borrow recency weight half-life |
POOL_LIMIT | 100 | Max results per OR query |
TOP_CANDIDATES | 300 | Max books shown in Top Picks |
MMR_LAMBDA | 0.5 | Diversity vs. relevance trade-off |
MIN_COSINE | 0.75 | Min cosine similarity to seed when caching search results |
REFRESH_HOURS | 4 | Search result cache TTL |
FORMATS_REFRESH_HOURS | 24 | Physical format list cache TTL |
HOLDS_TTL_MIN | 15 | Holds cache freshness (background refresh interval) |
CHECKOUTS_TTL_MIN | 60 | Checkouts cache freshness |
HISTORY_TTL_MIN | 60 | History cache freshness |
SYNC_MAX_CONCURRENCY | 3 | Max parallel background BC sync jobs |
SYNC_RETRY_MIN | 5 | Retry delay after a failed sync |
app.py → Flask web app (all routes, device cookie middleware, template filters)
api.py → Bibliocommons API client (search, login, proxy functions)
search_recs.py → Recommendation engine: OR queries → cache → embedding → MaxSim → MMR
sync_manager.py → Account data worker: job queue, TTL, dedup, search-cache prewarm
login_manager.py → Serialized BC re-login (single-flight per account+library)
vault.py → SQLite storage: accounts, devices, pair codes, encrypted account data, catalog cache
cache.py → Generic RefreshCache with TTL-based refresh, in-flight dedup, SQLite persistence
config.py → Library definitions and configuration constants
Every browser is auto-provisioned with an opaque device token (HttpOnly cookie bc_device); the server only stores its hash. Device tokens map to an account that owns all data. Pairing is one-way: the device with your library cards shows a 6-digit code in Settings → Devices, and a new (empty) device enters it to join the same account; devices with existing library data cannot claim a code. A device can be revoked at any time; "Forget this device" unlinks the current browser.
Library card credentials (card number, PIN, BC session tokens) are stored encrypted (Fernet) in the SQLite vault and shared across all linked devices. Connecting a card immediately pulls holds, checkouts, and history (blocking, so the reload lands on fresh data). Proxy endpoints use the stored tokens server-side and automatically re-login on 401; re-logins are serialized per (account, library) so concurrent requests never race a login. The card number is exposed to the frontend for the barcode view; the PIN never leaves the server after connecting.
(library_id, metadata_id) via RefreshCache with a 4-hour TTL, persisted to SQLite so they survive restarts. The cache is pre-warmed by the backend history sync job.MIN_COSINE (0.75) to its seed book (model2vec potion-base-4M) before being cached.| Route | Method | Description |
|---|---|---|
/ | GET | Home page with recommendations or search |
/book/<metadata_id> | GET | Book detail page |
/holds | GET | Hold management page |
/history | GET | Borrowing history page |
/settings | GET | Settings (branch, creds, devices, server) |
/api/recommendations | GET | Top Picks carousel (computed server-side from vault history) |
/api/search | POST | Catalog search |
/api/search/suggest | GET | Search autocomplete suggestions |
/api/bib/<metadata_id> | GET | Book metadata + covers |
/api/branches | GET | Branch list for all libraries |
/api/ol-cover-search/<isbn> | GET | OpenLibrary cover search fallback |
/api/me | GET | Current account, linked devices, per-library connection status |
/api/creds/login | POST | Connect a library card (logs in, stores encrypted, syncs holds/checkouts/history) |
/api/creds/disconnect | POST | Remove a library card |
/api/holds/<lib> | GET | Cached holds ({data, stale, last_updated}) |
/api/checkouts/<lib> | GET | Cached checkouts ({data, stale, last_updated}) |
/api/history/<lib> | GET | Cached borrowing history ({data, stale, last_updated}) |
/api/pair/create | POST | Generate a 6-digit pairing code |
/api/pair/claim | POST | Link this device to the code's account |
/api/device/revoke | POST | Revoke a linked device |
/api/device/forget | POST | Unlink the current device |
/api/proxy/checkout/renew | POST | Renew a checkout |
/api/proxy/hold/place | POST | Place a hold |
/api/proxy/hold/cancel | POST | Cancel a hold |
/api/restart | POST | Restart server |
MIT
434 commits
Python
43.7%
HTML
41.3%
CSS
15.0%
In-page catalog search and library account management for Bibliocommons library systems. Supports Santa Clara City/County, San Jose, Sunnyvale, and Palo Alto libraries.
git clone https://github.com/diaorui/bibliorecs.git
cd bibliorecs
pip install -r requirements.txt
python app.py
Open http://localhost:5050.
Edit config.py if needed. The defaults work for the 5 supported libraries:
| Variable | Default | Description |
|---|---|---|
HALF_LIFE_DAYS | 90 | Borrow recency weight half-life |
POOL_LIMIT | 100 | Max results per OR query |
TOP_CANDIDATES | 300 | Max books shown in Top Picks |
MMR_LAMBDA | 0.5 | Diversity vs. relevance trade-off |
MIN_COSINE | 0.75 | Min cosine similarity to seed when caching search results |
REFRESH_HOURS | 4 | Search result cache TTL |
FORMATS_REFRESH_HOURS | 24 | Physical format list cache TTL |
HOLDS_TTL_MIN | 15 | Holds cache freshness (background refresh interval) |
CHECKOUTS_TTL_MIN | 60 | Checkouts cache freshness |
HISTORY_TTL_MIN | 60 | History cache freshness |
SYNC_MAX_CONCURRENCY | 3 | Max parallel background BC sync jobs |
SYNC_RETRY_MIN | 5 | Retry delay after a failed sync |
app.py → Flask web app (all routes, device cookie middleware, template filters)
api.py → Bibliocommons API client (search, login, proxy functions)
search_recs.py → Recommendation engine: OR queries → cache → embedding → MaxSim → MMR
sync_manager.py → Account data worker: job queue, TTL, dedup, search-cache prewarm
login_manager.py → Serialized BC re-login (single-flight per account+library)
vault.py → SQLite storage: accounts, devices, pair codes, encrypted account data, catalog cache
cache.py → Generic RefreshCache with TTL-based refresh, in-flight dedup, SQLite persistence
config.py → Library definitions and configuration constants
Every browser is auto-provisioned with an opaque device token (HttpOnly cookie bc_device); the server only stores its hash. Device tokens map to an account that owns all data. Pairing is one-way: the device with your library cards shows a 6-digit code in Settings → Devices, and a new (empty) device enters it to join the same account; devices with existing library data cannot claim a code. A device can be revoked at any time; "Forget this device" unlinks the current browser.
Library card credentials (card number, PIN, BC session tokens) are stored encrypted (Fernet) in the SQLite vault and shared across all linked devices. Connecting a card immediately pulls holds, checkouts, and history (blocking, so the reload lands on fresh data). Proxy endpoints use the stored tokens server-side and automatically re-login on 401; re-logins are serialized per (account, library) so concurrent requests never race a login. The card number is exposed to the frontend for the barcode view; the PIN never leaves the server after connecting.
(library_id, metadata_id) via RefreshCache with a 4-hour TTL, persisted to SQLite so they survive restarts. The cache is pre-warmed by the backend history sync job.MIN_COSINE (0.75) to its seed book (model2vec potion-base-4M) before being cached.| Route | Method | Description |
|---|---|---|
/ | GET | Home page with recommendations or search |
/book/<metadata_id> | GET | Book detail page |
/holds | GET | Hold management page |
/history | GET | Borrowing history page |
/settings | GET | Settings (branch, creds, devices, server) |
/api/recommendations | GET | Top Picks carousel (computed server-side from vault history) |
/api/search | POST | Catalog search |
/api/search/suggest | GET | Search autocomplete suggestions |
/api/bib/<metadata_id> | GET | Book metadata + covers |
/api/branches | GET | Branch list for all libraries |
/api/ol-cover-search/<isbn> | GET | OpenLibrary cover search fallback |
/api/me | GET | Current account, linked devices, per-library connection status |
/api/creds/login | POST | Connect a library card (logs in, stores encrypted, syncs holds/checkouts/history) |
/api/creds/disconnect | POST | Remove a library card |
/api/holds/<lib> | GET | Cached holds ({data, stale, last_updated}) |
/api/checkouts/<lib> | GET | Cached checkouts ({data, stale, last_updated}) |
/api/history/<lib> | GET | Cached borrowing history ({data, stale, last_updated}) |
/api/pair/create | POST | Generate a 6-digit pairing code |
/api/pair/claim | POST | Link this device to the code's account |
/api/device/revoke | POST | Revoke a linked device |
/api/device/forget | POST | Unlink the current device |
/api/proxy/checkout/renew | POST | Renew a checkout |
/api/proxy/hold/place | POST | Place a hold |
/api/proxy/hold/cancel | POST | Cancel a hold |
/api/restart | POST | Restart server |
MIT
434 commits
Python
43.7%
HTML
41.3%
CSS
15.0%