From 6a72ff7269e4d92176eb6f98653953a2e50656df Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 22 Feb 2026 08:47:09 -0500 Subject: [PATCH] docs(readme): add item count badges --- README.md | 58 ++++++++++++++++++++++++++++++++---- src/update_db.py | 16 ++++++++++ tests/unit/test_update_db.py | 19 ++++++++++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9d448992ac3a..ed8f995089e6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,56 @@ -# GameDB +
+

GameDB

+

Jekyll static site with IGDB data

+
-[![GitHub Workflow Status (DB)](https://img.shields.io/github/actions/workflow/status/lizardbyte/gamedb/update-db.yml.svg?branch=master&label=update%20db&logo=github&style=for-the-badge)](https://github.com/LizardByte/GameDB/actions/workflows/update-db.yml?query=branch%3Amaster) -[![Codecov](https://img.shields.io/codecov/c/gh/LizardByte/GameDB.svg?token=AG91ICECDX&style=for-the-badge&logo=codecov&label=codecov)](https://app.codecov.io/gh/LizardByte/GameDB) +
+ CI + Update DB + Codecov +
-This repository clones IGDB to gh-pages to be consumed by LizardByte projects, such as LizardByte/Sunshine. +--- -Information from YouTube API is also added to the database for videos. +
+ Characters + Collections + Franchises + Games + Platforms + Videos +
+ +## Overview + +GameDB is a database of games, platforms, characters, collections, franchises, and videos sourced from +[IGDB](https://www.igdb.com) and published as a static JSON API via +[GitHub Pages](https://lizardbyte.github.io/GameDB). Video metadata is enriched with data from the YouTube API. + +The data is intended to be consumed by [LizardByte](https://app.lizardbyte.dev) projects such as +[Sunshine](https://github.com/LizardByte/Sunshine). + +## Data + +The database is updated automatically on a schedule via the [update-db](.github/workflows/update-db.yml) workflow. +The generated JSON files are published to the `gh-pages` branch and served at +`https://app.lizardbyte.dev/GameDB/`. + +| Endpoint | Description | URL | +|-------------|-------------------------------------------------------|-----------------------------------------------------------| +| Buckets | Game name search index, split by first two characters | `https://app.lizardbyte.dev/GameDB/buckets/.json` | +| Characters | Individual character details and all characters | `https://app.lizardbyte.dev/GameDB/characters/.json` | +| Collections | Individual collection details and all collections | `https://app.lizardbyte.dev/GameDB/collections/.json` | +| Franchises | Individual franchise details and all franchises | `https://app.lizardbyte.dev/GameDB/franchises/.json` | +| Games | Individual game details (no aggregate `all.json`) | `https://app.lizardbyte.dev/GameDB/games/.json` | +| Platforms | Individual platform details and all platforms | `https://app.lizardbyte.dev/GameDB/platforms/.json` | +| Videos | Individual YouTube video metadata | `https://app.lizardbyte.dev/GameDB/videos/.json` | +| Stats | Total item counts per category | `https://app.lizardbyte.dev/GameDB/stats.json` | + +`all.json` files (e.g. `characters/all.json`) contain a summary of every item in that category as a single +dictionary keyed by ID. + +Buckets are used as a lightweight search index for game names. Each bucket file is named after the first two +alphanumeric characters of the game name (lowercased), e.g. `ha.json` for games starting with "Ha" such as +*Halo*. Games whose names contain a space as the second character are put into a bucket named after the first +character. Games whose names do not start with two alphanumeric characters are grouped into `@.json`. Each bucket +contains a dictionary of `{ id: { name } }` entries, keeping individual files small for fast lookups. diff --git a/src/update_db.py b/src/update_db.py index bdf0917dd936..31dd7fc147e3 100644 --- a/src/update_db.py +++ b/src/update_db.py @@ -451,6 +451,20 @@ def _enrich_game_videos(full_dict: dict) -> None: video['thumb'] = video_thumbs[0][1]['url'] +def _write_stats(full_dict: dict) -> None: + """ + Write a ``stats.json`` file to the output directory containing item counts per category. + + Parameters + ---------- + full_dict : dict + The combined data dictionary keyed by endpoint name. + """ + stats = {endpoint: len(items) for endpoint, items in full_dict.items()} + file_path = os.path.join(args.out_dir, 'stats') + write_json_files(file_path=file_path, data=stats) + + def get_data(): """ Get data from IGDB and YouTube. @@ -614,6 +628,8 @@ def get_data(): _enrich_game_videos(full_dict=full_dict) + _write_stats(full_dict=full_dict) + for endpoint, endpoint_dict in full_dict.items(): print(f'writing individual files for {endpoint}') for item_id, data in endpoint_dict.items(): diff --git a/tests/unit/test_update_db.py b/tests/unit/test_update_db.py index d4845d3cf7ab..84e247778a3a 100644 --- a/tests/unit/test_update_db.py +++ b/tests/unit/test_update_db.py @@ -445,6 +445,23 @@ def test_get_youtube(tmp_path): assert 'yt_key_123' in called_url +def test_write_stats(tmp_path): + udb.args = _make_args(tmp_path) + full_dict = { + 'characters': {1: {}, 2: {}, 3: {}}, + 'games': {10: {}, 20: {}}, + 'videos': {'abc': {}, 'def': {}}, + } + + with patch('src.update_db.write_json_files') as mock_write: + udb._write_stats(full_dict=full_dict) + + mock_write.assert_called_once() + call_kwargs = mock_write.call_args[1] + assert 'stats' in call_kwargs['file_path'] + assert call_kwargs['data'] == {'characters': 3, 'games': 2, 'videos': 2} + + def test_get_platform_cross_reference(tmp_path): udb.args = _make_args(tmp_path) @@ -474,6 +491,7 @@ def test_get_data_orchestration(tmp_path): patch('src.update_db._resolve_video_groups', return_value=[['vid1']]) as m_vgroups, \ patch('src.update_db._fetch_youtube_metadata') as m_yt, \ patch('src.update_db._enrich_game_videos') as m_enrich, \ + patch('src.update_db._write_stats') as m_stats, \ patch('src.update_db.write_json_files') as m_write: udb.get_data() @@ -484,6 +502,7 @@ def test_get_data_orchestration(tmp_path): m_vgroups.assert_called_once() m_yt.assert_called_once() m_enrich.assert_called_once() + m_stats.assert_called_once() assert m_write.call_count >= 2 # bucket files + individual files