Improve CheapShark Metacritic title matching

This commit is contained in:
2026-05-28 17:02:50 +09:00
parent a0204845c3
commit cb9c7681be
+71 -13
View File
@@ -48,6 +48,43 @@ _STORE_MAP = {
} }
_CS_DEDICATED_STORE_IDS = {"1", "7", "30"} _CS_DEDICATED_STORE_IDS = {"1", "7", "30"}
_MC_CACHE = {} _MC_CACHE = {}
_TITLE_SUFFIX_RE = re.compile(
r"\b("
r"complete|deluxe|ultimate|definitive|gold|premium|collector'?s|anniversary|remastered|remaster|"
r"goty|game\s+of\s+the\s+year|enhanced|extended|special|standard|starter|pack|bundle|edition"
r")\b",
re.IGNORECASE,
)
def _normalize_game_title(title):
text = str(title or "").lower()
text = re.sub(r"[™®©]", " ", text)
text = re.sub(r"[\[\(].*?[\]\)]", " ", text)
text = re.sub(r"\b\d{4}\b", " ", text)
text = re.sub(r"[:\-–—|_/]+", " ", text)
text = _TITLE_SUFFIX_RE.sub(" ", text)
text = re.sub(r"\s+", " ", text).strip()
return text
def _title_candidates(title):
base = str(title or "").strip()
normalized = _normalize_game_title(base)
variants = []
for item in (base, normalized):
item = str(item or "").strip()
if item and item not in variants:
variants.append(item)
if ":" in base:
head = _normalize_game_title(base.split(":", 1)[0])
if head and head not in variants:
variants.append(head)
if "-" in base:
head = _normalize_game_title(base.split("-", 1)[0])
if head and head not in variants:
variants.append(head)
return variants
def _metacritic_from_deal(d): def _metacritic_from_deal(d):
@@ -61,29 +98,50 @@ def _metacritic_from_deal(d):
return score, link return score, link
def _deal_match_score(deal, target):
deal_title = _normalize_game_title(deal.get("title") or "")
if not deal_title or not target:
return 0
if deal_title == target:
return 100
target_words = set(target.split())
deal_words = set(deal_title.split())
if len(target_words) <= 1:
return 0
if deal_title.startswith(target) or target.startswith(deal_title):
return 80
if not target_words or not deal_words:
return 0
overlap = len(target_words & deal_words)
return int((overlap / max(len(target_words), len(deal_words))) * 70)
def _cheapshark_metacritic_lookup(title="", steam_appid=None): def _cheapshark_metacritic_lookup(title="", steam_appid=None):
key = f"{steam_appid or ''}:{title or ''}".lower() key = f"{steam_appid or ''}:{title or ''}".lower()
if key in _MC_CACHE: if key in _MC_CACHE:
return _MC_CACHE[key] return _MC_CACHE[key]
params = {"pageSize": 5} if not (steam_appid or title):
if steam_appid:
params["steamAppID"] = steam_appid
elif title:
params["title"] = title
else:
_MC_CACHE[key] = (0, "") _MC_CACHE[key] = (0, "")
return _MC_CACHE[key] return _MC_CACHE[key]
try: try:
deals = _get(f"{_CS_BASE}/deals", params=params).json()
normalized = re.sub(r"\s+", " ", str(title or "").strip()).lower()
best = None best = None
for deal in deals or []: if steam_appid:
deal_title = re.sub(r"\s+", " ", str(deal.get("title") or "").strip()).lower() deals = _get(f"{_CS_BASE}/deals", params={"pageSize": 5, "steamAppID": steam_appid}).json()
if steam_appid or deal_title == normalized: best = next((deal for deal in deals or [] if int(float(deal.get("metacriticScore") or 0)) > 0), None)
best = deal
break
if best is None and deals: if best is None and deals:
best = deals[0] best = deals[0]
if best is None:
for candidate in _title_candidates(title):
deals = _get(f"{_CS_BASE}/deals", params={"pageSize": 12, "title": candidate}).json()
target = _normalize_game_title(candidate)
ranked = sorted(
(deal for deal in deals or [] if int(float(deal.get("metacriticScore") or 0)) > 0),
key=lambda deal: _deal_match_score(deal, target),
reverse=True,
)
if ranked and _deal_match_score(ranked[0], target) >= 45:
best = ranked[0]
break
_MC_CACHE[key] = _metacritic_from_deal(best or {}) _MC_CACHE[key] = _metacritic_from_deal(best or {})
except Exception as e: except Exception as e:
log.debug("CheapShark metacritic lookup failed title=%s appid=%s: %s", title, steam_appid, e) log.debug("CheapShark metacritic lookup failed title=%s appid=%s: %s", title, steam_appid, e)