Skip to content

Commit

Permalink
steamgridDB integration added
Browse files Browse the repository at this point in the history
  • Loading branch information
zurdi15 committed Jun 26, 2024
1 parent 8b1a63d commit 180c373
Show file tree
Hide file tree
Showing 15 changed files with 402 additions and 47 deletions.
2 changes: 2 additions & 0 deletions backend/endpoints/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from handler.filesystem import fs_platform_handler
from handler.metadata.igdb_handler import IGDB_API_ENABLED
from handler.metadata.moby_handler import MOBY_API_ENABLED
from handler.metadata.sgdb_handler import STEAMGRIDDB_API_ENABLED
from utils import get_version

router = APIRouter()
Expand All @@ -27,6 +28,7 @@ def heartbeat() -> HeartbeatResponse:
return {
"VERSION": get_version(),
"ANY_SOURCE_ENABLED": IGDB_API_ENABLED or MOBY_API_ENABLED,
"STEAMGRIDDB_ENABLED": STEAMGRIDDB_API_ENABLED,
"METADATA_SOURCES": {
"IGDB_API_ENABLED": IGDB_API_ENABLED,
"MOBY_API_ENABLED": MOBY_API_ENABLED,
Expand Down
1 change: 1 addition & 0 deletions backend/endpoints/responses/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ class HeartbeatResponse(TypedDict):
SCHEDULER: SchedulerDict
ANY_SOURCE_ENABLED: bool
METADATA_SOURCES: MetadataSourcesDict
STEAMGRIDDB_ENABLED: bool
FS_PLATFORMS: list
5 changes: 5 additions & 0 deletions backend/endpoints/responses/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ class SearchRomSchema(BaseModel):
summary: str
igdb_url_cover: str = ""
moby_url_cover: str = ""


class SearchCoverSchema(BaseModel):
name: str
resources: list
13 changes: 11 additions & 2 deletions backend/endpoints/search.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import emoji
from decorators.auth import protected_route
from endpoints.responses.search import SearchRomSchema
from endpoints.responses.search import SearchCoverSchema, SearchRomSchema
from fastapi import APIRouter, HTTPException, Request, status
from handler.database import db_rom_handler
from handler.metadata import meta_igdb_handler, meta_moby_handler
from handler.metadata import meta_igdb_handler, meta_moby_handler, meta_sgdb_handler
from handler.metadata.igdb_handler import IGDB_API_ENABLED
from handler.metadata.moby_handler import MOBY_API_ENABLED
from handler.scan_handler import _get_main_platform_igdb_id
Expand Down Expand Up @@ -107,3 +107,12 @@ async def search_rom(
log.info(f"\t - {m_rom['name']}")

return matched_roms


@protected_route(router.get, "/search/cover", ["roms.read"])
async def search_cover(
request: Request,
search_term: str | None = None,
) -> list[SearchCoverSchema]:

return meta_sgdb_handler.get_details(search_term)
17 changes: 11 additions & 6 deletions backend/handler/metadata/igdb_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,14 @@ def extract_metadata_from_igdb_rom(rom: dict) -> IGDBMetadata:

class IGDBBaseHandler(MetadataHandler):
def __init__(self) -> None:
self.platform_endpoint = "https://api.igdb.com/v4/platforms"
self.platform_version_endpoint = "https://api.igdb.com/v4/platform_versions"
self.platforms_fields = ["id", "name"]
self.games_endpoint = "https://api.igdb.com/v4/games"
self.BASE_URL = "https://api.igdb.com/v4"
self.platform_endpoint = f"{self.BASE_URL}/platforms"
self.platform_version_endpoint = f"{self.BASE_URL}/platform_versions"
self.platforms_fields = PLATFORMS_FIELDS
self.games_endpoint = f"{self.BASE_URL}/games"
self.games_fields = GAMES_FIELDS
self.search_endpoint = "https://api.igdb.com/v4/search"
self.search_fields = ["game.id", "name"]
self.search_endpoint = f"{self.BASE_URL}/search"
self.search_fields = SEARCH_FIELDS
self.pagination_limit = 200
self.twitch_auth = TwitchAuth()
self.headers = {
Expand Down Expand Up @@ -617,6 +618,8 @@ def get_oauth_token(self) -> str:
return token


PLATFORMS_FIELDS = ["id", "name"]

GAMES_FIELDS = [
"id",
"name",
Expand Down Expand Up @@ -667,6 +670,8 @@ def get_oauth_token(self) -> str:
"similar_games.cover.url",
]

SEARCH_FIELDS = ["game.id", "name"]

# Generated from the following code on https://www.igdb.com/platforms/:
# Array.from(document.querySelectorAll(".media-body a")).map(a => ({
# slug: a.href.split("/")[4],
Expand Down
63 changes: 42 additions & 21 deletions backend/handler/metadata/sgdb_handler.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,63 @@
from typing import Final

import requests
from config import STEAMGRIDDB_API_KEY
from logger.logger import log

# Used to display the Mobygames API status in the frontend
STEAMGRIDDB_API_ENABLED: Final = bool(STEAMGRIDDB_API_KEY)

# SteamGridDB dimensions
STEAMVERTICAL = "600x900"
GALAXY342 = "342x482"
GALAXY660 = "660x930"
SQUARE512 = "512x512"
SQUARE1024 = "1024x1024"


class SGDBBaseHandler:
def __init__(self) -> None:
self.BASE_URL = "https://www.steamgriddb.com/api/v2"
self.search_endpoint = f"{self.BASE_URL}/search/autocomplete"
self.grid_endpoint = f"{self.BASE_URL}/grids/game"
self.headers = {
"Authorization": f"Bearer {STEAMGRIDDB_API_KEY}",
"Accept": "*/*",
}
self.BASE_URL = "https://www.steamgriddb.com/api/v2"
self.DEFAULT_IMAGE_URL = "https://www.steamgriddb.com/static/img/logo-512.png"

def get_details(self, term):
def get_details(self, search_term):
search_response = requests.get(
f"{self.BASE_URL}/search/autocomplete/{term}",
f"{self.search_endpoint}/{search_term}",
headers=self.headers,
timeout=120,
).json()

if len(search_response["data"]) == 0:
log.info(f"Could not find {term} on SteamGridDB")
return ("", "", self.DEFAULT_IMAGE_URL)

game_id = search_response["data"][0]["id"]
game_name = search_response["data"][0]["name"]

game_response = requests.get(
f"{self.BASE_URL}/grid/game/{game_id}", headers=self.headers, timeout=120
).json()

if len(game_response["data"]) == 0:
log.info(f"Could not find {game_name} image on SteamGridDB")
return (game_id, game_name, self.DEFAULT_IMAGE_URL)

game_image_url = game_response["data"][0]["url"]

return (game_id, game_name, game_image_url)
log.warning(f"Could not find '{search_term}' on SteamGridDB")
return ""

games = []
for game in search_response["data"]:
covers_response = requests.get(
f"{self.grid_endpoint}/{game['id']}",
headers=self.headers,
timeout=120,
params={
"dimensions": f"{STEAMVERTICAL},{GALAXY342},{GALAXY660},{SQUARE512},{SQUARE1024}",
},
).json()

games.append(
{
"name": game["name"],
"resources": [
{"thumb": cover["thumb"], "url": cover["url"]}
for cover in covers_response["data"]
],
}
)

return games


sgdb_handler = SGDBBaseHandler()
1 change: 1 addition & 0 deletions frontend/assets/scrappers/sgdb.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/__generated__/index.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/src/__generated__/models/HeartbeatResponse.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions frontend/src/__generated__/models/SearchCoverSchema.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 23 additions & 18 deletions frontend/src/components/common/Game/AdminMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,46 @@ const heartbeat = storeHeartbeat();
</script>

<template>
<v-list
rounded="0"
class="pa-0"
>
<v-list rounded="0" class="pa-0">
<v-list-item
:disabled="!heartbeat.value.ANY_SOURCE_ENABLED"
class="py-4 pr-5"
@click="emitter?.emit('showMatchRomDialog', rom)"
>
<v-list-item-title class="d-flex">
<v-icon
icon="mdi-search-web"
class="mr-2"
/>Manual
search
<v-icon icon="mdi-search-web" class="mr-2" />Manual match
</v-list-item-title>
<v-list-item-subtitle>
{{
!heartbeat.value.ANY_SOURCE_ENABLED ? "No metadata source enabled" : ""
!heartbeat.value.ANY_SOURCE_ENABLED
? "No metadata source enabled"
: ""
}}
</v-list-item-subtitle>
</v-list-item>

<v-list-item
:disabled="!heartbeat.value.STEAMGRIDDB_ENABLED"
class="py-4 pr-5"
@click="emitter?.emit('showSearchCoverDialog', rom)"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-image-outline" class="mr-2" />Cover search
</v-list-item-title>
<v-list-item-subtitle>
{{
!heartbeat.value.STEAMGRIDDB_ENABLED ? "No SteamgridDB enabled" : ""
}}
</v-list-item-subtitle>
</v-list-item>

<v-divider />
<v-list-item
class="py-4 pr-5"
@click="emitter?.emit('showEditRomDialog', { ...rom })"
>
<v-list-item-title class="d-flex">
<v-icon
icon="mdi-pencil-box"
class="mr-2"
/>Edit
<v-icon icon="mdi-pencil-box" class="mr-2" />Edit
</v-list-item-title>
</v-list-item>
<v-divider />
Expand All @@ -51,10 +59,7 @@ const heartbeat = storeHeartbeat();
@click="emitter?.emit('showDeleteRomDialog', [rom])"
>
<v-list-item-title class="d-flex">
<v-icon
icon="mdi-delete"
class="mr-2"
/>Delete
<v-icon icon="mdi-delete" class="mr-2" />Delete
</v-list-item-title>
</v-list-item>
</v-list>
Expand Down
Loading

0 comments on commit 180c373

Please sign in to comment.