162 lines
5.3 KiB
Python
162 lines
5.3 KiB
Python
import json
|
|
import logging
|
|
from datetime import datetime
|
|
from operator import itemgetter
|
|
from typing import Dict, Tuple
|
|
|
|
import pytz
|
|
import requests
|
|
from django.conf import settings
|
|
from django.contrib.auth import get_user_model
|
|
|
|
TWITCH_AUTH_BASE = "https://id.twitch.tv/"
|
|
REFRESH_TOKEN_URL = (
|
|
TWITCH_AUTH_BASE
|
|
+ "oauth2/token?client_id={id}&client_secret={secret}&grant_type=client_credentials"
|
|
)
|
|
SEARCH_URL = "https://api.igdb.com/v4/search"
|
|
GAMES_URL = "https://api.igdb.com/v4/games"
|
|
ALT_NAMES_URL = "https://api.igdb.com/v4/alternative_names"
|
|
SCREENSHOT_URL = "https://api.igdb.com/v4/screenshots"
|
|
COVER_URL = "https://api.igdb.com/v4/covers"
|
|
PLATFORMS_URL = "https://api.igdb.com/v4/platforms"
|
|
|
|
IGDB_CLIENT_ID = getattr(settings, "IGDB_CLIENT_ID")
|
|
IGDB_CLIENT_SECRET = getattr(settings, "IGDB_CLIENT_SECRET")
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
def get_igdb_token() -> str:
|
|
token_url = REFRESH_TOKEN_URL.format(id=IGDB_CLIENT_ID, secret=IGDB_CLIENT_SECRET)
|
|
response = requests.post(token_url)
|
|
results = json.loads(response.content)
|
|
return results.get("access_token")
|
|
|
|
|
|
def lookup_platform_names(platform_ids: list, headers: dict) -> list:
|
|
"""Resolve IGDB platform IDs to platform names"""
|
|
if not platform_ids:
|
|
return []
|
|
ids_str = ",".join(str(pid) for pid in platform_ids)
|
|
body = f"fields name; where id = ({ids_str});"
|
|
resp = requests.post(PLATFORMS_URL, data=body, headers=headers)
|
|
if resp.status_code != 200:
|
|
logger.warn(f"Failed to resolve platform IDs {platform_ids}")
|
|
return []
|
|
results = json.loads(resp.content)
|
|
return [p["name"] for p in results if "name" in p]
|
|
|
|
|
|
def lookup_game_id_from_gdb(name: str) -> str:
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {get_igdb_token()}",
|
|
"Client-ID": IGDB_CLIENT_ID,
|
|
}
|
|
if "(" in name:
|
|
name = name.split(" (")[0]
|
|
|
|
body = f'fields name,game,published_at; search "{name}"; limit 100;'
|
|
response = requests.post(SEARCH_URL, data=body, headers=headers)
|
|
results = json.loads(response.content)
|
|
if not results:
|
|
logger.warn(
|
|
f"Search of game on IGDB failed, no results found",
|
|
extra={"search_query": name},
|
|
)
|
|
return ""
|
|
|
|
if results[0].get("status") == 406:
|
|
logger.warn(
|
|
f"Search of game on IGBD failed, API error",
|
|
extra={
|
|
"cause": results.get("cause"),
|
|
"details": results.get("details"),
|
|
},
|
|
)
|
|
# Sort results by release date (oldest first) to prefer the original game
|
|
results = [r for r in results if r.get("game")]
|
|
results = sorted(results, key=lambda k: k.get("published_at") or 9999999999)
|
|
return results[0].get("game", "") if results else ""
|
|
|
|
|
|
def lookup_game_from_igdb(name_or_igdb_id: str) -> Dict:
|
|
"""Given credsa and an IGDB game ID, lookup the game metadata and return it
|
|
in a dictionary mapped to our internal game fields
|
|
|
|
"""
|
|
try:
|
|
igdb_id = int(name_or_igdb_id)
|
|
except ValueError:
|
|
igdb_id = lookup_game_id_from_gdb(name_or_igdb_id)
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {get_igdb_token()}",
|
|
"Client-ID": IGDB_CLIENT_ID,
|
|
}
|
|
fields = "id,name,alternative_names.*,genres.*,release_dates.*,cover.*,screenshots.*,rating,rating_count,summary"
|
|
|
|
game_dict = {}
|
|
body = f"fields {fields}; where id = {igdb_id}; sort id asc;"
|
|
response = requests.post(GAMES_URL, data=body, headers=headers)
|
|
results = json.loads(response.content)
|
|
if not results:
|
|
logger.warn(f"Lookup of game on IGDB failed for ID {igdb_id}")
|
|
return game_dict
|
|
|
|
game = results[0]
|
|
|
|
alt_name = None
|
|
if "alternative_names" in game.keys():
|
|
alt_name = game.get("alternative_names")[0].get("name")
|
|
screenshot_url = None
|
|
if "screenshots" in game.keys():
|
|
screenshot_url = "https:" + game.get("screenshots")[0].get("url").replace(
|
|
"t_thumb", "t_screenshot_big_2x"
|
|
)
|
|
cover_url = None
|
|
if "cover" in game.keys():
|
|
cover_url = "https:" + game.get("cover").get("url").replace(
|
|
"t_thumb", "t_cover_big_2x"
|
|
)
|
|
release_date = None
|
|
if "release_dates" in game.keys():
|
|
release_date = game.get("release_dates")[0].get("date")
|
|
if release_date:
|
|
release_date = datetime.utcfromtimestamp(release_date).replace(
|
|
tzinfo=pytz.utc
|
|
)
|
|
genres = []
|
|
if "genres" in game.keys():
|
|
for genre in game.get("genres"):
|
|
genres.append(genre["name"])
|
|
|
|
platforms = []
|
|
if "release_dates" in game.keys():
|
|
platform_ids = set()
|
|
for rd in game["release_dates"]:
|
|
pid = rd.get("platform")
|
|
if pid is not None:
|
|
platform_ids.add(pid)
|
|
if platform_ids:
|
|
platforms = lookup_platform_names(list(platform_ids), headers)
|
|
|
|
game_dict = {
|
|
"igdb_id": game.get("id"),
|
|
"title": game.get("name"),
|
|
"alternative_name": alt_name,
|
|
"screenshot_url": screenshot_url,
|
|
"cover_url": cover_url,
|
|
"rating": game.get("rating"),
|
|
"rating_count": game.get("rating_count"),
|
|
"release_date": release_date,
|
|
"summary": game.get("summary"),
|
|
"genres": genres,
|
|
"platforms": platforms,
|
|
}
|
|
|
|
return game_dict
|