268 lines
9.0 KiB
Python
268 lines
9.0 KiB
Python
import logging
|
|
from typing import Optional
|
|
|
|
import requests
|
|
from django.core.files.base import ContentFile
|
|
from videogames.howlongtobeat import lookup_game_from_hltb
|
|
from videogames.igdb import lookup_game_from_igdb
|
|
from videogames.models import VideoGame, VideoGamePlatform
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_or_create_videogame(
|
|
name_or_id: str,
|
|
force_update: bool = False,
|
|
) -> Optional[VideoGame]:
|
|
"""Look up game by name or ID from HowLongToBeat, then enrich with IGDB"""
|
|
|
|
hltb_data = lookup_game_from_hltb(name_or_id)
|
|
|
|
if hltb_data:
|
|
game = _create_update_from_dict(hltb_data, force_update)
|
|
else:
|
|
igdb_data = lookup_game_from_igdb(name_or_id)
|
|
if igdb_data:
|
|
game = _create_update_from_dict(igdb_data, force_update)
|
|
else:
|
|
return None
|
|
|
|
if game:
|
|
game.fix_metadata()
|
|
return game
|
|
|
|
|
|
def _create_update_from_dict(
|
|
game_dict: dict, force_update: bool = False
|
|
) -> Optional[VideoGame]:
|
|
|
|
platform_ids = []
|
|
if "platforms" in game_dict.keys():
|
|
platforms = game_dict.get("platforms", [])
|
|
if platforms:
|
|
for platform in platforms:
|
|
p, _created = VideoGamePlatform.objects.get_or_create(name=platform)
|
|
platform_ids.append(p.id)
|
|
game_dict.pop("platforms")
|
|
|
|
cover_url = game_dict.pop("cover_url")
|
|
|
|
screenshot_url = ""
|
|
if "screenshot_url" in game_dict.keys():
|
|
screenshot_url = game_dict.pop("screenshot_url")
|
|
|
|
genres = []
|
|
if "genres" in game_dict.keys():
|
|
genres = game_dict.pop("genres")
|
|
|
|
title = game_dict.get("title")
|
|
if not title:
|
|
return None
|
|
|
|
hltb_id = game_dict.get("hltb_id")
|
|
igdb_id = game_dict.get("igdb_id")
|
|
if hltb_id:
|
|
game, game_created = VideoGame.objects.get_or_create(
|
|
hltb_id=hltb_id,
|
|
title=title,
|
|
)
|
|
elif igdb_id:
|
|
game, game_created = VideoGame.objects.get_or_create(
|
|
igdb_id=igdb_id,
|
|
title=title,
|
|
)
|
|
else:
|
|
game, game_created = VideoGame.objects.get_or_create(title=title)
|
|
|
|
if game_created or force_update:
|
|
VideoGame.objects.filter(pk=game.id).update(**game_dict)
|
|
game.refresh_from_db()
|
|
|
|
if platform_ids:
|
|
game.platforms.add(*platform_ids)
|
|
|
|
if genres:
|
|
game.genre.add(*genres)
|
|
|
|
if screenshot_url:
|
|
r = requests.get(screenshot_url)
|
|
if r.status_code == 200:
|
|
fname = f"{game.title}_{game.uuid}.jpg"
|
|
game.screenshot.save(fname, ContentFile(r.content), save=True)
|
|
|
|
if cover_url:
|
|
headers = {"User-Agent": "Vrobbler 0.11.12"}
|
|
r = requests.get(cover_url, headers=headers)
|
|
logger.debug(r.status_code)
|
|
if r.status_code == 200:
|
|
fname = f"{game.title}_cover_{game.uuid}.jpg"
|
|
game.hltb_cover.save(fname, ContentFile(r.content), save=True)
|
|
logger.debug("Loaded cover image from HLtB")
|
|
|
|
tag = "hltb-enriched" if hltb_id else "igdb-enriched"
|
|
if tag not in game.tags.names():
|
|
game.tags.add(tag)
|
|
logger.info(f"Game {game} tagged {tag}")
|
|
|
|
return game
|
|
|
|
|
|
def load_game_data_from_hltb(
|
|
game_id: int, expected_title: str = ""
|
|
) -> Optional[VideoGame]:
|
|
"""Look up HLtB data for an existing game and apply it"""
|
|
game = VideoGame.objects.filter(id=game_id).first()
|
|
if not game:
|
|
logger.warn(f"Video game with ID {game_id} not found")
|
|
return
|
|
|
|
logger.info(f"Looking up HLtB data for {game}")
|
|
hltb_data = lookup_game_from_hltb(game.title, search_by_title=True)
|
|
if not hltb_data:
|
|
logger.warn(f"No HLtB data found for {game}")
|
|
return
|
|
|
|
update_fields = []
|
|
|
|
hltb_title = hltb_data.get("title", "")
|
|
if hltb_title and hltb_title != game.title:
|
|
game.title = hltb_title
|
|
update_fields.append("title")
|
|
logger.info(f"Game {game.id} title updated to {hltb_title!r}")
|
|
|
|
if hltb_data.get("hltb_id") and (game.hltb_id is None):
|
|
game.hltb_id = hltb_data["hltb_id"]
|
|
update_fields.append("hltb_id")
|
|
|
|
if hltb_data.get("release_year") and (game.release_year is None):
|
|
game.release_year = hltb_data["release_year"]
|
|
update_fields.append("release_year")
|
|
|
|
if hltb_data.get("main_story_time") and (game.main_story_time is None):
|
|
game.main_story_time = hltb_data["main_story_time"]
|
|
update_fields.append("main_story_time")
|
|
|
|
if hltb_data.get("main_extra_time") and (game.main_extra_time is None):
|
|
game.main_extra_time = hltb_data["main_extra_time"]
|
|
update_fields.append("main_extra_time")
|
|
|
|
if hltb_data.get("completionist_time") and (game.completionist_time is None):
|
|
game.completionist_time = hltb_data["completionist_time"]
|
|
update_fields.append("completionist_time")
|
|
|
|
if hltb_data.get("hltb_score") is not None and (game.hltb_score is None):
|
|
game.hltb_score = hltb_data["hltb_score"]
|
|
update_fields.append("hltb_score")
|
|
|
|
if update_fields:
|
|
game.save(update_fields=update_fields)
|
|
|
|
platforms = hltb_data.get("platforms", [])
|
|
if platforms:
|
|
for name in platforms:
|
|
p, _ = VideoGamePlatform.objects.get_or_create(name=name)
|
|
game.platforms.add(p)
|
|
|
|
cover_url = hltb_data.get("cover_url")
|
|
if cover_url:
|
|
headers = {"User-Agent": "Vrobbler 0.11.12"}
|
|
r = requests.get(cover_url, headers=headers)
|
|
if r.status_code == 200:
|
|
fname = f"{game.title}_cover_{game.uuid}.jpg"
|
|
game.hltb_cover.save(fname, ContentFile(r.content), save=True)
|
|
|
|
if "hltb-enriched" not in game.tags.names():
|
|
game.tags.add("hltb-enriched")
|
|
logger.info(f"Game {game} tagged hltb-enriched")
|
|
|
|
return game
|
|
|
|
|
|
def load_game_data_from_igdb(
|
|
game_id: int, igdb_id: str = "", expected_title: str = ""
|
|
) -> Optional[VideoGame]:
|
|
"""Look up game, if it doesn't exist, lookup data from igdb"""
|
|
game = VideoGame.objects.filter(id=game_id).first()
|
|
if not game:
|
|
logger.warn(f"Video game with ID {game_id} not found")
|
|
return
|
|
|
|
if not game.igdb_id and not igdb_id:
|
|
logger.warn(f"Video game has no IGDB ID")
|
|
return
|
|
|
|
igdb_id = game.igdb_id or igdb_id
|
|
|
|
logger.info(f"Looking up video game {igdb_id}")
|
|
game_dict = lookup_game_from_igdb(igdb_id)
|
|
|
|
if not game_dict:
|
|
logger.warn(f"No game data found on IGDB for ID {igdb_id}")
|
|
return
|
|
|
|
igdb_title = game_dict.get("title", "")
|
|
igdb_title_clean = igdb_title.split(" (")[0].strip() if " (" in igdb_title else igdb_title
|
|
expected = expected_title or game.title
|
|
if igdb_title_clean.lower() != expected.lower():
|
|
logger.info(
|
|
f"IGDB title {igdb_title!r} doesn't match expected {expected!r} for {game} — re-searching…"
|
|
)
|
|
from videogames.igdb import lookup_game_id_from_gdb
|
|
|
|
new_id = lookup_game_id_from_gdb(expected)
|
|
if new_id:
|
|
new_data = lookup_game_from_igdb(str(new_id))
|
|
if new_data:
|
|
new_title = new_data.get("title", "")
|
|
new_title_clean = new_title.split(" (")[0].strip() if " (" in new_title else new_title
|
|
if new_title_clean.lower() == expected.lower():
|
|
game_dict = new_data
|
|
igdb_id = int(new_id)
|
|
if game.igdb_id != igdb_id:
|
|
game.igdb_id = igdb_id
|
|
game.save(update_fields=["igdb_id"])
|
|
logger.info(f"Game {game} IGDB ID updated to {igdb_id}")
|
|
|
|
igdb_title = game_dict.get("title", "")
|
|
igdb_title_clean = igdb_title.split(" (")[0].strip() if " (" in igdb_title else igdb_title
|
|
if igdb_title_clean.lower() != expected.lower():
|
|
if "igdb-mismatch" not in game.tags.names():
|
|
game.tags.add("igdb-mismatch")
|
|
logger.info(
|
|
f"Game {game} tagged igdb-mismatch (IGDB: {igdb_title!r} vs expected: {expected!r})"
|
|
)
|
|
return
|
|
|
|
screenshot_url = game_dict.pop("screenshot_url")
|
|
cover_url = game_dict.pop("cover_url")
|
|
genres = game_dict.pop("genres")
|
|
platforms = game_dict.pop("platforms", [])
|
|
|
|
VideoGame.objects.filter(pk=game.id).update(**game_dict)
|
|
game.refresh_from_db()
|
|
|
|
game.genre.add(*genres)
|
|
|
|
if platforms:
|
|
for name in platforms:
|
|
p, _ = VideoGamePlatform.objects.get_or_create(name=name)
|
|
game.platforms.add(p)
|
|
|
|
if screenshot_url:
|
|
r = requests.get(screenshot_url)
|
|
if r.status_code == 200:
|
|
fname = f"{game.title}_{game.uuid}.jpg"
|
|
game.screenshot.save(fname, ContentFile(r.content), save=True)
|
|
|
|
if cover_url:
|
|
r = requests.get(cover_url)
|
|
if r.status_code == 200:
|
|
fname = f"{game.title}_{game.uuid}.jpg"
|
|
game.cover.save(fname, ContentFile(r.content), save=True)
|
|
|
|
if "igdb-enriched" not in game.tags.names():
|
|
game.tags.add("igdb-enriched")
|
|
logger.info(f"Game {game} tagged igdb-enriched")
|
|
|
|
return game
|