Skip to content

About

Generate YouTube-ready orienteering videos from DJI footage, Garmin FIT telemetry, and Livelox maps

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

Repository files navigation

oriVideoGen

oriVideoGen turns orienteering action-cam footage into YouTube-ready videos with synchronized Garmin telemetry and a moving Livelox-style map inset — without recording your screen for the entire race.

Why

Typical manual workflow:

  1. Film with a DJI Osmo Action (or similar)
  2. Export a Garmin .fit activity
  3. Open Livelox and screen-record the replay in real time (a 60-minute race ≈ 60 minutes of recording)
  4. Place HR, speed, elevation, compass, and map overlays by hand in a video editor

oriVideoGen automates steps 3–4:

oriVideoGen \
  --video run.mp4 \
  --fit activity.fit \
  --livelox "https://www.livelox.com/Viewer/...?classId=123456" \
  --output youtube_ready.mp4

Features

  • Garmin FIT telemetry overlays — elevation, gradient, distance, heart rate, speed gauge, heading/compass
  • Livelox map inset (required) — circular runner-centered map with course, controls, and purple route trail
  • Two map modes
    • render (default/fast): download map + georef + route once, draw each frame programmatically
    • capture (fallback): headless Chromium seeks the Livelox timeline and screenshots the map (~10–15 min for a 1 h race instead of watching it live)
  • Time sync — auto-align via video creation_time vs FIT start, or set --offset manually
  • Preview — --preview 30 to validate sync before a full encode
  • Hardware encode — optional NVIDIA NVENC via --hw-encode
  • NLE export — --overlay-only for a transparent overlay clip (Premiere / Resolve)

Requirements

Dependency Notes
Python 3.11+ Developed/tested on 3.11–3.14
FFmpeg Must be on PATH (ffmpeg and ffprobe)
Chromium (optional) Only for --map-mode capture (playwright install chromium)

Install FFmpeg

Windows (recommended):

winget install Gyan.FFmpeg

Restart the terminal so PATH updates.

macOS: brew install ffmpeg
Linux: use your package manager (apt install ffmpeg, etc.)

Installation

git clone https://github.com/TunaSen/oriVideoGen.git
cd oriVideoGen

python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS / Linux
source .venv/bin/activate

pip install -e .

# Optional: only needed for --map-mode capture
playwright install chromium

Verify:

oriVideoGen --help
ffmpeg -version

Quick start

  1. Upload / open your route in Livelox and copy the viewer URL. It must include classId, for example:

    https://www.livelox.com/Viewer/My-Event/H21?classId=382140

  2. Export the Garmin activity as .fit (Garmin Connect / watch export).

  3. Have your DJI (or other) MP4 ready.

  4. Run a short preview first:

oriVideoGen \
  --video "C:\Videos\DJI_run.mp4" \
  --fit "C:\Garmin\activity.fit" \
  --livelox "https://www.livelox.com/Viewer/My-Event/H21?classId=382140" \
  --output "C:\Videos\preview.mp4" \
  --preview 30
  1. If overlays look early/late, adjust --offset (see Time sync), then render the full video without --preview.

CLI reference

oriVideoGen [OPTIONS]
Option Required Description
--video PATH yes Source POV / action-cam MP4
--fit PATH yes Garmin FIT activity file
--livelox URL yes Livelox viewer URL containing classId
--output PATH yes Output MP4 (or directory for --map-only)
--offset FLOAT no Manual sync offset in seconds
--map-mode auto|render|capture no Default auto
--participant TEXT no Prefer a Livelox route matching this name
--preview FLOAT no Only encode the first N seconds
--overlay-only no Export transparent overlay MOV (qtrle)
--map-only no Export circular map inset PNG sequence
--hw-encode no Prefer h264_nvenc when available
--layout PATH no YAML layout overrides
--cache-dir PATH no Override Livelox cache directory
--force-fetch no Ignore cached Livelox downloads
--capture-fps FLOAT no Screenshot FPS for capture mode (default 1.0)
-v / --verbose no Debug logging

Overlay layout

Default layout matches a common YouTube orienteering POV style:

Position Content Source
Top-left Elevation, gradient, total distance Garmin FIT
Top-right Heart rate (BPM) Garmin FIT
Bottom-right Circular orienteering map Livelox
Bottom-right (below map) Speed gauge (km/h) Garmin FIT
Bottom-center Heading / compass (°) Garmin FIT

