Skip to content

Commit c5ed17e

Browse files
authored
Merge pull request #88 from adn8naiagent/staging
Add AUTO_PRECOMPUTE env var for session fetching control
2 parents bde6acb + 09f2e27 commit c5ed17e

5 files changed

Lines changed: 84 additions & 11 deletions

File tree

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ R2_ACCESS_KEY_ID=
1414
R2_SECRET_ACCESS_KEY=
1515
R2_BUCKET_NAME=f1timingdata
1616

17+
# Auto-precompute: which session types to fetch in the background during race weekends.
18+
# Runs every 30 minutes Fri–Mon and downloads any matching session as soon as F1
19+
# publishes data. Sessions not in this set are still fetched on-demand the first
20+
# time you click them (with the usual 1–3 minute wait).
21+
# off — disable, fetch everything on-demand only
22+
# race — race + sprint
23+
# race+qual — race, sprint, qualifying, sprint qualifying (default)
24+
# all — every session including practice
25+
AUTO_PRECOMPUTE=race+qual
26+
1727
# Optional — authentication
1828
# AUTH_ENABLED=true
1929
# AUTH_PASSPHRASE=your-passphrase

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to F1 Replay Timing will be documented in this file.
44

5+
## 2.0.1
6+
7+
### Improvements
8+
- **Broadcast delay up to 5 minutes** — Live Timing Offset slider now extends to -5m (previously -60s) to accommodate longer streaming-service lag, with M:SS formatting and ±30s quick-adjust buttons
9+
- **Configurable auto-precompute** — new `AUTO_PRECOMPUTE` env var controls which session types the background task fetches during race weekends. Accepts `off`, `race`, `race+qual` (default), or `all`. Self-hosters who don't watch practice can avoid downloading FP1/FP2/FP3 data; sessions outside the configured set are still available on-demand
10+
11+
---
12+
513
## 2.0.0
614

715
### Migrating from v1.x

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,16 @@ docker compose exec f1timing python precompute.py 2024 2025 --skip-existing
205205
- A full race weekend (FP1, FP2, FP3, Qualifying, Race) takes **3-5 minutes**
206206
- A complete season (~24 rounds, all sessions) takes **2-3 hours**
207207

208-
The app also includes a background task that automatically checks for and processes new session data on race weekends (Friday-Monday).
208+
**Background auto-precompute:** On race weekends (Fri–Mon), a background task checks every 30 minutes for new session data and processes it ahead of time so the first click is instant. Which session types it fetches is controlled by the `AUTO_PRECOMPUTE` env var:
209+
210+
| Value | What it fetches |
211+
|---|---|
212+
| `off` | Nothing — everything is fetched on-demand the first time you click it |
213+
| `race` | Race + sprint |
214+
| `race+qual` | Race, sprint, qualifying, sprint qualifying — **default** |
215+
| `all` | Every session including practice |
216+
217+
Sessions outside the configured set are still available — they're just processed on demand (with the usual 1–3 minute first-load wait) rather than pre-fetched.
209218

210219
### Manual setup (without Docker)
211220

backend/main.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from auth import is_auth_enabled, verify_token
1313
from routers import sessions, track, laps, results, replay, telemetry, sync, live, live_status
1414
from routers import auth_routes
15-
from services.auto_precompute import auto_precompute_loop
15+
from services.auto_precompute import auto_precompute_loop, get_allowed_session_types
1616

1717
load_dotenv()
1818

@@ -22,16 +22,20 @@
2222

2323
@asynccontextmanager
2424
async def lifespan(app: FastAPI):
25-
# Start background auto-precompute task
26-
task = asyncio.create_task(auto_precompute_loop())
27-
logger.info("Auto-precompute background task scheduled")
25+
allowed = get_allowed_session_types()
26+
task: asyncio.Task | None = None
27+
if allowed:
28+
task = asyncio.create_task(auto_precompute_loop())
29+
logger.info(f"Auto-precompute scheduled for session types: {sorted(allowed)}")
30+
else:
31+
logger.info("Auto-precompute disabled (AUTO_PRECOMPUTE=off)")
2832
yield
29-
# Cancel on shutdown
30-
task.cancel()
31-
try:
32-
await task
33-
except asyncio.CancelledError:
34-
pass
33+
if task is not None:
34+
task.cancel()
35+
try:
36+
await task
37+
except asyncio.CancelledError:
38+
pass
3539

3640

3741
app = FastAPI(

backend/services/auto_precompute.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
Runs every 30 minutes on Friday–Monday (race weekend days).
44
Uses FastF1's schedule to find sessions that should have data available,
55
checks if we've already processed them, and runs precompute if not.
6+
7+
Which session types are auto-fetched is controlled by the AUTO_PRECOMPUTE
8+
env var. See get_allowed_session_types() for accepted values.
69
"""
710

811
import asyncio
912
import logging
13+
import os
1014
import traceback
1115
from datetime import datetime, timedelta, timezone
1216

@@ -21,6 +25,37 @@
2125
# How long after a session's scheduled start before we try to fetch data
2226
DATA_AVAILABILITY_DELAY = timedelta(hours=0)
2327

28+
# AUTO_PRECOMPUTE presets: env value -> set of session type codes to fetch.
29+
# Codes match SESSION_NAME_TO_TYPE in services/f1_data.py.
30+
_AUTO_PRECOMPUTE_PRESETS: dict[str, set[str]] = {
31+
"off": set(),
32+
"race": {"R", "S"},
33+
"race+qual": {"R", "S", "Q", "SQ"},
34+
"all": {"R", "S", "Q", "SQ", "FP1", "FP2", "FP3"},
35+
}
36+
_AUTO_PRECOMPUTE_DEFAULT = "race+qual"
37+
38+
39+
def get_allowed_session_types() -> set[str]:
40+
"""Return the set of session type codes that should be auto-precomputed.
41+
42+
Driven by the AUTO_PRECOMPUTE env var. Accepted values:
43+
- off : disable auto-precompute entirely
44+
- race : race + sprint
45+
- race+qual : race, sprint, qualifying, sprint qualifying (default)
46+
- all : every session including practice
47+
48+
Unknown values fall back to the default with a warning.
49+
"""
50+
value = os.environ.get("AUTO_PRECOMPUTE", _AUTO_PRECOMPUTE_DEFAULT).strip().lower()
51+
if value not in _AUTO_PRECOMPUTE_PRESETS:
52+
logger.warning(
53+
f"Unknown AUTO_PRECOMPUTE value '{value}', falling back to '{_AUTO_PRECOMPUTE_DEFAULT}'. "
54+
f"Valid values: {sorted(_AUTO_PRECOMPUTE_PRESETS)}"
55+
)
56+
value = _AUTO_PRECOMPUTE_DEFAULT
57+
return set(_AUTO_PRECOMPUTE_PRESETS[value])
58+
2459

2560
async def _check_and_process():
2661
"""Check for new sessions and process any that have data available."""
@@ -32,6 +67,10 @@ async def _check_and_process():
3267
now = datetime.now(timezone.utc)
3368
year = now.year
3469

70+
allowed_types = get_allowed_session_types()
71+
if not allowed_types:
72+
return
73+
3574
try:
3675
events = _fetch_schedule_sync(year)
3776
except Exception as e:
@@ -53,6 +92,9 @@ async def _check_and_process():
5392
if not session_type:
5493
continue
5594

95+
if session_type not in allowed_types:
96+
continue
97+
5698
# Skip if session hasn't had enough time for data to be available
5799
if now < session_ts + DATA_AVAILABILITY_DELAY:
58100
continue

0 commit comments

Comments
 (0)