Authorized Metacritic game discovery, enrichment, and monitoring service.
Overview • Architecture • Getting Started • Configuration • API • Testing
Metacritic Games is a Python service that discovers games from Metacritic, stores canonical Game data in PostgreSQL, and exposes the catalog through a FastAPI backend with a React frontend.
The project is built as a modular monolith with three long-running processes:
webserves the API and frontend.schedulerenqueues Discovery Runs on a fixed interval.workerclaims queued work, processes new Games, and retries failed fetches.
Core Game data is stored independently from Enrichments. That means a Game can be saved successfully even when review summaries, description translation, or similarity embeddings are temporarily unavailable.
Important
This project is configured for authorized crawling only. The default user agent and settings explicitly assume that you have permission to fetch Metacritic content.
- Discover new Game pages from Metacritic browse pages.
- Fetch canonical Game details, developer names, platform ratings, cover art, and source video links.
- Fetch user scores from the Metacritic backend API per platform.
- Fall back to Playwright when plain HTTP responses do not contain enough page content.
- Store durable Discovery Runs, job state, and retry state in PostgreSQL.
- Generate critic and user Review Summaries through an optional chat-model adapter.
- Translate Game descriptions to Russian through an optional translation adapter.
- Compute Similar Games with optional embeddings stored in
pgvector. - Serve a searchable catalog, Game detail pages, and a live processing monitor backed by SSE.
Metacritic browse pages / Game pages / review APIs
|
v
httpx + Beautiful Soup + Playwright fallback
|
v
GameApplication
discovery + retries + enrichments
|
+-----------+-----------+
| |
v v
PostgreSQL + pgvector FastAPI + React
Games, jobs, retries, catalog, detail,
runs, summaries, monitor, SSE
translations, embeddings
The main boundaries are:
metacritic_games/metacritic_core.py: browsing and core page parsing.metacritic_games/metacritic.py: user scores, review fetching, cover extraction, and richer page handling.metacritic_games/application.py: Discovery Run orchestration, retry handling, enrichment, and catalog queries.metacritic_games/api/: HTTP API and frontend hosting.frontend/: React application for catalog, Game detail, and monitor pages.metacritic_games/database/: SQLAlchemy models for catalog, processing, jobs, and embeddings.
The service uses a Processing Day ledger and a durable retry queue.
- The
schedulerenqueues one discovery job per interval. - The
workerclaims the next queued discovery job. - The application discovers up to 20 Game candidates from the next browse page for the current Processing Day.
- Each selected Game is reserved once for that day before fetch work starts.
- Core Game data is saved atomically.
- Failed Game fetches are moved to the retry queue with exponential backoff.
- Review summaries, description translation, and embeddings run independently after core data is stored.
This follows the ADRs in docs/adr/:
- Core Game data is separate from Enrichments.
- Daily selection is separate from processing retries.
- PostgreSQL is the single durable store for both catalog data and background work state.
- Python
3.11+ - Node.js
22+ - PostgreSQL
16+with thevectorextension if you want similarity embeddings - Chromium for Playwright
- Install Python dependencies and the local CLI.
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
pip install -e .
python -m playwright install chromium- Install frontend dependencies.
npm install- Create your local environment file.
cp .env.example .env- Build the frontend bundle served by FastAPI.
npm run build- Set
MCG_DATABASE_URLin.envto match your local database.
For example, for a local PostgreSQL instance:
MCG_DATABASE_URL=postgresql+psycopg://metacritic:metacritic@127.0.0.1:5432/metacritic- Initialize the database schema.
metacritic-games init-db- Start the services in separate terminals.
metacritic-games webmetacritic-games schedulermetacritic-games workerThe web UI is available at http://127.0.0.1:8000.
Note
init-db and normal app startup both create the SQLAlchemy schema automatically. On PostgreSQL, the application
also enables CREATE EXTENSION IF NOT EXISTS vector before building the schema.
If you want live frontend development instead of serving the built bundle, run Vite separately and keep the backend on
port 8000:
npm run devThe repository includes a single-node Compose setup with postgres, web, scheduler, and worker.
Before starting Compose, make sure .env contains the Compose database password:
MCG_DATABASE_URL=postgresql+psycopg://metacritic:12345@postgres:5432/metacriticdocker compose up --buildThe Compose stack exposes the app on http://127.0.0.1:8000 and mounts ./logs into /data/logs.
Settings are loaded from .env with the MCG_ prefix.
| Variable | Default | Purpose |
|---|---|---|
MCG_DATABASE_URL |
postgresql+psycopg://metacritic:metacritic@postgres:5432/metacritic |
SQLAlchemy connection string |
MCG_TIMEZONE |
Asia/Jerusalem |
Service timezone for the Processing Day ledger |
MCG_LOG_LEVEL |
INFO |
Root log level |
MCG_LOG_TO_FILES |
false |
Enable rotating per-service log files |
MCG_LOG_DIR |
/data/logs |
Directory for file logs |
MCG_LOG_FILE_MAX_BYTES |
10485760 |
Max size of one log file before rotation |
MCG_LOG_FILE_BACKUP_COUNT |
5 |
Number of rotated log files to keep |
MCG_SCHEDULE_INTERVAL_SECONDS |
3600 |
How often the scheduler enqueues a Discovery Run |
MCG_WORKER_POLL_SECONDS |
5 |
Worker idle polling interval |
MCG_HEARTBEAT_SECONDS |
10 |
Heartbeat interval for running discovery jobs |
MCG_STALE_JOB_SECONDS |
300 |
When running jobs or retries are considered stale |
MCG_REQUEST_DELAY_SECONDS |
1 |
Minimum delay between Metacritic requests |
MCG_REQUEST_TIMEOUT_SECONDS |
30 |
HTTP and Playwright request timeout |
MCG_FETCH_WORKERS |
3 |
Parallel fetch workers for selected Games |
MCG_LLM_WORKERS |
1 |
Thread-pool workers for LLM enrichments |
MCG_METACRITIC_USER_AGENT |
MetacriticGamesAgent/1.0 (authorized crawler; contact=owner) |
User agent sent to Metacritic |
MCG_METACRITIC_PERMISSION_REFERENCE |
owner-authorized |
Human-readable authorization reference |
MCG_LLAMA_CHAT_URL |
unset | Base URL for summary and translation requests |
MCG_LLAMA_CHAT_MODEL |
local-chat-model |
Model ID stored with summaries and translations |
MCG_LLAMA_REQUEST_TIMEOUT_SECONDS |
600 |
Timeout for chat and embedding LLM requests |
MCG_LLAMA_EMBEDDING_URL |
unset | Base URL for embedding requests |
MCG_LLAMA_EMBEDDING_MODEL |
local-embedding-model |
Model ID stored with embeddings |
| Setting | Enables |
|---|---|
MCG_LLAMA_CHAT_URL |
Critic and user Review Summaries plus Russian description translation |
MCG_LLAMA_EMBEDDING_URL |
Similar Game ranking |
Tip
Without LLM settings, the service still discovers Games, stores core catalog data, exposes the UI, and processes retries. The enrichment features simply stay empty or unavailable.
The README is in English, but the current Game detail experience is partly Russian by design:
- translated descriptions are returned in Russian when the translator is configured;
- summary section labels and action text on the Game page are currently Russian;
- the catalog and monitor pages are currently English.
| Route | Purpose |
|---|---|
/ |
Catalog page with search, platform filter, sorting, and pagination |
/games/{slug} |
Game detail page with ratings, summaries, similar Games, refresh actions, and source links |
/monitor |
Processing monitor with live queue and recent run state |
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness check |
GET |
/api/games |
Catalog query with search, platform, sort, direction, and page |
GET |
/api/games/{slug} |
Game detail, summaries, and similar Games |
POST |
/api/games/{slug}/refresh |
Re-fetch and persist one Game |
POST |
/api/games/{slug}/summaries/regenerate |
Rebuild review summaries for one Game |
POST |
/api/runs |
Enqueue a manual Discovery Run |
GET |
/api/status |
Poll current queue and run status |
GET |
/api/events |
Server-sent status stream |
.venv/bin/python -m pytestThe Python test suite covers:
- Metacritic parsing and request behavior
- retry handling and job heartbeats
- scheduler behavior
- settings and logging
- summary, translation, and similarity flows
- API and frontend-hosting behavior
Integration tests require a live PostgreSQL database exposed through TEST_DATABASE_URL.
.venv/bin/python -m pytest -m integrationThe Playwright suite builds the frontend and boots tests.e2e_server:app on http://127.0.0.1:8765.
npm run test:e2e.
├── frontend/ # React source
├── metacritic_games/
│ ├── api/ # FastAPI routes and service layer
│ ├── database/ # SQLAlchemy models and DB core
│ ├── llm/ # Summary, translation, and embedding adapters
│ ├── frontend_dist/ # Built frontend bundle served by FastAPI
│ ├── application.py # Discovery, retries, enrichments, catalog queries
│ ├── browser.py # Playwright HTML fallback
│ ├── cli.py # `web`, `scheduler`, `worker`, `init-db`
│ ├── config.py # Environment-backed settings
│ ├── factory.py # Dependency wiring
│ ├── metacritic.py # Metacritic integration
│ └── scheduler.py # Interval-based job enqueueing
├── tests/ # Unit, integration, and e2e tests
├── docs/adr/ # Architectural decisions
├── docker-compose.yml # Local multi-service stack
└── Dockerfile # Production-style image build
- The backend package ships the built frontend from
metacritic_games/frontend_dist. npm run buildmust run again after frontend changes if you want FastAPI to serve the updated bundle.npm run devserves the frontend fromfrontend/and proxies/apiand/statictohttp://127.0.0.1:8000.- The worker reclaims stale running jobs and stale retries automatically.
- Similarity scoring combines cosine similarity with small developer and platform overlap bonuses.