backloggd-py 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.
- backloggd_py-0.1.0/.gitignore +74 -0
- backloggd_py-0.1.0/LICENSE +21 -0
- backloggd_py-0.1.0/PKG-INFO +409 -0
- backloggd_py-0.1.0/README.md +373 -0
- backloggd_py-0.1.0/pyproject.toml +75 -0
- backloggd_py-0.1.0/src/backloggd/__init__.py +74 -0
- backloggd_py-0.1.0/src/backloggd/analytics/__init__.py +3 -0
- backloggd_py-0.1.0/src/backloggd/analytics/matcher.py +152 -0
- backloggd_py-0.1.0/src/backloggd/cache.py +621 -0
- backloggd_py-0.1.0/src/backloggd/client.py +791 -0
- backloggd_py-0.1.0/src/backloggd/exceptions.py +34 -0
- backloggd_py-0.1.0/src/backloggd/models/__init__.py +31 -0
- backloggd_py-0.1.0/src/backloggd/models/comparison.py +31 -0
- backloggd_py-0.1.0/src/backloggd/models/game.py +83 -0
- backloggd_py-0.1.0/src/backloggd/models/journal.py +23 -0
- backloggd_py-0.1.0/src/backloggd/models/library.py +157 -0
- backloggd_py-0.1.0/src/backloggd/models/list.py +28 -0
- backloggd_py-0.1.0/src/backloggd/models/review.py +23 -0
- backloggd_py-0.1.0/src/backloggd/models/user.py +48 -0
- backloggd_py-0.1.0/src/backloggd/parsers/__init__.py +29 -0
- backloggd_py-0.1.0/src/backloggd/parsers/game_page.py +90 -0
- backloggd_py-0.1.0/src/backloggd/parsers/games.py +149 -0
- backloggd_py-0.1.0/src/backloggd/parsers/journal.py +94 -0
- backloggd_py-0.1.0/src/backloggd/parsers/lists.py +112 -0
- backloggd_py-0.1.0/src/backloggd/parsers/profile.py +124 -0
- backloggd_py-0.1.0/src/backloggd/parsers/reviews.py +222 -0
- backloggd_py-0.1.0/src/backloggd/parsers/search.py +112 -0
- backloggd_py-0.1.0/src/backloggd/parsers/social.py +54 -0
- backloggd_py-0.1.0/src/backloggd/py.typed +1 -0
- backloggd_py-0.1.0/src/backloggd/sync_client.py +794 -0
- backloggd_py-0.1.0/src/backloggd/utils.py +134 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# C extensions
|
|
8
|
+
*.so
|
|
9
|
+
|
|
10
|
+
# Distribution / packaging
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# Virtual environments
|
|
30
|
+
.venv/
|
|
31
|
+
env/
|
|
32
|
+
venv/
|
|
33
|
+
ENV/
|
|
34
|
+
env.bak/
|
|
35
|
+
venv.bak/
|
|
36
|
+
|
|
37
|
+
# Testing & Coverage
|
|
38
|
+
.pytest_cache/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
|
|
50
|
+
# Agent & AI tool metadata
|
|
51
|
+
.agents/
|
|
52
|
+
.gemini/
|
|
53
|
+
|
|
54
|
+
# IDE & Editor files
|
|
55
|
+
.idea/
|
|
56
|
+
.vscode/
|
|
57
|
+
*.swp
|
|
58
|
+
*.swo
|
|
59
|
+
*~
|
|
60
|
+
.DS_Store
|
|
61
|
+
Thumbs.db
|
|
62
|
+
|
|
63
|
+
# Output & Cache files
|
|
64
|
+
*.csv
|
|
65
|
+
.cache/
|
|
66
|
+
*.db
|
|
67
|
+
*.sqlite3
|
|
68
|
+
|
|
69
|
+
# Local credentials & secrets
|
|
70
|
+
cookies.local.json
|
|
71
|
+
*.local.json
|
|
72
|
+
.env
|
|
73
|
+
.env.*
|
|
74
|
+
!.env.example
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Arda Günaydın
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: backloggd-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast, typed async & sync Python SDK, web scraper, and taste-matching engine for Backloggd.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ardax12/backloggd-matcher
|
|
6
|
+
Project-URL: Documentation, https://github.com/ardax12/backloggd-matcher#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/ardax12/backloggd-matcher
|
|
8
|
+
Project-URL: Issues, https://github.com/ardax12/backloggd-matcher/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/ardax12/backloggd-matcher/releases
|
|
10
|
+
Author: Arda
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: backloggd,gaming,letterboxd,recommendation-engine,scraper,sdk,taste-matcher,video-games
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Games/Entertainment
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
30
|
+
Requires-Dist: httpx[http2]>=0.28.1
|
|
31
|
+
Requires-Dist: lxml>=6.1.3
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# backloggd-py
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/backloggd-py/)
|
|
40
|
+
[](https://www.python.org/)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
[](#testing)
|
|
43
|
+
[](https://github.com/psf/black)
|
|
44
|
+
|
|
45
|
+
A modern, high-performance async & sync Python SDK, web scraper, and taste-matching recommendation engine for [Backloggd](https://backloggd.com).
|
|
46
|
+
|
|
47
|
+
Because Backloggd has no public REST API, `backloggd-py` provides an ergonomic, fully-typed interface to extract user libraries, profile cards, play journals, game reviews, and catalog queries—complete with an offline SQLite cache and Bayesian taste similarity analysis.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Highlights
|
|
52
|
+
|
|
53
|
+
- **Dual Sync & Async Engines**: Use `Backloggd` for scripts, notebooks, and ETL, or `BackloggdClient` with `async`/`await` for Discord bots and FastAPI services.
|
|
54
|
+
- **Sub-Second Profile Scraping**: Direct `/u/<username>/` parser fetches avatars, bios, badges, pinned favorites, and quick counters in ~300ms without paginating thousands of library items.
|
|
55
|
+
- **Path-Based Sorting & Filtering**: Natural Python dictionaries that map cleanly to Backloggd's native URL path segments (`sort="rating"`, `filter={"genre": "rpg", "platform": "ps5"}`).
|
|
56
|
+
- **Targeted Collections**: Rapidly query active play statuses (`playing`, `backlog`, `wishlist`) without fetching entire backlogs.
|
|
57
|
+
- **Global Catalog Browsing**: Search any game or explore top games filtered by platform, genre, and release year.
|
|
58
|
+
- **Social Graph & Mutuals**: Extract followers and following lists, and automatically compute mutual friends.
|
|
59
|
+
- **Taste Recommendation Engine**: Bayesian Pearson correlation, Szymkiewicz-Simpson overlap, and Jaccard metrics to compare gaming tastes and recommend unplayed gems.
|
|
60
|
+
- **Transparent Multi-Table SQLite Cache**: Built-in TTL caching per entity (Profiles, Libraries, Games, Reviews, Lists) with instant invalidation.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from backloggd import Backloggd, GameStatus
|
|
68
|
+
|
|
69
|
+
with Backloggd() as client:
|
|
70
|
+
# 1. Instant profile overview (~300ms)
|
|
71
|
+
profile = client.get_user_profile("ardax12")
|
|
72
|
+
print(f"{profile.username} | {profile.played_count} played | Bio: {profile.bio}")
|
|
73
|
+
|
|
74
|
+
# 2. Paginated user library
|
|
75
|
+
library = client.get_user_library("ardax12", max_pages=2)
|
|
76
|
+
top_games = library.filter(status=GameStatus.PLAYED, min_rating=4.5)
|
|
77
|
+
print(f"Top rated games ({len(top_games)}): {[g.title for g in top_games]}")
|
|
78
|
+
|
|
79
|
+
# 3. Taste comparison between two users
|
|
80
|
+
match = client.compare_users("ardax12", "nehir")
|
|
81
|
+
print(f"Compatibility: {match.similarity_score}%")
|
|
82
|
+
print("Mutual Favorites:", [g.title for g in match.mutual_favorites[:3]])
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
### From Source (Current)
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/ardax12/backloggd-matcher.git
|
|
93
|
+
cd backloggd-matcher
|
|
94
|
+
pip install -e .
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or using [`uv`](https://github.com/astral-sh/uv):
|
|
98
|
+
```bash
|
|
99
|
+
uv pip install -e ".[dev]"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### From PyPI (Upcoming v0.1.0 release)
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install backloggd-py
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Authentication
|
|
111
|
+
|
|
112
|
+
Backloggd uses Cloudflare / Anubis bot verification. Providing your active session cookie bypasses challenge gates and unlocks full, unthrottled access.
|
|
113
|
+
|
|
114
|
+
### 1. Environment Variable (Recommended for CI / Docker / Cloud)
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
export BACKLOGGD_SESSION="your_cookie_value_here"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. `cookies.json` File
|
|
121
|
+
|
|
122
|
+
Extract `_backloggd_session` from DevTools (`F12` -> Application -> Cookies -> `https://backloggd.com`) and save to `cookies.json`:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
[
|
|
126
|
+
{
|
|
127
|
+
"name": "_backloggd_session",
|
|
128
|
+
"value": "your_cookie_value_here",
|
|
129
|
+
"domain": "backloggd.com"
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
*(A flat dictionary `{"_backloggd_session": "..."}` is also supported.)*
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Usage Guide
|
|
139
|
+
|
|
140
|
+
### 1. User Profiles (`get_user_profile`)
|
|
141
|
+
|
|
142
|
+
Fetches user metadata from the root profile page without scanning the game library:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
with Backloggd() as client:
|
|
146
|
+
profile = client.get_user_profile("ardax12")
|
|
147
|
+
|
|
148
|
+
print(profile.username) # "ardax12"
|
|
149
|
+
print(profile.avatar_url) # "https://images.backloggd.com/..."
|
|
150
|
+
print(profile.bio) # User bio text
|
|
151
|
+
print(profile.badges) # ["goty_2025", "total_played3", ...]
|
|
152
|
+
print(profile.short_url) # "https://bckl.gg/..."
|
|
153
|
+
print(profile.played_count) # 248
|
|
154
|
+
print(profile.playing_count) # 3
|
|
155
|
+
print(profile.backlog_count) # 112
|
|
156
|
+
print(profile.wishlist_count) # 54
|
|
157
|
+
|
|
158
|
+
# Pinned favorite games (list[GameEntry])
|
|
159
|
+
for game in profile.favorite_games:
|
|
160
|
+
print(f"★ {game.title} (Cover: {game.cover_url})")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 2. User Libraries & Collections
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
with Backloggd() as client:
|
|
167
|
+
# Full library with pagination limit
|
|
168
|
+
library = client.get_user_library("ardax12", max_pages=3)
|
|
169
|
+
|
|
170
|
+
# Filter in-memory
|
|
171
|
+
played_5_stars = library.filter(status="played", min_rating=5.0)
|
|
172
|
+
|
|
173
|
+
# Export to CSV or Python dict
|
|
174
|
+
library.to_csv("my_library.csv")
|
|
175
|
+
records = library.to_dict()
|
|
176
|
+
|
|
177
|
+
# Fast targeted queries (bypasses full library pagination)
|
|
178
|
+
playing = client.get_user_playing("ardax12")
|
|
179
|
+
backlog = client.get_user_backlog("ardax12", max_pages=1)
|
|
180
|
+
wishlist = client.get_user_wishlist("ardax12", max_pages=1)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 3. Path-Based Sorting & Filtering
|
|
184
|
+
|
|
185
|
+
Backloggd organizes filters as URL path segments (e.g. `/games/rating/type:played;release_platform:ps5/`). The SDK translates standard Python dictionaries:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
with Backloggd() as client:
|
|
189
|
+
# Supported sort keys: "popular", "rating", "release", "title", "last_played", "played"
|
|
190
|
+
top_ps5_played = client.get_user_library(
|
|
191
|
+
"ardax12",
|
|
192
|
+
sort="rating",
|
|
193
|
+
filter={"type": "played", "platform": "ps5"},
|
|
194
|
+
max_pages=2,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Sort targeted collections
|
|
198
|
+
wishlist_by_popularity = client.get_user_wishlist("ardax12", sort="popular")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 4. Global Games Catalog Browsing
|
|
202
|
+
|
|
203
|
+
Explore or search games across Backloggd:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
with Backloggd() as client:
|
|
207
|
+
# Search for a specific title
|
|
208
|
+
results = client.search_games("Metaphor ReFantazio")
|
|
209
|
+
for r in results:
|
|
210
|
+
print(f"{r.title} ({r.release_year}) -> {r.slug}")
|
|
211
|
+
|
|
212
|
+
# Browse catalog with sorting & category filtering
|
|
213
|
+
popular_rpgs = client.browse_games(
|
|
214
|
+
sort="popular",
|
|
215
|
+
filter={"genre": "role-playing-rpg", "platform": "win"},
|
|
216
|
+
max_pages=1,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Game details
|
|
220
|
+
elden_ring = client.get_game("elden-ring")
|
|
221
|
+
print(f"{elden_ring.title}: {elden_ring.rating_stars}* ({elden_ring.plays_count} plays)")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 5. Social Graph: Followers, Following & Friends
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
with Backloggd() as client:
|
|
228
|
+
following = client.get_user_following("ardax12", max_pages=1)
|
|
229
|
+
followers = client.get_user_followers("ardax12", max_pages=1)
|
|
230
|
+
|
|
231
|
+
# Automatically compute mutual friends (followers ∩ following)
|
|
232
|
+
friends = client.get_user_friends("ardax12")
|
|
233
|
+
for friend in friends:
|
|
234
|
+
print(f"Mutual friend: {friend.username} ({friend.url})")
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 6. Play Journal & Community Reviews
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
with Backloggd() as client:
|
|
241
|
+
# Completion journal
|
|
242
|
+
journal = client.get_user_journal("ardax12", max_pages=1)
|
|
243
|
+
for entry in journal:
|
|
244
|
+
replay = " [Replay]" if entry.is_replay else ""
|
|
245
|
+
print(f"{entry.played_date}: {entry.game_title} - {entry.rating}*{replay}")
|
|
246
|
+
|
|
247
|
+
# Game reviews
|
|
248
|
+
reviews = client.get_game_reviews("hollow-knight", max_pages=1)
|
|
249
|
+
for rev in reviews[:3]:
|
|
250
|
+
print(f"{rev.author} ({rev.rating}*): {rev.review_text[:100]}...")
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 7. Taste Matching & Recommendations
|
|
254
|
+
|
|
255
|
+
Computes taste compatibility using normalized Bayesian Pearson correlation, Jaccard overlap, and relative rating agreement:
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
with Backloggd() as client:
|
|
259
|
+
match = client.compare_users("ardax12", "nehir", max_pages=2)
|
|
260
|
+
|
|
261
|
+
print(f"Match Score: {match.similarity_score}%")
|
|
262
|
+
print(f"Shared Games: {match.overlap_count}")
|
|
263
|
+
|
|
264
|
+
print("Mutual Favorites:")
|
|
265
|
+
for g in match.mutual_favorites:
|
|
266
|
+
print(f" - {g.title}")
|
|
267
|
+
|
|
268
|
+
print("Polarizing Disagreements:")
|
|
269
|
+
for d in match.disagreements:
|
|
270
|
+
print(f" - {d.title}: {d.user1_rating}* vs {d.user2_rating}*")
|
|
271
|
+
|
|
272
|
+
print("Recommendations for User 1:")
|
|
273
|
+
for rec in match.recommendations_for_user1[:5]:
|
|
274
|
+
print(f" - {rec.title}")
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 8. Asynchronous Client (`BackloggdClient`)
|
|
278
|
+
|
|
279
|
+
For high-throughput bots, crawlers, or asynchronous web frameworks:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
import asyncio
|
|
283
|
+
from backloggd import BackloggdClient
|
|
284
|
+
|
|
285
|
+
async def main():
|
|
286
|
+
async with BackloggdClient(cookies_file="cookies.json") as client:
|
|
287
|
+
# Run requests concurrently
|
|
288
|
+
profile, playing, catalog = await asyncio.gather(
|
|
289
|
+
client.get_user_profile("ardax12"),
|
|
290
|
+
client.get_user_playing("ardax12"),
|
|
291
|
+
client.browse_games(sort="popular", max_pages=1),
|
|
292
|
+
)
|
|
293
|
+
print(f"Profile: {profile.username}")
|
|
294
|
+
print(f"Currently playing: {len(playing)} games")
|
|
295
|
+
print(f"Top game: {catalog[0].title}")
|
|
296
|
+
|
|
297
|
+
asyncio.run(main())
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 9. SQLite Caching & Invalidation
|
|
301
|
+
|
|
302
|
+
All responses are cached locally in SQLite (`.cache/backloggd_cache.db`) with granular TTLs:
|
|
303
|
+
|
|
304
|
+
| Entity | SQLite Table | Default TTL |
|
|
305
|
+
|:---|:---|:---|
|
|
306
|
+
| User Profile | `profile_cache` | 24 Hours (`86400s`) |
|
|
307
|
+
| User Library | `library_cache` | 24 Hours (`86400s`) |
|
|
308
|
+
| Game Details | `game_cache` | 7 Days (`604800s`) |
|
|
309
|
+
| Game Reviews | `review_cache` | 24 Hours (`86400s`) |
|
|
310
|
+
| Custom Lists | `list_cache` | 24 Hours (`86400s`) |
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
# Custom TTL configuration
|
|
314
|
+
client = Backloggd(
|
|
315
|
+
profile_ttl=3600.0, # 1 hour
|
|
316
|
+
library_ttl=3600.0, # 1 hour
|
|
317
|
+
game_ttl=86400.0 * 14, # 14 days
|
|
318
|
+
cache_path="data/custom_cache.db",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# Invalidation helpers
|
|
322
|
+
client.cache.delete_profile("ardax12")
|
|
323
|
+
client.cache.delete_library("ardax12")
|
|
324
|
+
client.cache.delete_game("elden-ring")
|
|
325
|
+
client.cache.clear() # Flush entire database
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 10. Error Handling
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
from backloggd import Backloggd
|
|
332
|
+
from backloggd.exceptions import (
|
|
333
|
+
BackloggdError,
|
|
334
|
+
ChallengeResponseError,
|
|
335
|
+
RateLimitError,
|
|
336
|
+
UserNotFoundError,
|
|
337
|
+
GameNotFoundError,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
with Backloggd() as client:
|
|
341
|
+
try:
|
|
342
|
+
game = client.get_game("unknown-slug-xyz")
|
|
343
|
+
except GameNotFoundError:
|
|
344
|
+
print("Game not found on Backloggd.")
|
|
345
|
+
except ChallengeResponseError:
|
|
346
|
+
print("Cloudflare anti-bot challenge encountered. Update your session cookie.")
|
|
347
|
+
except RateLimitError:
|
|
348
|
+
print("HTTP 429 Rate limited. Automatic backoff failed.")
|
|
349
|
+
except BackloggdError as e:
|
|
350
|
+
print(f"SDK error: {e}")
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## API Parity Matrix
|
|
356
|
+
|
|
357
|
+
Both sync and async clients share identical capabilities and signatures:
|
|
358
|
+
|
|
359
|
+
| Capability | Synchronous (`Backloggd`) | Asynchronous (`BackloggdClient`) |
|
|
360
|
+
|:---|:---|:---|
|
|
361
|
+
| Profile Lookup | `get_user_profile(user)` | `await get_user_profile(user)` |
|
|
362
|
+
| Full Library | `get_user_library(user, ...)` | `await get_user_library(user, ...)` |
|
|
363
|
+
| Targeted Collections | `get_user_playing()`, `get_user_backlog()`, `get_user_wishlist()` | `await get_user_playing()`, `get_user_backlog()`, `get_user_wishlist()` |
|
|
364
|
+
| Browse Catalog | `browse_games(sort=..., filter=...)` | `await browse_games(sort=..., filter=...)` |
|
|
365
|
+
| Search Games | `search_games(query)` | `await search_games(query)` |
|
|
366
|
+
| Game Details | `get_game(slug)` | `await get_game(slug)` |
|
|
367
|
+
| Game Reviews | `get_game_reviews(slug)` | `await get_game_reviews(slug)` |
|
|
368
|
+
| User Reviews | `get_user_reviews(user)` | `await get_user_reviews(user)` |
|
|
369
|
+
| User Lists | `get_user_lists(user)`, `get_list(url)` | `await get_user_lists(user)`, `get_list(url)` |
|
|
370
|
+
| Social Graph | `get_user_followers()`, `get_user_following()`, `get_user_friends()` | `await get_user_followers()`, `get_user_following()`, `get_user_friends()` |
|
|
371
|
+
| Play Journal | `get_user_journal(user)` | `await get_user_journal(user)` |
|
|
372
|
+
| Taste Matching | `compare_users(user1, user2)` | `await compare_users(user1, user2)` |
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Testing
|
|
377
|
+
|
|
378
|
+
Run the full test suite with 28 unit and integration tests:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# Using pytest
|
|
382
|
+
pytest
|
|
383
|
+
|
|
384
|
+
# Or via uv
|
|
385
|
+
uv run pytest
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Roadmap
|
|
391
|
+
|
|
392
|
+
- [x] Dual sync & async client architecture with 100% method parity
|
|
393
|
+
- [x] Sub-second profile scraping (`get_user_profile`)
|
|
394
|
+
- [x] Full paginated library retrieval with bounded concurrency
|
|
395
|
+
- [x] Path-based URL sorting and multi-criteria filtering
|
|
396
|
+
- [x] Targeted instant collections (`playing`, `backlog`, `wishlist`)
|
|
397
|
+
- [x] Social network scraping and mutual friends calculation
|
|
398
|
+
- [x] Playthrough journal & community review extraction
|
|
399
|
+
- [x] Global games catalog browsing (`browse_games`)
|
|
400
|
+
- [x] Persistent multi-table SQLite cache with customizable TTLs
|
|
401
|
+
- [x] Bayesian taste similarity engine & recommendations
|
|
402
|
+
- [ ] Rich terminal CLI (`backloggd-cli`)
|
|
403
|
+
- [ ] PyPI publishing via GitHub Actions CI/CD
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## License
|
|
408
|
+
|
|
409
|
+
Distributed under the [MIT License](LICENSE).
|