Customize sizes and margins with a YAML file (see examples/sample_config.yaml):

oriVideoGen ... --layout examples/sample_config.yaml

How the Livelox map is obtained

The map overlay is required. oriVideoGen never skips it; it only speeds up how the data is acquired.

Mode: render (primary, fastest)

  1. POST https://www.livelox.com/Data/ClassInfo with the classId from your URL
  2. Download the class blob (map metadata)
  3. Save map PNG, projection / world-file matrix, controls, course line, and GPS route
  4. For each video frame, crop the map around the runner and draw the purple trail + controls

Results are cached under:

~/.cache/oriVideoGen/livelox/<classId>/

Re-runs of the same class are much faster unless you pass --force-fetch.

Mode: capture (fallback)

If scraping fails or you want the original Livelox rendering:

oriVideoGen ... --map-mode capture

Playwright opens the viewer headlessly, seeks the replay timeline (no real-time playback), and screenshots the map region. For a one-hour race at 1 FPS this is typically on the order of 10–15 minutes, still far less than a live screen recording.

Mode: auto (default)

Try programmatic fetch/render; if route/controls are missing or fetch fails, fall back to capture.

Time sync

Offset meaning:

video_time = fit_activity_time + offset
  • Positive offset — FIT activity starts later than the video (skip the beginning of the video to reach FIT start)
  • Negative offset — FIT started before the video

Auto methods:

  1. --offset if provided (always wins)
  2. Else video container creation_time vs FIT session/record start
  3. Else assume both start together (offset = 0) and print a hint

Practical tip: always validate with --preview 30 before a full-length encode.

Example workflows

Full YouTube export

oriVideoGen \
  --video run.mp4 \
  --fit activity.fit \
  --livelox "https://www.livelox.com/Viewer/Event/Class?classId=123456" \
  --output youtube_ready.mp4 \
  --hw-encode

Force original Livelox visuals

oriVideoGen \
  --video run.mp4 \
  --fit activity.fit \
  --livelox "https://www.livelox.com/Viewer/Event/Class?classId=123456" \
  --output youtube_ready.mp4 \
  --map-mode capture \
  --capture-fps 1

Transparent overlay for Premiere / DaVinci Resolve

oriVideoGen \
  --video run.mp4 \
  --fit activity.fit \
  --livelox "https://www.livelox.com/Viewer/Event/Class?classId=123456" \
  --output overlay.mov \
  --overlay-only

Debug map frames only

oriVideoGen \
  --video run.mp4 \
  --fit activity.fit \
  --livelox "https://www.livelox.com/Viewer/Event/Class?classId=123456" \
  --output map_frames \
  --map-only \
  --preview 60

Project structure

oriVideoGen/
├── oriVideoGen/
│   ├── cli.py                 # Click CLI entry point
│   ├── config/layout.py       # Overlay layout defaults + YAML loader
│   ├── telemetry/             # FIT parse + per-frame interpolation
│   ├── sync/                  # ffprobe + creation_time / offset aligner
│   ├── livelox/               # ClassInfo fetch, route decode, Playwright capture
│   └── render/                # Widgets, overlay canvas, FFmpeg compositor
├── examples/sample_config.yaml
├── tests/
├── pyproject.toml
└── README.md

Development

pip install -e ".[dev]"
pytest

Tests include FIT/interpolation unit tests, Livelox URL/georef/route decoder tests, and an FFmpeg smoke compose (synthetic clip, no network).

Limitations & notes

  • Livelox’s official public API does not expose maps/routes for copyright reasons. This tool uses the same class viewer data endpoints that the web UI loads for a class you can already open, plus an optional headless capture of that viewer.
  • Map images remain under the event organizer’s copyright. Use this with your own footage and routes; do not redistribute maps you are not allowed to share.
  • Auto sync depends on trustworthy video timestamps. DJI metadata is usually good; if clocks disagree, use --offset.
  • Full-length 4K encodes can take tens of minutes; use --preview and --hw-encode when available.

License

MIT. See repository license metadata. Orienteering maps shown in your outputs remain subject to the rights of the map authors / organizers.

About

Generate YouTube-ready orienteering videos from DJI footage, Garmin FIT telemetry, and Livelox maps

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages