Add HowLongToBeat estimates to game cards
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

- Fetch HLTB main/extra/completionist hours when a game item is saved
- Re-fetch only when name or category changes on edit
- Steam imports also fetch HLTB for each selected game
- Cards show compact HLTB row: "HLTB: 40h · +extra 60h · 100% 100h"
- Edit form shows HLTB breakdown as a hint next to Total hours field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-31 23:04:36 +03:00
parent a4c31bf40b
commit ffcd8c40b4
7 changed files with 100 additions and 2 deletions

48
backlogger/hltb.py Normal file
View File

@@ -0,0 +1,48 @@
from howlongtobeatpy import HowLongToBeat
def _h(val):
"""Return float hours if valid, else None."""
try:
v = float(val)
return v if v > 0 else None
except (TypeError, ValueError):
return None
def fetch(game_name):
"""
Search HowLongToBeat for game_name.
Returns dict with keys 'main', 'extra', 'complete' (each float or None),
or None if nothing found / on any error.
"""
try:
results = HowLongToBeat().search(game_name)
except Exception:
return None
if not results:
return None
best = max(results, key=lambda r: r.similarity)
if best.similarity < 0.4:
return None
return {
'main': _h(best.main_story),
'extra': _h(best.main_extra),
'complete': _h(best.completionist),
}
def apply_to_item(item):
"""Fetch HLTB data and save it onto item. Silently does nothing on failure."""
if item.category != 'games' or not item.name:
return
data = fetch(item.name)
if data is None:
return
item.hltb_main = data['main']
item.hltb_extra = data['extra']
item.hltb_complete = data['complete']
item.save(update_fields=['hltb_main', 'hltb_extra', 'hltb_complete'])