lbd-followback 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ data/
3
+ output/
4
+ __pycache__/
5
+ *.pyc
6
+ dist/
7
+ *.egg-info/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,67 @@
1
+ # AGENTS.md — lbd-followback
2
+
3
+ ## Stack
4
+
5
+ - Python 3.14+, managed via `uv` (not pip/conda)
6
+ - `letterboxdpy` for scraping (NOT raw requests/BS4 — it handles TLS fingerprinting and retries)
7
+ - `rich` for terminal output
8
+ - `curl-cffi` (transitive via letterboxdpy) — Chrome impersonation, not stdlib `requests`
9
+
10
+ ## Commands
11
+
12
+ ```bash
13
+ lbd-followback <username> # after uv tool install .
14
+ uv run python -m lbd_followback <username> # or via uv
15
+ uv run python main.py <username> # same, thin wrapper
16
+ uv run python -m lbd_followback <username> -f # bypass 24h cache
17
+ uv add <pkg> # add dep
18
+ ```
19
+
20
+ No test/lint/typecheck scripts exist.
21
+
22
+ ## Architecture
23
+
24
+ ```
25
+ main.py → thin wrapper → lbd_followback.__main__.main()
26
+ lbd_followback/
27
+ __main__.py → CLI entrypoint (argparse)
28
+ __init__.py → re-exports scrape, analyze, generate
29
+ transport.py → HTTP session, retry, 429 backoff (depends on curl-cffi)
30
+ parser.py → BS4 HTML parsing + number extraction
31
+ scraper.py → pagination orchestration (uses transport + parser + cache)
32
+ cache.py → JSON disk cache with 24h TTL
33
+ compare.py → set math: mutual / not_following_back / not_followed_back
34
+ report.py → HTML report generation
35
+ config.py → constants (DOMAIN, delay, cache TTL)
36
+ ~/.cache/lbd-followback/ → cached JSON (auto-created)
37
+ CWD/<username>_report.html → output (default)
38
+ ```
39
+
40
+ ## Key constraints
41
+
42
+ - **Do not use letterboxdpy.User.get_following() directly** — it paginates internally without inter-page delay, triggering rate limits. Use `scraper.scrape()` which adds 3-4s delay between pages.
43
+ - **DOMAIN** must come from `config.py` (`https://letterboxd.com`), never hardcoded.
44
+ - **Number extraction** uses `parser.extract_number()` (regex), not ad-hoc `re.search`.
45
+ - **429 handling**: exponential backoff 30s→60s→120s in `transport.py`. Do not change without testing against real LBD rate limits.
46
+ - **Cache**: `~/.cache/lbd-followback/`, 24h TTL. Non-existent users are NOT cached (404 returns empty without saving).
47
+ - **No parallel requests** — sequential only. Parallel triggers rate limits immediately.
48
+ - **Entry point**: `lbd-followback = "lbd_followback.__main__:main"` in `[project.scripts]`. Also `python -m lbd_followback` works via `__main__.py`.
49
+ - **Public API** re-exported from `__init__.py`: `scrape`, `analyze`, `generate`.
50
+ - `rich.console.Console` is used for all user-facing output (not `print`).
51
+
52
+ ## Modules & boundaries
53
+
54
+ | Module | Depends on | Never import |
55
+ |--------|-----------|--------------|
56
+ | transport.py | curl-cffi, bs4, rich | scraper, parser, cache |
57
+ | parser.py | bs4, config | transport, scraper, cache |
58
+ | scraper.py | transport, parser, cache | curl-cffi, bs4 directly |
59
+ | cache.py | config, json, pathlib | scraper, transport, parser |
60
+ | compare.py | nothing | scraper, transport, parser, cache |
61
+ | report.py | config, rich | scraper, transport, parser, cache |
62
+
63
+ ## SOLID notes (applied)
64
+
65
+ - transport.py abstracts HTTP (swap curl-cffi → httpx by changing one file)
66
+ - parser.py abstracts BS4 (swap to selectolax by changing one file)
67
+ - scraper.py is an orchestrator only — no HTTP or parsing logic