Changelog
Version:
v1.2.20 (d6bdf00)
Last updated:
2026-04-04
Latest commit:
Add tooltip tap support, harden redirect mappings, extensive code review (#56)
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[1.2.20] - 2026-04-03
Frontend
Fixed
- Weather tooltip hover/click parity - Kept the disabled
Race dayweather hint visible when desktop users click after hovering while preserving tap-to-toggle dismissal for touch users, so the floating tooltip no longer disappears until the pointer leaves and re-enters the trigger
Backend
Fixed
- SQLite backup snapshots - Replaced byte-for-byte SQLite file copies with SQLite's online backup API for both scheduled and detailed S3 backups so WAL-enabled databases upload crash-safe, internally consistent snapshots
- Translation and season fallbacks - Stopped recursive i18n fallback loops when the default locale file is missing and made season helpers fall back to the current year beyond the last hardcoded race-start date while still preferring the newest bundled teams dataset
- Config test cache reset - Refreshed the module-level
configsingleton whenever tests clear the config cache so patched environment variables propagate consistently across imports - Static-analysis hygiene cleanup - Reworked the config test-reset helper, analytics/standings/version logging calls, database cursor context-manager nesting, schema-init state naming, stats cleanup query whitelisting, and ASGI middleware constructor names so the branch stays clean under DeepSource Python review without changing runtime behavior
- Background task supervision - Replaced fire-and-forget
create_task()call sites for startup generation and web-vitals analytics with supervised background tasks that keep references, log failures, and report exceptions to Sentry instead of dropping them silently - Configurable stats retention - Extended the scheduled stats cleanup path to cover
api_callsandperf_metricsalongside legacyrequest_stats, but now gate it behindSTATS_RETENTION_DAYSso deployments keep statistics indefinitely by default and can opt into bounded cleanup only when explicitly configured - Hourly stats history source-of-truth - Switched
/api/stats/historyto derive hourly and rolling-24h counts directly from persistedapi_callsso historical stats work against existing production data even when the legacyrequest_statssnapshot table is empty, and rolling windows now stay correct across sparse hourly gaps - Cross-instance DB initialization safety - Serialized schema initialization and migrations per database path instead of per
Database()instance so concurrent request-scoped database handles cannot race each other into duplicateALTER TABLEmigrations on older SQLite files - ASGI middleware migration - Replaced
BaseHTTPMiddlewarewrappers for static cache headers and canonical-host/HSTS handling with pure ASGI middleware so streaming responses keep middleware compatibility without sacrificing the existing redirect and cache behavior - Shared outbound HTTP clients - Reused long-lived
httpx.AsyncClientinstances for Jolpica, Open-Meteo, GitHub release checks, and Umami requests, then closed them during app shutdown so outbound API calls stop paying per-request client setup costs without leaking connections across tests or process exit - Shared HTTP retry helper - Consolidated the duplicated Jolpica 429 backoff logic into
app.utils.http.fetch_with_retry()so F1, standings, and teams services share one tested retry path instead of maintaining three drift-prone copies - Config-driven standings endpoints - Switched standings fetches to derive their Jolpica base URL from
config.JOLPICA_API_URLand use UTC-aware default-year resolution so mirrored endpoints and year-rollover behavior stay consistent with the rest of the services layer - BMP cache headroom - Increased the in-memory BMP cache capacity to 512 entries so localized display and weather combinations fit without immediate LRU eviction churn during normal traffic
- Non-blocking BMP analytics - Moved calendar BMP analytics dispatch in the image routes onto supervised background tasks so download responses no longer wait on Umami network round-trips
- Configure redirect validation - Validate configure screen types before localized redirect canonicalization so invalid
/configure/...language-query permutations return a 404 instead of bubbling aKeyErrorinto a 500 - Query-safe redirect merging - Hardened
_redirect_path()to merge existing canonical query strings with preserved request parameters instead of producing malformed double-?redirects if future targets include their own query string - Method-preserving canonical host redirects - Switched the
wwwto apex redirect middleware to HTTP 308 so canonical redirects keep POST request bodies intact for endpoints like/api/perf-metrics - Static error cache headers - Limited static-asset cache headers to successful
/static/...responses so missing files keep their defaultno-storesemantics instead of inheriting public asset caching - Startup task shutdown ordering - Cancel pending initial image generation before closing shared HTTP clients and database handles so shutdown cannot race a still-running startup task against torn-down resources
- Scheduler shutdown drain clarity - Made scheduler shutdown explicitly wait for in-flight jobs after cancelling the startup generation task so shared HTTP/database resources are only closed once background work has drained
- Umami warning deduplication - Stopped re-raising already-logged non-200 Umami responses so failed analytics calls emit one warning instead of noisy duplicate log lines
- Next-race retry parity - Moved
F1Service.get_next_race()onto the shared Jolpica retry helper so the primary upcoming-race fetch now retries 429s the same way as the rest of the live API client - Local env example visibility - Stopped ignoring
.env.local.exampleso the checked-in local development template remains visible to contributors while.env.localstays untracked - Consistent dev extras - Aligned
[project.optional-dependencies].devwith[dependency-groups].devsopip install -e "[dev]"anduv sync --devinstall the same linting, typing, test, and data-science helper toolchain - Consistent byte formatting - Matched the shared frontend
formatBytes()helper to the backend decimal-unit formatter so cache/stat sizes render the same KB/MB/GB values across server and browser UI - Shared circuit metadata maps - Moved the duplicated
CIRCUIT_ID_MAPandCOUNTRY_MAPconstants into one shared module so renderer and service updates stay in sync across display modes and API helpers - Endpoint rate limiting - Added lightweight per-IP in-memory throttling for BMP generation and
POST /api/perf-metricsso expensive image renders and analytics ingestion have a basic abuse guard by default - Zero-valued stats aggregates - Preserved legitimate
0.0values in page, trend, aggregate, and 24-hour API stats payloads instead of collapsing them toNone, so perfect vitals samples and zero-latency operational windows remain visible in dashboards - Safer BMP purge timing - Deferred deletion of pregenerated BMP assets until after static next-race data is available, so a transiently missing race snapshot no longer wipes the last known-good BMP set before generation can begin
- Persistent SQLite connections - Reworked
Databaseto reuse one loop-aware SQLite connection per instance instead of reopening and reconfiguring a fresh connection on every operation, and now close live database handles during app shutdown - Configurable GitHub API base URL - Moved GitHub release/version requests onto
config.GITHUB_API_BASE_URLso those external API calls follow the same environment-driven configuration path as the rest of the services - Renderer asset handle cleanup - Wrapped disk-backed
Image.open()calls for logos, tracks, flags, driver portraits, and team logos in context-managed loads so PIL image conversions keep returning detached images without leaking file handles during repeated preview and render jobs - Schedule event field naming - Renamed the internal
ScheduleEventtimestamp attribute toevent_datetimewhile keepingdatetimeas a compatibility alias so model code no longer shadows the importeddatetimetype - Standings metadata extraction - Moved season-facing driver-number and team-id lookup tables out of
api.pyinto a shared utility module so route handlers no longer own that update-prone mapping data - BWR renderer helper deduplication - Extracted shared track-image and results-header helpers for the BWR and BWRY renderers so multi-color calendar variants stop carrying near-identical fallback and flag-loading logic in parallel
- Shared renderer text helpers - Moved common team-layout and text-measurement helpers out of the 1-bit and Spectra 6 renderers so both primary renderer classes now reuse one source of truth for column splits, truncation, alignment, and team header formatting
- Shared schedule label helpers - Centralized localized session-name normalization and sprint-qualifying label formatting so the monochrome and Spectra 6 renderers no longer maintain duplicate schedule translation logic, and the Spectra 6 results footer now uses the canonical
session_qualifying/session_racetranslation keys - Sprint-qualifying accent normalization - Normalize
SprintQualifying/SprintShootout/Shootoutaliases before Spectra 6 accent-color lookup so sprint-qualifying schedule rows keep their dedicated highlight color instead of silently falling back to black - Shared logo/result text helpers - Moved team-logo keying, logo crop utilities, and historical-result text fitting into shared renderer helpers so both primary renderers stop carrying duplicate asset-prep logic
- Shared results header helper - Centralized the year/flag footer header renderer so monochrome and Spectra 6 variants share one flag-loading path, including ISO fallback handling for countries like
UK,USA, andUAE - Shared new-track message helper - Moved the centered
NEW TRACKfooter message into a shared renderer helper so monochrome and Spectra 6 variants stop duplicating the same fallback layout math - Shared renderer font fallbacks - Centralized Symbola, weather-icon, and Racing Sans One fallback loading so the primary renderers reuse one source of truth for icon and number font fallback behavior
- Shared driver portrait helper - Moved surname normalization, racing-number rendering, and portrait resize/paste logic into one shared helper so the monochrome and Spectra 6 team cards stop carrying duplicate driver-photo rendering code
- Shared schedule-section helper - Centralized schedule title rendering, row iteration, and countdown handoff so the monochrome and Spectra 6 calendar renderers reuse one schedule-section layout flow
- Shared race-header helper - Moved the logo/title split-header layout into one shared helper so monochrome and Spectra 6 race views now share the same title-block geometry and text positioning
- Shared circuit-stats helper - Centralized circuit fact composition and right-column stat-block layout so monochrome and Spectra 6 renderers stop duplicating the same lap/length/history rendering logic
- Shared track-section helper - Centralized circuit label layout and track-image placement so monochrome and Spectra 6 renderers now share the same left-column track-section geometry while keeping display-specific image preparation
- Shared team-card layout helper - Centralized the common team-row card geometry, meta-text placement, and logo container math so monochrome and Spectra 6 renderers now share the same card skeleton while keeping display-specific stats and driver-row rendering
- Shared countdown-box helper - Centralized race-status/countdown rendering and optional weather overlay layout so monochrome and Spectra 6 renderers now share one countdown/status box flow with only display colors passed in
- Shared team stats/driver-row helpers - Centralized the repeated team-card stats panel and driver-row rendering flow so monochrome and Spectra 6 renderers now only pass badge colors and text fills instead of maintaining duplicate row logic
- Shared track asset loader - Centralized source/fallback track asset resolution so monochrome and Spectra 6 renderers now share one loader path with only variant suffixes and fallback directories passed in
- Shared results-section helper - Centralized footer separator/new-track branching and qualifying/race column dispatch so monochrome and Spectra 6 renderers now share one historical-results section flow with only display colors and callbacks passed in
- Shared multi-color results header helper - Generalized the shared footer year/flag renderer to accept preferred flag-directory fallback chains and route footer rendering through overridable renderer hooks so B/W/R and B/W/R/Y variants reuse the shared header flow without bypassing their display-specific flag assets
- Shared schedule-row helper - Centralized schedule date/day/time parsing and row text layout so monochrome and Spectra 6 renderers now differ only in the session-name fill color passed into one shared row renderer
- Shared track-stem helper - Centralized circuit-id/location normalization for track asset candidate lookup so monochrome and Spectra 6 renderers now share the same race-data-to-track-stem resolution path before applying display-specific asset fallbacks
- Removed team-logo wrapper methods - Inlined the last display-specific team-logo callback wrappers into the shared team-card flow so monochrome and Spectra 6 renderers no longer carry separate
_draw_team_logo()pass-through methods - Removed results-header wrapper methods - Inlined the last monochrome and Spectra 6 results-header pass-through wrappers into the shared footer-section callback wiring so those renderers now call the shared year/flag helper directly
- Removed layout utility wrapper methods - Swapped remaining team-layout callsites and tests over to shared
split_teams_for_columns(),get_text_y(), andright_align_x()helpers so the primary renderers no longer carry duplicate pass-through utilities for those calculations - Removed shared formatter wrapper methods - Rewired remaining team-card/result callsites and tests to use shared
build_team_header_values(),format_team_driver_display_name(),format_points(),clamp_text(), andfit_result_text()helpers directly so the primary renderers no longer keep utility pass-through methods for those common formatting paths - Removed shared logo helper wrapper methods - Swapped remaining team-logo keying and crop callsites/tests over to shared
get_team_logo_key(),crop_to_content(), andcrop_primary_horizontal_band()helpers so the primary renderers no longer keep private pass-through utilities for logo preparation - Removed shared schedule i18n wrapper methods - Rewired remaining schedule/session callsites and tests to use shared
format_schedule_session_name(),build_sprint_qualifying_label(), andtranslate_session_name()helpers directly so the primary renderers no longer keep pass-through i18n wrappers for session labels - Shared F1 logo and track placeholder helpers - Moved the last identical header-logo and missing-track placeholder drawing logic into
renderer_commonso monochrome and Spectra 6 renderers now share those visual fallbacks instead of keeping duplicate local helpers
Security
Fixed
- Optional operational API token hardening - Followed up the original token-gated operational endpoints with release-ready validation, docs, and changelog coverage so deployments can lock down
/api/stats,/api/stats/history, andGET /api/perf-metricswithout breaking browser-sidePOST /api/perf-metricsweb-vitals ingestion - Masked S3 credentials - Stored S3 access keys as
SecretStrvalues and resolved them only at S3 client creation time so accidental config logging and tracebacks no longer expose raw backup credentials - SITE_URL validation - Added the shared URL validator to
SITE_URLso malformed canonical host configuration falls back safely instead of leaking invalid redirect/canonical origins
Development
Changed
- Stable Python baseline - Relaxed the project runtime/tooling baseline from pre-release Python
3.14.3to stable Python3.13across packaging metadata, Docker images, CI workflows, and local setup docs - Compose quickstart refresh - Switched the quickstart helper to the modern
docker composeCLI and removed the obsolete top-levelversionfield fromdocker-compose.yml - CI dependency caching and coverage - Enabled pip dependency caching in the main CI workflow, installed the unified dev extras set directly, and turned on pytest coverage reporting so pull requests surface both test failures and app coverage regressions in one run
- Example env sync - Updated
.env.exampleand.env.local.exampleto include the new GitHub API base URL, rate-limiting, operational API token, and statistics-retention settings so deployment templates match the current config surface - Native async pytest style - Converted the async task and analytics test modules away from
asyncio.run()wrappers to nativepytest.mark.asynciotests so they follow the configured strict asyncio test mode directly - Weather test async migration - Converted the weather service test suite from nested
asyncio.run()wrappers to native async pytest cases so async cache and forecast coverage now runs directly under the configured strict event-loop mode - Remaining async test migration - Converted the shared HTTP client/retry, F1 season, standings, and database async test modules away from
asyncio.run()wrappers so the remaining service-layer async coverage now runs directly under pytest-managed event loops - Stdlib timezone support - Replaced
pytzwith a sharedzoneinfohelper for config validation, request parameter checks, and race schedule conversion so timezone handling now uses the Python standard library across the app
[1.2.19] - 2026-03-31
Frontend
Changed
- Calendar configure tooltip refresh - Reworked the weather availability hint into a floating tooltip that follows the pointer, stays clear of the desktop sidebar, and remains accessible across mouse, keyboard, and touch interactions by using
aria-disabledstate instead of relying on native disabled-button hover behavior - Credits wording and layout sync - Aligned homepage and desktop credits labels with the mobile drawer wording, expanded provider attribution rows, and standardized entries such as
Jolpica-F1 API,Weather-icons, analytics, hosting, and device sources
Backend
Changed
- Public redirect hardening - Switched the remaining public-page language redirects on home, configure, privacy, changelog, API docs, and stats routes to the validated
_redirect_path()helper so canonical redirects keep only approved on-site targets and preserve the intended query handling
Security
Fixed
- Optional operational API token introduction - Added the initial opt-in token checks for read-only operational metrics endpoints so deployments can lock down
/api/stats,/api/stats/history, andGET /api/perf-metricswithout breaking browser-sidePOST /api/perf-metricsweb-vitals ingestion - Dependency vulnerability updates - Bumped
pygmentsinuv.lockandpicomatchinpackage-lock.jsonto patched versions to clear the open GitHub security alerts on the release branch
Development
Changed
- Local artifact ignore rules - Added
.playwright-cli/to.gitignoreso Playwright CLI snapshots and console captures do not get committed with future UI review iterations
[1.2.18] - 2026-03-30
Frontend
Added
- Themed HTML 404 page - Added a branded browser-facing 404 page with
noindexmetadata, localized navigation shortcuts, and direct rendering for unknown public HTML routes instead of JSON fallback responses
Changed
- Canonical public-page normalization - Public HTML routes now use explicit canonical redirects for language roots and trailing-slash variants so crawler-facing URLs stay stable across home, configure, stats, changelog, privacy, and API docs pages
- Favicon asset serving - Replaced the inline SVG emoji favicon with the packaged ICO asset so browser and audit tooling both see the same icon resource referenced by the site manifest and HTML head
- 404 and credits localization polish - Localized the new 404 page copy across all supported languages, regrouped homepage credits into a stable responsive layout, and aligned site/README acknowledgements for weather data, weather icons, analytics, monitoring, flag assets, and inspiration sources
- Homepage and mobile-nav polish - Refined the homepage credits presentation, removed the desktop header credits dropdown, moved mobile credits into the drawer footer, and tightened mobile header spacing so the logo, menu button, and language switcher stay balanced across breakpoints
- Language selector labels - Shortened the visible
PT-BRandZH-CNlanguage labels to compactPTandZHdisplay text while keeping the underlying locale routing unchanged - Header and drawer navigation cleanup - Restored the smaller desktop logo/header balance, simplified the language switcher label treatment, and tuned the mobile drawer so navigation remains at the top while credits stay anchored in a compact footer block
- Credits naming and layout sync - Renamed credits entries to the current public wording (
Inspiration,Jolpica-F1 API,Weather-icons,Devices) and aligned mobile drawer credits into consistent label/value rows with inline device and hosting pairs - README cleanup - Removed decorative emoji section headings and refreshed credits/documentation copy so the public project page matches the current app behavior and deployment requirements
Backend
Changed
- Proxy-aware canonical redirects - Disabled FastAPI's implicit slash redirects, added explicit 301 route normalizers, and taught the app/runtime to respect forwarded proxy headers so HTTPS canonical redirects no longer downgrade to
httpbehind Coolify/Traefik - Canonical host enforcement - Added
wwwto apex redirect handling in middleware, hardened host parsing for non-defaultSITE_URLports, and now emit HSTS on both normal HTTPS responses and thewwwredirect itself - Trusted proxy defaults - Switched the container startup command to a configurable
FORWARDED_ALLOW_IPSallowlist with private-network defaults, keeping proxy header trust adjustable per deployment instead of unconditional* - Startup config centralization - Moved
FORWARDED_ALLOW_IPSandSKIP_PERSISTENCE_CHECKintoapp/config.pyso runtime defaults and environment loading stay in one configuration path
Fixed
- Crawler and SEO endpoint coverage - Added regression tests for
robots.txt,sitemap.xml, canonical redirects, HTML 404 rendering, HSTS behavior, favicon responses, and HTTPS-only redirect assertions so the Search Console remediation work stays locked to the configured canonical host
Documentation
Changed
- Deployment env guidance - Added missing required environment variables to the example env files and clarified that
SITE_URLcontrols canonical URLs, Open Graph tags,robots.txt,sitemap.xml, andwwwcanonicalization whileANALYTICS_HOSTNAMEonly affects analytics - Coolify apex redirect recipe - Documented the production-ready Traefik and Caddy label setup for single-hop
http://www -> https://apexcanonical redirects on self-hosted Coolify deployments
[1.2.17] - 2026-03-29
Frontend
Added
- Top-10 locale expansion - Added localized site/UI coverage for
de,es,fr,it,nl,pl,pt-BR,tr,ja,zh-CN, andsk, including locale metadata, alphabetized language selectors, and shared frontend locale handling
Changed
- Localized configure/docs source-of-truth - Moved configure-page UI copy into the shared translation files, injected canonical locale codes into shared frontend helpers, aligned language switching/redirect behavior with the current route and query string, and updated localized HTML API docs to expose the full supported language set
- README and public docs refresh - Updated README/examples/self-hosting guidance to match the current localized configure flow, teams endpoints, startup warmup behavior, generated localized preview assets, and API docs examples that now use the active request host instead of placeholder domains
- Track artwork refresh - Added new Miami, Albert Park, and Shanghai track source/variant assets for all supported display modes, including PSD source files for future edits
Backend
Changed
- Localized asset generation and startup warmup - Expanded scheduled/startup generation to cover all configured locales for calendar and teams assets, kept timezone-specific calendar generation for popular non-default TZs, moved version refresh to hourly
:05, and warmed teams render assets on startup to cut cold-start render latency - Pregenerated localized image serving -
/calendar.bmpand/teams.bmpnow reuse pregenerated localized assets across all supported locales instead of special-casing onlyen/cs
Fixed
- Localized page consistency - Fixed localized configure/API docs routes, corrected
og:localemetadata, aligned shared locale lists across backend/frontend, and localized configure race/session copy so non-en/cspages no longer fall back to stale English strings - Renderer and preview reliability - Added locale-aware CJK font coverage to image rendering, fit long translated schedule labels, shortened sprint qualifying schedule labels per locale, kept CJK teams driver names clear of the team-card header, restored teams previews/static loading paths, disabled invalid FastAPI preview response-model inference, and reused scheduler preview data across languages to avoid redundant fetches
- Teams rendering polish - Limited the CJK font override on teams screens to the translated header subtitle, kept the lower team cards on the brand font, and normalized Red Bull / Racing Bulls power-unit labels down to
RB ...variants - Teams configure resiliency - Fixed
Season Leaders/teams configure loading regressions caused by page-specific JS assumptions and made teams previews prefer pregenerated localized PNGs with dynamic fallback - Security and release validation follow-ups - Removed unsafe
innerHTMLusage from the ePaper setup flow, hardened client-side language redirects, fixed DeepSource validator findings, and kept changelog/release validation metadata in sync for release-readiness checks - Image key consistency - Hardened pregenerated image-key helpers to reject unsupported display modes and aligned teams route cache keys with the shared scheduler/pregeneration key format
[1.2.16] - 2026-03-25
Backend
Fixed
- Cancelled season refresh persistence - Static season updates now preserve previously tracked cancelled races such as Bahrain and Jeddah when Jolpica omits them, and season JSON refreshes now end with a trailing newline for cleaner diffs and POSIX-friendly tooling
[1.2.15] - 2026-03-24
Frontend
Added
- Teams display parity - Added
1bit,B/W/R,B/W/R/Y, andSpectra 6variants to the teams configure flow, previews, API docs, and a dedicated teams display breakdown in the stats dashboard - Display-aware session accents - Multi-color calendar outputs now color
Race,Qualifying,Sprint, andPracticelabels according to the active display palette, with unsupported colors falling back to black - SVERIO hardware docs - Added SVERIO
B/W/R/YPaperBoard hardware references to the site credits/README and documented a tested 7.5"GDEM075F52screenshot placeholder for the four-color panel
Backend
Added
- Teams display renderers - Extended
/teams.bmpto support the same display selection flow as/calendar.bmp, including color-aware teams rendering, per-display preview generation, analytics tagging, and scheduler/configure preview generation for all display modes
Fixed
- Teams logo/render polish - Refined logo sourcing and 1-bit/logo conversion behavior across teams renderers, added dedicated 1-bit overrides for problematic team marks, preserved half-points in standings output, and treated legacy teams analytics rows without
display_typeas1bit - Teams header layout - Reworked the team-card header stats area into a shared aligned panel, improved vertical/horizontal centering, right-aligned points across teams, highlighted the leading constructor position in color-capable displays, and improved
B/W/RandB/W/R/Ymeta-text readability - Teams row alignment - Fixed driver-name column alignment by anchoring names to a shared slot, centering driver numbers inside that slot, and keeping spacing consistent across mixed-width number/photo assets
- Sauber logo fallback - Non-
Spectra 6teams renders now remap Sauber's green accent to a mono-friendly white-on-black mark so1bit,B/W/R, andB/W/R/Yvariants stay legible in 2024/2025 layouts - Calendar session color mapping -
Spectra 6now uses red/yellow/green/blue accents forRace/Qualifying/Sprint/Practice, whileB/W/RandB/W/R/Ygracefully fall back to supported palette colors - Benchmark and quality follow-ups - Lazy-loaded teams assets for the 1-bit renderer to avoid unrelated benchmark regressions, restored CodSpeed to the GitHub-hosted runner, resolved DeepSource findings (including helper visibility/static-method cleanup), and split complex team-row renderer methods into smaller helpers
- Documentation sync - Updated README/examples/self-hosting/BMP pipeline docs so public documentation matches the current endpoints, display variants, generated preview naming, and teams rendering behavior
[1.2.14] - 2026-03-19
Frontend
Changed
- Configure cleanup - Split the shared configure screen into calendar/teams partials, removed dead teams timezone controls, renamed the teams side panel to
Season Leaders, and tightened the mobile sidebar layout with compact display mode labels (B/W,B/W/R,B/W/R/Y,6C) - Stats dashboard polish - Unified breakdown color ranking across cards, widened the display breakdown, clarified range/fallback copy, shortened mobile race labels to
GP, and kept percentage bars aligned while preserving a visible white lowest-rank bar
Backend
Changed
- Stats shaping cleanup - Removed unused display color metadata and dropped unused performance dashboard queries from the stats page route while keeping localized range labels in the rendered context
Fixed
- Race stats aggregation - Combined auto-selected and manually selected requests for the same race into a single breakdown row while retaining an
auto_selected_countsignal for display hints
[1.2.13] - 2026-03-17
Frontend
Added
- B/W/R/Y display mode - Added
display=bwryto the calendar configure UI, preview routing, API docs, and stats labeling for the four-color calendar output
Backend
Added
- B/W/R/Y renderer pipeline - Added dedicated
bwrycalendar rendering, scheduler generation, track/flag preprocessing scripts, and calendar API support alongside existing1bit,bwr, andspectra6modes
Fixed
- SEO: lightweight HTML HEAD responses - Public HTML page routes now short-circuit
HEADrequests before analytics or stats queries, preventing 405 responses in Search Console without adding crawler-only pageview noise or extra DB load
[1.2.12] - 2026-03-16
Frontend
Changed
- Track asset variants - Track rendering now prefers display-specific source assets like
_bw,_bwr, and_spectra6, with the plain circuit PNG kept as the generic fallback and_bwrynaming prepared for future display support - Track render quality -
1bit,B/W/R, andSpectra 6track maps now prefer source artwork over preprocessed BMP fallbacks so resized text and thin lines stay sharper in the final composed image - Display palettes -
B/W/Rnow uses a vivid pure red palette andSpectra 6now uses more vivid red/yellow/green/blue output colors to better match the prepared source artwork - BMP pipeline docs - Added a dedicated
BMP_PROCESSING.mdguide that documents the current source naming, preprocessing steps, fallback order, and final BMP encoding flow for all active and prepared display variants
Backend
Removed
main->devsync automation - Removed the post-release sync workflow and retired the dedicateddevbranch maintenance flow
Changed
- Track preprocessing scripts - Added explicit preprocessing flows for
spectra6and preparedbwrytrack assets, and aligned runtime/source resolution around per-display source variants with generic fallback assets
Fixed
- Post-merge quality issues - Added Open-Meteo config validation at startup, cleaned up DeepSource-reported service/test patterns, and documented the safe
0.0.0.0bind used for container deployments - Cancelled race persistence - Season API responses now merge cancelled races back in from static season JSON when Jolpica omits them entirely, so removed Bahrain/Jeddah weekends still appear at the end of the calendar as cancelled entries
[1.2.11] - 2026-03-16
Frontend
Changed
- Cancelled race selection - Configure UI now keeps cancelled weekends at the end of the season list, highlights them in red, and lets previews request them via stable
race_keyidentifiers
Backend
Changed
- F1 data automation - Scheduled
Update F1 Dataruns now refresh both historical circuit results and static season calendar JSON files underapp/assets/seasons/
Fixed
- Race status rendering -
/calendar.bmpkeeps cancelled races addressable without breaking existingyear+roundrequests, skips weather fetches for cancelled weekends, renders a centeredCANCELLED/ZRUŠENOlabel in the countdown slot, and now showsIN PROGRESS/PROBÍHÁfor the first three hours after lights out before switching toCOMPLETED/DOKONČEN
[1.2.10] - 2026-03-13
Frontend
Added
- Stats display breakdown - Added a new statistics card that shows calendar request share by
1bit,B/W/R, andSpectra 6display type
Backend
- Display type analytics - Persisted calendar display mode in API call statistics so the stats dashboard can aggregate usage by display type
[1.2.9] - 2026-03-13
Frontend
Added
- B/W/R display mode - Added
display=bwrto the configure UI and preview routing for black/white/red calendar output
Backend
- B/W/R renderer pipeline - Added dedicated B/W/R rendering, scheduler generation, asset preprocessing scripts, and calendar API support alongside existing
1bitandspectra6modes
[1.2.8] - 2026-03-11
Frontend
Fixed
- Track map assets - Refreshed
albert_parkandshanghaisource track maps to improve circuit clarity in calendar previews
Backend
Changed
- Pre-processed track bitmaps - Regenerated
albert_park.bmpandshanghai.bmpintracks_processedfrom the updated source maps to keep 1-bit rendering output aligned - Spectra 6 track bitmap - Updated
tracks_spectra6/albert_park.bmpto match the refreshed Albert Park circuit source - Release sync automation - Updated release and sync workflows to publish releases with
SYNC_PAT, trigger main-to-dev sync after published releases, and record the syncedmainSHA in the automation PR
[1.2.7] - 2026-03-09
Frontend
Fixed
- Teams & Drivers preview - Corrected 2026 team ordering, standings placement, right-aligned logos, and season selector behavior so the configure preview matches the live championship layout
- Driver number rendering - Switched
teams.bmpto render cleaner font-based driver numbers instead of relying on stale number image assets
Backend
Fixed
- 2026 teams bitmap data - Added curated 2026 team data, corrected driver numbers, and normalized override handling so live and static team renders stay in sync
- Teams bitmap rendering - Team logos now support Audi and Cadillac, constructor standings render in the correct order for 11-team 2026 grids, and team header layout avoids collisions with standings and driver names
- Teams bitmap freshness -
/teams.bmpnow disables long-lived HTTP caching so lineup and standings changes appear immediately after deploys - Teams data matching - Driver standings merging now tolerates ASCII/diacritic name differences and sponsor-prefixed constructor names such as
Perez/Pérez,Hulkenberg/Hülkenberg, andAtlassian Williams/Williams
Added
- 2026 regression coverage - Added renderer and teams-service tests for 2026 team data, manual driver-number overrides, and full BMP rendering in English and Czech
[1.2.6] - 2026-03-01
Backend
Changed
- Python runtime baseline - Raised minimum supported version to
3.14.3and aligned Ruff target topy314 - Docker runtime parity - Updated builder and runtime images to
python:3.14.3-slim - CI runtime parity - Updated workflows to run on Python
3.14.3and moved CI dependency install topip install -e ".[dev]" - Dependency lock refresh - Regenerated
uv.lockforrequires-python = ">=3.14.3"with compatible resolved versions - Docs/runtime alignment - Updated README and deployment docs to reflect the new Python baseline and current API surface
[1.2.5] - 2026-03-01
Backend
Added
- CodSpeed benchmark workflow - Added
.github/workflows/codspeed.ymlto run renderer benchmarks on pull requests and pushes tomain - Renderer performance benchmarks - Added benchmark coverage for 1-bit and Spectra 6 renderers (
calendar,calendar + historical,teams/drivers,standings,error) intests/test_benchmarks.py
Changed
- Benchmarking dependencies - Added
pytest-codspeedto dev dependencies and updated the lockfile for reproducible benchmark runs
[1.2.4] - 2026-02-25
Frontend
Added
- Open-Meteo attribution - Added Open-Meteo to the Credits list in both desktop header dropdown and mobile configure sidebar
Backend
Fixed
- Forecast range boundary - Race-day weather now requests a forecast window that always includes the race date (fixes missing data near the 14-day boundary)
- Nearest-hour fallback - When exact race start hour is unavailable, fallback now picks the closest hour on race day instead of the first hour of that day
Added
- Weather regression tests - Added coverage for forecast-days window calculation and nearest-hour fallback selection
- Release automation - Added CI workflow that creates GitHub releases from the latest
CHANGELOG.mdentry after pushes tomain - Main→dev sync automation - Added CI workflow that opens/updates an auto-merge PR to sync
mainback intodevafter each push tomain
[1.2.3] - 2026-02-25
Frontend
Fixed
- Race day weather controls - Configure page now enables/disables race-day weather based on the selected race date (14-day forecast window)
- Desktop/mobile sync - Race selection now re-checks race-day weather availability on both desktop and mobile controls
- Disabled button states - Race-day buttons now use consistent disabled styling with
aria-disabledfor better UX and accessibility
Backend
Changed
- Weather context helper - Added
get_weather_context()to centralize current and race-day forecast loading - Race-day forecast rendering -
/calendar.bmpand preview generation now use dedicated race-day forecast data instead of reusing current weather weather_typealias support - Added handling forracealias in weather rendering logic for both 1-bit and Spectra 6 countdown labels- Version metadata -
last_updatednow reflects the latest main-branch commit timestamp
Fixed
- Privacy i18n test stability - Updated cookie handling in test client to make language preference checks deterministic
[1.2.2] - 2026-01-12
Frontend
Changed
- Language URLs - Switched from query parameters (
?lang=cs) to subdirectory URLs (/cs/) for better SEO - Automatic redirects - Old
?lang=URLs now redirect (301) to new subdirectory format - Language switcher - Updated to navigate between subdirectory URLs
Backend
Added
- Subdirectory language routing - All HTML pages now support
/{lang}/prefix (e.g.,/cs/privacy,/cs/configure/calendar) - Language URL helpers - New
lang_url()function for generating language-aware URLs in templates
Changed
- Sitemap - Now includes all language variants with subdirectory URLs
- Hreflang tags - Updated to reference subdirectory URLs instead of query parameters
- BMP endpoints unchanged - API endpoints (
/calendar.bmp,/teams.bmp) still use?lang=parameter
Fixed
- Router ordering - Reordered routers so
/preview/*routes are matched before/{lang}/*patterns - Service Worker - Removed non-existent
favicon.svgfrom cache list (was causing SW install failure) - Configure page preload - Removed unused image preload that didn't match dynamically loaded variants
[1.2.1] - 2026-01-12
Backend
Fixed
- Spectra 6 PNG previews - Configure page previews now display colors instead of grayscale
- SEO: Sitemap HEAD support -
/sitemap.xmland/robots.txtnow respond to HEAD requests (fixes Google Search Console) - SEO: Canonical URLs - Sitemap now uses clean URLs without
?lang=parameter
[1.2.0] - 2026-01-11
API
Added
displayparameter for/calendar.bmp- set tospectra6for 6-color E-Ink displays (default:1bit)
Frontend
Added
- Display type selector - Choose between 1-BIT (monochrome) and 6-COLOR (Spectra 6) display modes
- Mobile display controls - Full display type settings available on mobile sidebar
- Pre-rendered preview variants - Configure page now uses pre-rendered PNG for all weather/display combinations
Fixed
- SEO - Invalid or empty
?lang=parameter now redirects to canonical URL (301) - Language preference - Stored language preference now applied correctly on page load
- Weather buttons - Fixed visibility in desktop mode
- Preview weather sync - Weather OFF/current/race buttons now show correct preview image
Backend
Added
- Spectra 6 Renderer - New 840-line renderer for 7.3" 6-color E-Ink displays (800×480)
- 6-color palette - BLACK, WHITE, RED (#A02020), YELLOW (#F0E050), GREEN (#608050), BLUE (#5080B8)
- Pre-processed track images - 25 circuit maps optimized for Spectra 6 (494×271, no dithering)
- Pre-processed flag images - 26 country flags for Spectra 6 (87×58)
- Dedicated F1 logo - Color-optimized logo for Spectra 6 displays
- Weather pre-fetch - Scheduler fetches weather at :55 before hourly image generation
- DeepSource integration - Automated code quality analysis
- PNG preview variants - Scheduler generates preview PNG for all weather/display combinations
Changed
- Scheduler - Pre-generates all variants: 2 displays × 3 weather modes × 2 languages
- Session colors - Simplified for Spectra 6: only "Race" is RED, all others BLACK
- Red accent lines - F1 logo underline and results separator now use RED in Spectra 6
- Code quality - Added @staticmethod decorators to pure utility methods
- Short countdown labels - Uses "d"/"h" instead of "days"/"hours" for current/race_day weather types
- Unified weather cache - Image generation now uses same in-memory cache as dynamic rendering
Fixed
- CIRCUIT_ID_MAP - Added Vegas → Las Vegas mapping for track images
- Country code logic - Removed redundant fallback (already in COUNTRY_MAP)
- Weather config - Added missing weather configuration attributes
- Track centering - Spectra 6 renderer now centers track images horizontally and vertically
- Pre-generated image selection - Correctly selects BMP file based on weather_type parameter
[1.1.4] - 2026-01-04
API
Added
weatherparameter for/calendar.bmp- set tofalseto disable weather displayweather_typeparameter -currentfor current weather,race_dayfor race day forecast (default)
Frontend
Added
- Weather display in countdown box - Shows weather icon, temperature, and precipitation chance
- Weather Icons font - Professional weather icons from erikflowers/weather-icons (SIL OFL 1.1)
- Weather type selector - Choose between current weather or race day forecast
- Mobile weather controls - Full weather settings available on mobile sidebar
Changed
- Countdown box redesign - Now shows
🏁 5D 3Hformat with weather on the right - Circuit stats simplified - Only shows track length, fastest lap, and First GP (weather moved to countdown)
- Configure page - Weather section with neobrutalism-styled toggle buttons (OFF/CURRENT/RACE DAY)
Backend
Added
WeatherServiceclass with Open-Meteo API integrationWeatherDatadataclass with icon, temperature, and precipitation properties- Weather icon font loader (
_load_weather_icon_font) - Race day weather forecast (up to 14 days ahead)
- Current weather fallback when race day forecast unavailable
- Weather caching (60 minutes default)
Changed
render_calendar()now acceptsweather_dataparameter_draw_countdown_box()renders weather alongside countdown_draw_circuit_stats()simplified - removed weather row- Weather params included in BMP cache key
[1.1.3] - 2026-01-02
Backend
Fixed
- Font MIME Type - TTF fonts served as
text/plaininstead offont/ttf- registered correct MIME types
[1.1.2] - 2026-01-02
Backend
Fixed
- Font Loading - SpaceMono TTF files were corrupted (HTML instead of binary) - re-downloaded from Google Fonts
- SEO - Fixed hreflang x-default to include
?lang=enfor consistency with canonical URLs - Performance - Removed oversized favicon.svg (240KB embedded PNG) - using PNG favicon instead
[1.1.1] - 2026-01-02
Frontend
Fixed
- LCP Optimization - First preview image now has
fetchpriority="high"and no lazy loading - CLS Prevention - Added explicit
width="800" height="480"to preview images - Logo Optimization - Removed oversized 2x srcset (19KB → 10KB saved), added
fetchpriority="high" - Render-blocking - Deferred
common.jsscript loading
Accessibility
- WCAG AA Contrast - Fixed low contrast text in credits section (
text-gray-400→text-gray-600) - WCAG AA Contrast - FoxeeLab link now uses
font-boldwith hover state instead oftext-racing-red - Screen Readers - Added
aria-labelto mobile menu buttons with fallback (nav.get('nav_menu', 'Menu')) - Screen Readers - Added
aria-hidden="true"to decorative SVG icons - Screen Readers - Added
sr-onlylabel to language switcher select
SEO
- Canonical URLs - Now correctly use
request.url.pathwith language parameter - Hreflang Tags - Fixed to match canonical URL structure per page
[1.1.0] - 2026-01-01
API
Added
/teams.bmpendpoint for teams & drivers grid/api/teams/{year}endpoint for team data (JSON)/api/standings/leaderendpoint for championship leaders (JSON)/api/perfendpoint collects Web Vitals (LCP, FCP, CLS, TTFB, INP) from browsers
Frontend
Added
- New landing page with screen type selection (Calendar/Teams)
- Teams & Drivers screen - Full team grid with driver photos and points
- Teams panel sidebar on homepage showing championship leaders
- Configure page (
/configure/{screen}) for interactive preview of each screen type - Percentile Stats - p50/p75/p95 percentiles for Core Web Vitals with color-coded thresholds
- Service Worker - Offline caching for static assets (
/static/*) with cache-first strategy - Critical CSS Inlining - Above-the-fold CSS inlined in
<head>for faster first paint
Changed
- Redesigned homepage with screen type selector
- Improved teams screen layout with full driver names and enlarged photos
- Driver numbers now display in Racing Sans font when photo is missing
- Team logos centered with fixed container sizing
- Drivers sorted by points within each team
- Stats page Web Vitals section now displays percentile table instead of simple averages
- Stylesheet loading now uses preload/onload pattern with noscript fallback
Fixed
- Mobile navigation - Added missing nav links (Stats, API, Privacy, Changelog) to configure page sidebar
- Driver number font size increased to 22px for better proportion
- Left-aligned driver positions with enlarged P1-P3 badges
- Preview position now maintains consistent spacing
- Removed duplicate logo from homepage
- Fixed Zivyobraz.eu capitalization
Backend
Added
- Team logos integration in teams service
- GZip Compression - Response compression middleware for responses >=500 bytes
get_perf_trends()method in database service for hourly metric aggregation- Percentile calculation (
_calculate_percentile()) for performance statistics
Changed
get_perf_stats()now returns p50/p75/p95 percentiles for each metric
Fixed
- Teams "Current" season logic - Now correctly returns 2025 data until March 8, 2026 (season start) instead of using calendar year
[1.0.2] - 2025-12-30
Backend
Security
- Fix 7 CodeQL path injection vulnerabilities (high severity) in
i18n.py,f1_service.py,main.py
[1.0.1] - 2025-12-28
Backend
Security
- Add timezone, language, and year validation to prevent path injection attacks
- Add
permissions: contents: readto CI workflow
Added
- Startup check for persistent storage - Creates
.persistence_markerfile, warns if storage is non-persistent
[1.0.0] - 2025-12-26
Initial public release.
API
Added
/calendar.bmpendpoint for E-Ink calendar imagelangparameter for language selection (cs/en)tzparameter for timezone conversionyearandroundparameters for specific race selection
Frontend
Added
- Neobrutalist UI redesign with Space Mono font, neo-brutalist shadows, and black borders
- Persistent header with navigation links (GitHub, API, Privacy, Credits dropdown)
- Mobile-responsive layout with hamburger menu for small screens
- Timezone selector with continent filters and search functionality
- Auto-detection of user's timezone in browser
- Credits dropdown in header with links to all third-party services and inspiration
- Privacy Policy page (
/privacy) with multi-language support (EN/CS) - Interactive API documentation (
/api/docs/html) with styled UI and code examples - 800x480 1-bit BMP calendar image for E-Ink displays
- Multi-language support (Czech and English)
- Historical race results display (previous year's podium)
- Circuit statistics display (length, laps, first GP)
- Track map rendering
Changed
- Footer removed - all links moved to persistent header navigation
- UI language detection from
Accept-Languageheader with?lang=override - Improved mobile layout with collapsible sidebar instead of inline controls
Fixed
- Header title now clickable link to homepage on all pages
- Hamburger menu now properly closes with overlay click and close button
- Missing timezone label element added for timezone display
Backend
Added
- FastAPI backend with async support
- F1 race data fetching from Jolpica API
- Timezone conversion (UTC to configurable timezone)
- Umami analytics integration
- Sentry/GlitchTip error monitoring
- Docker and Docker Compose support
- Coolify deployment support
- SQLite-based API call logging with data transfer statistics
- Complete favicon set: SVG, ICO (48x48), Apple Touch Icon (180x180), PWA manifest icons
- New translations:
laps,first_gp,circuit_not_available,contact_github, privacy policy texts
Changed
- F1Service now accepts timezone parameter
- Database paths now use absolute paths (
/app/data/) for Docker container compatibility
Fixed
- Database persistence with absolute paths for Docker deployments
- HTML structure with proper
<main>element nesting toggleSidebar()function added to main page JavaScript- Umami analytics task reference retention for proper async tracking
- Circuit ID mapping:
vegasnow correctly maps tolas_vegasfor circuit stats - Moved debug scripts to
scripts/directory - Cleaned up root directory (removed test files, old Dockerfile)