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
+
-[](https://github.com/LizardByte/GameDB/actions/workflows/update-db.yml?query=branch%3Amaster)
-[](https://app.codecov.io/gh/LizardByte/GameDB)
+
-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.
+
+
+## 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