HMCPL Library Manager
CLI tool for managing your Huntsville-Madison County Public Library account.
Features
- View account status (checkouts, holds, fines, expiration)
- List checked out items with due dates
- List holds and their status
- Search the library catalog
- Place holds on items
- Renew checked out items
Installation
# Install dependencies
uv sync
# Install Playwright browser
uv run playwright install chromium
System Dependencies (headless servers)
Headless Chromium requires system libraries. Install with:
sudo apt-get install -y libnspr4 libnss3 libatk1.0-0t64 libatk-bridge2.0-0t64 \
libcups2t64 libxkbcommon0 libxdamage1 libgbm1 libpango-1.0-0 libcairo2 \
libasound2t64 libxcomposite1 libxfixes3 libxrandr2
To check for missing libraries: ldd ~/.cache/ms-playwright/chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell | grep "not found"
Configuration
Create a .env file with your library credentials:
HMCPL_BARCODE=your_library_card_number
HMCPL_PIN=your_pin
Usage
# View account summary
uv run hmcpl status
# List checkouts
uv run hmcpl checkouts
uv run hmcpl checkouts --due-soon 3 # Items due within 3 days
uv run hmcpl checkouts --overdue # Only overdue items
# List holds
uv run hmcpl holds
uv run hmcpl holds --ready # Items ready for pickup
uv run hmcpl holds --pending # Pending holds only
# Search catalog
uv run hmcpl search "python programming"
uv run hmcpl search "stephen king" --index Author --limit 10
# Place a hold
uv run hmcpl hold <record-id> --pickup "South Huntsville"
# Renew items
uv run hmcpl renew <item-id>
uv run hmcpl renew --all
# Force re-login (clear cached session)
uv run hmcpl --relogin status
# Set browser timeout (default 60s, useful for slow connections or Cloudflare delays)
uv run hmcpl --timeout 300 bootstrap
Output
All commands output JSON to stdout. Errors are written to stderr as JSON.
Example status output:
{
"num_checked_out": 3,
"num_overdue": 0,
"num_holds": 2,
"num_available_holds": 1,
"total_fines": 0.0,
"expires": "2027-11-01",
"name": "MARK AARON ROBERT K."
}
Headless Mode (Server Environments)
For server environments without a display (e.g. OpenClaw, CI), use headless mode. Headless mode scrapes rendered pages via a headless Chromium browser instead of calling AJAX APIs (which Cloudflare blocks).
Setup
-
Bootstrap once on a machine with a display:
uv run hmcpl bootstrap
# Use --timeout 300 if Cloudflare challenges take a while
This opens a browser, logs in, and saves the full browser state to ~/.hmcpl_browser_state.json.
-
Copy the state file to your server:
scp ~/.hmcpl_browser_state.json server:~/.hmcpl_browser_state.json
-
Use headless mode on the server:
uv run hmcpl --headless status
Or set the environment variable for all commands:
export HMCPL_HEADLESS=1
uv run hmcpl status # Now runs headless automatically
Headless Mode Limitations
- Holds detail: The
/MyAccount/Holds URL is blocked by Cloudflare WAF (the word "Hold" triggers a rule). The holds command returns [] in headless mode. Use status for hold counts.
- Session expiry: If the saved browser state expires, re-run
bootstrap on a machine with a display and copy the state file again.
Architecture
Non-headless mode
- Playwright for login (Cloudflare bypass requires real browser)
- httpx for AJAX API calls (faster for authenticated requests)
Headless mode
Cloudflare blocks httpx entirely (TLS fingerprinting) and even blocks fetch() from within Playwright pages to AJAX endpoints. Headless mode uses a completely different approach:
- Single persistent Playwright page for all operations
- Page scraping of rendered HTML instead of AJAX calls
_navigate() helper that skips navigation if already on the target URL (Cloudflare blocks repeated page.goto() to the same URL)
page.evaluate() for JS-loaded content like the user's name
Key files:
| File | Purpose |
|---|
hmcpl/client.py | Main client with headless/non-headless branching |
hmcpl/parser.py | HTML parsers for both AJAX responses and full pages |
hmcpl/cli.py | CLI argument parsing and command dispatch |
hmcpl/models.py | Pydantic data models |
~/.hmcpl_state.json | Cached session cookies (simple format) |
~/.hmcpl_browser_state.json | Full Playwright browser state for headless mode |
Cloudflare Constraints (important for contributors)
These behaviors are specific to catalog.hmcpl.org and must be preserved: