No description
  • Svelte 53.2%
  • Python 42.6%
  • TypeScript 2.4%
  • CSS 0.9%
  • Shell 0.6%
  • Other 0.2%
Find a file
Wolfhound 2f7ffb3a16
All checks were successful
CI / lint (push) Successful in 33s
CI / test (push) Successful in 3m32s
CI / frontend (push) Successful in 2m29s
Add customizable dashboard sections with toggle modal
Users can now hide/show individual dashboard sections via a settings
modal (sliders icon next to tab list). Preferences persist to
localStorage. Tabs auto-hide when all their sections are toggled off.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 02:00:32 -04:00
.forgejo/workflows Initial public release 2026-04-07 20:31:34 -04:00
data Initial public release 2026-04-07 20:31:34 -04:00
docs Initial public release 2026-04-07 20:31:34 -04:00
frontend Add customizable dashboard sections with toggle modal 2026-04-11 02:00:32 -04:00
packaging Initial public release 2026-04-07 20:31:34 -04:00
scripts Initial public release 2026-04-07 20:31:34 -04:00
src/yafst Add first_seen/last_seen dates to opponent stats 2026-04-10 11:07:45 -04:00
tests Track Gemini API input/output token usage 2026-04-10 04:25:17 -04:00
.gitignore Initial public release 2026-04-07 20:31:34 -04:00
.pre-commit-config.yaml Initial public release 2026-04-07 20:31:34 -04:00
CLAUDE.md Update CLAUDE.md files and README to match current codebase state 2026-04-07 20:43:27 -04:00
IMPROVEMENT_PLAN.md Phase 6.3: Enhance tier collection — badge images, time-in-tier, visited/locked states 2026-04-10 07:55:30 -04:00
IMPROVEMENT_PROGRESS.md Log visual improvement loop progress (Gemini-assisted, 3 passes) 2026-04-10 13:13:11 -04:00
LICENSE Initial public release 2026-04-07 20:31:34 -04:00
Makefile Initial public release 2026-04-07 20:31:34 -04:00
PROGRESS.md Initial public release 2026-04-07 20:31:34 -04:00
pyproject.toml Initial public release 2026-04-07 20:31:34 -04:00
README.md Add Gemini API cost section to README 2026-04-10 04:55:12 -04:00

yafst -- Yet Another Finals Stats Tracker

A Linux-first, open-source ranked stats tracker for THE FINALS. The game has no public API, so yafst captures your screen in real time, detects which game screen is showing, and stores frames for data extraction. Everything is stored in a local SQLite database and viewable through a built-in web dashboard.

Status: Scene detection, capture, and data extraction are working. The pipeline captures and classifies frames, extracts stats via Gemini vision API, and stores results automatically.

Screenshots

Dashboard

Rank score, peak RS, win rate, RS goal tracking, recent form, and rotating tips. Tabbed insight sections: At a Glance, Where You Shine, Your Grind, Your Arsenal, Familiar Faces.

Dashboard

Dashboard Insights

Match History

Filterable match list with search, placement badges, seed-to-place tracking, and RS progression. Filter by map, class, weapon, placement range, date, and session. Export to CSV or JSON.

Matches

Match Detail

Placement badge, RS change, bracket reward curve, per-round scoreboards with player stats, loadout display, and match notes.

Match Detail

Sessions

Session list with duration, match count, win record, RS change, VS previous session comparison, RS/hr rate, tilt grade, and sparkline charts. Session RS trend bar chart at the bottom.

Sessions

Session Detail

Per-session stat cards, VS career average comparison with trend indicators, RS progression chart, and match breakdown table.

Session Detail

Features

Capture and Detection

  • Live screen capture via PipeWire ScreenCast (Wayland-native, same as OBS), KDE KWin ScreenShot2, x11grab, or grim -- auto-detects the best backend
  • Scene detection using anchor-based pixel spot-checks and color analysis across 6 game screens (bracket, loadout, in-game leaderboard, post-round results, post-game tournament, post-game team performance)
  • Resolution profiles -- supports bundled and custom profiles at any resolution via the calibrate page
  • Rank tier lookup -- converts RS values to tier names (Bronze through Ruby) based on Season 6 thresholds
  • Game data constants -- weapons, gadgets, specializations, maps, and rank tiers bundled in the codebase

Storage and Management

  • SQLite storage with sessions, matches, rounds, per-player stats, and loadouts
  • Database backup/restore -- yafst backup, yafst restore, yafst backups
  • TOML config file at ~/.config/yafst/config.toml for persistent settings
  • Manual match creation and editing -- add matches by hand, edit stats
  • Match notes -- freeform text notes on any match

Web Dashboard

  • Dark THE FINALS-themed UI built on SvelteKit SPA + FastAPI API backend
  • RS history charts with color-coded placements and rank tier annotations
  • Match filtering and search by map, placement range, date range, and text
  • Pagination across all list views
  • Session detail pages with per-session RS charts and match tables
  • Personal bests and win streaks -- best KD, most eliminations, longest streak
  • Rolling performance stats -- sliding-window win rate, placement, and KD
  • Loadout performance analytics -- per-weapon and per-class win rates and stats
  • CSV/JSON export of full match history with round-level detail
  • Keyboard shortcuts for common navigation actions
  • Print-friendly CSS for match detail and session pages
  • Accessibility -- ARIA labels, skip-to-content links, focus-visible styles

API

  • JSON REST API for stats, matches, sessions, RS history, loadout stats, personal bests, and map stats
  • API documentation via Swagger UI (/docs) and ReDoc (/redoc)

Calibration Tools

  • Calibrate page (/calibrate) for setting up detection anchors -- required before first use
  • Frame extraction from Steam DASH recordings
  • Batch scene classification for testing detection accuracy

Quick Start

1. Install system dependencies

All system dependencies are optional but recommended for specific features.

# Arch
sudo pacman -S ffmpeg xdotool gstreamer

# Debian/Ubuntu
sudo apt install ffmpeg xdotool gstreamer1.0-tools

# Fedora
sudo dnf install ffmpeg xdotool gstreamer1-plugins-base-tools

Verify with yafst check after install (see step 3).

2. Install yafst

git clone https://forge.wolfhound.dev/wolfhound/yafst.git
cd yafst
uv venv
uv pip install -e ".[capture,dev]"

The capture extra installs PipeWire/D-Bus dependencies (dbus-fast, PyGObject). Omit it if you only want to run tests or the web UI.

Or use the Makefile for a full dev setup:

make dev-setup    # installs everything + runs setup wizard

3. First-run setup

The first time you run yafst run, a default config is written silently and the web server starts. Your browser is then redirected to a setup wizard at /setup where you can enter your player name, choose a capture backend, and configure your Gemini API key.

yafst run
# Open http://127.0.0.1:8745 — redirects to /setup on first run

You can re-open the setup wizard at any time from the Settings page, or force it from the CLI with yafst --setup. To generate a default config without starting the server:

yafst --init-config

4. Verify your setup

yafst check
System dependencies:
  ✓ ffmpeg 6.1
  ✗ xdotool (not found -- optional, needed for X11 window detection)
  ✓ gst-inspect-1.0 1.24.3

5. Calibrate

Before first use, open the calibrate page to set up screen detection anchors for your resolution:

yafst web
# Open http://127.0.0.1:8745/calibrate

Upload or capture screenshots of each game screen and configure the detection anchors. Without calibration, yafst cannot detect game screens.

6. Run

Start the tracker while THE FINALS is running:

yafst run

The web dashboard starts automatically at http://127.0.0.1:8745. Use --no-web to run capture only, or --port 9000 to change the port.

To view past results without capturing:

yafst web

Seed fake data (for testing the UI)

python scripts/seed_data.py

CLI Tools

Command Description
yafst run Main tracker -- captures screen, detects scenes, stores frames, and serves the dashboard.
yafst web Starts only the web dashboard (no screen capture).
yafst check Check system dependencies (ffmpeg, etc.) and print a summary.
yafst backup Back up the database to a timestamped file (or -o PATH for a custom destination).
yafst restore <file> Restore the database from a backup file (prompts for confirmation).
yafst backups List available database backups with dates and sizes.
yafst calibrate Run calibration to capture reference templates.
yafst reselect Clear saved screen capture selection and re-pick the source.
yafst --setup Force the first-run setup wizard (re-configure).
yafst --init-config Generate a default config.toml at ~/.config/yafst/.
yafst-calibrate classify <image> Classify a screenshot into a scene type.
yafst-calibrate gemini <image> Test Gemini extraction on a screenshot (--model, --scene, --no-crop).
yafst-calibrate extract <dash_dir> <timestamp> Pull a frame from a Steam DASH recording.
yafst-calibrate batch <dash_dir> Extract frames at intervals and classify each one.
yafst-replay <dash_dir> Replay a DASH recording through the full pipeline.

Architecture

src/yafst/
  main.py              CLI entry point (run / web / backup / restore / check)
  config.py            TOML config, XDG paths, capture/web settings
  setup.py             First-run setup wizard (interactive config)
  depcheck.py          System dependency checker (ffmpeg, xdotool, gstreamer)
  game_data.py         Known weapons/gadgets/specs, rank tiers, maps
  util.py              Shared utilities (ISO duration parser, D-Bus checks)
  capture/             Screen capture backends (pipewire, kwin, x11grab, grim)
  detection/
    scene.py           Scene enum (6 primary + legacy states) + SceneClassifier
    anchors.py         Anchor definitions for scene detection
    fields.py          Field definitions for extraction regions
    regions.py         Per-scene region definitions (relative coordinates)
    profiles.py        Resolution profiles (save/load JSON, auto-detect)
  pipeline/
    processor.py       Async capture loop (poll -> classify -> store)
    state.py           Game state machine (idle -> monitoring -> capturing)
    extraction.py      Frame extraction via Gemini vision API
    debug_state.py     Pipeline <-> web bridge for live debug view
  models/
    schemas.py         Data models (Session, Match, Round, PlayerStats, Loadout)
    database.py        SQLite operations with schema migrations
  web/
    app.py             FastAPI application factory (Swagger UI at /docs, ReDoc at /redoc)
    routes.py          Dashboard, matches, sessions, calibrate, debug, API endpoints
    frontend/          SvelteKit SPA (prerendered, served by FastAPI)
    static/            Static assets (favicon, examples)
  calibrate.py         Calibration CLI
  replay.py            DASH recording replay
Makefile               Dev setup, lint, test, and build targets
scripts/
  seed_data.py         Generate realistic fake match data
tests/                 316 tests (pytest)
docs/
  CONTRIBUTING_FRAMES.md  Guide for adding new screens and features

Data Storage

All data lives under ~/.local/share/yafst/ (respects XDG_DATA_HOME):

  • yafst.db -- SQLite database (sessions, matches, rounds, stats, loadouts)
  • screenshots/ -- raw captures saved during tracking

Config at ~/.config/yafst/:

  • config.toml -- persistent settings
  • profiles/ -- custom resolution calibration profiles (includes detection anchors)

What's Tracked

Scene detection identifies these screens automatically. Data extraction uses the Gemini vision API to read stats from captured frames.

Data Source Screen Detection Extraction
Final placement (1st-8th) POST_GAME_TOURNAMENT Working Gemini
Rank Score (RS) after match POST_GAME_TOURNAMENT Working Gemini
Per-player stats (8 fields) POST_GAME_TEAM_PERF Working Gemini
Self-player identification POST_GAME_TEAM_PERF Working Gemini
Loadout (class, weapon, spec, gadgets) LOADOUT Working Gemini
Map name BRACKET / LOADOUT Working Gemini
Skill range BRACKET Working Gemini
Round number LOADOUT Working Gemini
Rank tier (Bronze through Ruby) Computed from RS N/A Working

Gemini API Cost

yafst uses Google's Gemini vision API to extract match data from captured screenshots. Each match generates multiple API calls depending on the number of rounds played and leaderboard snapshots captured.

Per-match breakdown

A typical 3-round match triggers 11-13 API calls: bracket, loadout (per round), post-round results, post-game tournament, post-game team performance, and leaderboard snapshots (first + last per round).

Call type Calls per match Input tokens Output tokens
Bracket 1 ~1,360 ~110
Loadout 1-3 ~1,710 ~130
Post-round results 1 ~1,330 ~270
Post-game tournament 1 ~1,270 ~250
Post-game team perf 1 ~1,330 ~340
Leaderboard snapshot 2-6 ~1,500 ~1,490

Monthly cost estimate

Based on 10 games per weekday and 20 on weekends (~380 matches/month), assuming every match goes to 3 rounds with full leaderboard capture (worst case):

Model Monthly cost Notes
gemini-2.5-flash-lite ~$2.30 Cheapest, but unreliable on complex scenes
gemini-3.1-flash-lite ~$7.72 Best accuracy-to-cost ratio (default)
gemini-2.5-flash ~$12.00 Higher accuracy, frequent 503s under load

Most players will land well under these numbers -- early exits, fewer leaderboard captures, and shorter sessions all reduce calls. A casual player doing 5-10 matches a day would see roughly $2-4/month on the default model.

Free tier

Google's free tier allows 1,000 requests per day on lite models. Even a heavy 20-match session produces ~260 API calls, so the free tier covers normal play. You'd need 75+ matches in a single day to exceed it.

Data region cropping

The calibrate page (/calibrate) lets you define crop regions per scene. Instead of sending the full screenshot, yafst composites just the relevant areas into a smaller image. On lite models this doesn't reduce token cost (images are flat-rate), but it reduces visual noise and improves extraction accuracy.

Token usage is tracked per call and visible on the Settings page.

Roadmap

  • AI vision model extraction -- Gemini vision API extracts match data from captured frames; accuracy improvements and edge-case handling are ongoing
  • Scoreboard (TAB) extraction -- region definitions needed, scene detection already works (needs screenshots)
  • Built-in resolution profiles for common resolutions (1080p, 1440p, 4K, ultrawide) -- needs screenshots from other resolutions

Contributing

See docs/CONTRIBUTING_FRAMES.md for how to help with new screen calibration, feature expansion, and resolution support.

License

MIT