yihong0618
running_page
Python

Make your own running home page

Last updated Jul 7, 2026
4.5k
Stars
1.4k
Forks
55
Issues
+4
Stars/day
Attention Score
99
Language breakdown
No language data available.
Files click to expand
README

Note

  • clone or Fork before vercel 404 need to pull the latest code
  • python in README means python3 python
  • use v2.0 need change vercel setting from gatsby to vite
  • 2023.09.26 garmin need secret_string(and in Actions) get
python runpage/getgarmin_secret.py ${email} ${password}
     # if cn
     python runpage/getgarmin_secret.py ${email} ${password} --is-cn
  • 2024.09.29: Added Elevation Gain field, If you forked the project before this update, please run the following command:
- To resolve errors: sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: activities.elevation_gain - If you don't have a local environment, set RUNTYPE to dbupdater in the .github/workflows/rundatasync.yml file once then change back.
python runpage/dbupdater.py

- For old data: To include Elevation Gain for past activities, perform a full reimport. - To show the 'Elevation Gain' column, modify SHOWELEVATIONGAIN in src/utils/const.ts - note: Elevation Gain may be inaccurate. You can use Strava's "Correct Elevation" or Garmin's "Elev Corrections" feature for more precise data.

  • This project now uses MapCN (free) by default. If you choose to use Mapbox, please get your own token. Do not use the project maintainer's token - check this issue and issue #1055

Create a personal running home page

GitHub Action Chat on telegram

demo

English | 简体中文 | Wiki

Runner's Page Show

Running page runners


| Runner | page | App | | ---------------------------------------------------- | ---------------------------------------------- | ----------- | | zhubao315 | | Strava | | shaonianche | | Strava | | yihong0618 | | Nike | | superleeyom | | Strava | | geekplux | | Nike | | guanlan | | Strava | | tuzimoe | | Nike | | ben29 | | Strava | | kcllf | | Garmin-cn | | mq | | Keep | | zhaohongxuan | | Strava | | yvetterowe | | Strava | | love-exercise | | Keep | | zstone12 | | Keep | | Lax | | Keep | | lusuzi | | Nike | | wh1994 | | Garmin | | liuyihui | | Keep | | sunyunxian | page> | Strava | | AhianZhang | | Nike | | L1cardo | | Nike | | luckylele666 | | Strava | | MFYDev | | Garmin-cn | | Eished | | Keep | | Liuxin | | Nike | | loucx | | Nike | | winf42 | | Garmin-cn | | sun0225SUN | | Nike | | Zhan | | Nike | | Dennis | | Garmin-cn | | hanpei | | Garmin-cn | | liugezhou | | Strava | | Jason Tan | page> | Nike | | Conge | page> | Strava | | zHElEARN | | Strava | | Ym9i | | Strava | | jianchengwang | page> | Suunto | | fxbin | | Keep | | shensl4499 | | codoon | | haowei93 | | gpx | | stevenash0822 | | Strava | | Vint | | Keep | | Muyids | | Garmin-cn | | Gao Hao | page/> | Garmin-cn | | Jinlei | | 咕咚 | | RealTiny656 | page/> | JoyRun | | EINDEX | | Strava/Nike | | Melt | | Strava | | deepinwine | | Garmin-cn | | Echo | | JoyRun | | Jeffggmm | page/> | Garmin | | s1smart | page/> | Strava | | XmchxUp | page/> | Strava | | Ryan | | Strava | | PPZ | | Strava | | Yer1k | | Strava | | AlienVision | | Strava | | 闻笑忘 | | 苹果健身 | | Vensent | page/> | Garmin | | Zeonsing | | Coros | | yaoper | | codoon | | NoZTurn | | Strava | | laqieer | page/> | Strava | | Guoxin | | Strava | | laihj | | 苹果健身 | | Ginta | | Keep | | Samuel | page/> | Keep | | Evan | | Keep | | Shuqi | | Garmin | | shugoal | | Garmin | | Daniel | | Codoon | | arthurfsy2 | | Garmin | | JMGutiH | page/> | Strava | | Bolyn | | Coros | | LeiChen | page/> | Coros | | itrunner | | Garmin | | maslke | page/> | Garmin-cn | | Niewei Yang | | Strava | | RUN.LOG | | Strava | | StoneRicky | page/> | COROS | | coutureone | | Garmin |

How it works

image

Features

  • GitHub Actions automatically synchronizes running data and generates page displays
  • Support for Vercel (recommended) and GitHub Pages automated deployment
  • React Hooks
  • Mapbox for map display
  • Supports most sports apps such as nike strava...
  • Support for metric and imperial units
  • Terminal UI (TUI) for browsing activities locally
automatically backup gpx data for easy backup and uploading to other software.
>
Note: If you don't want to make the data public, you can choose strava's fuzzy processing, or private repositories.

Support

Download

Clone or fork the repo.

git clone https://github.com/yihong0618/running_page.git --depth=1

Installation and testing (node >= 20 python >= 3.11)

pip3 install -r requirements.txt
npm install -g corepack && corepack enable && pnpm install
pnpm develop

Open your browser and visit

TUI (Terminal UI)

You can browse your activities in the terminal using the built-in Textual TUI.

# Using make
make tui

Or run directly with uv

uv run run_page

Or specify a custom activities.json path

uv run run_page /path/to/your/activities.json

Keyboard shortcuts inside TUI:

  • 1 / 2 – Switch between List and Stats views
  • / – Change year filter
  • / – Navigate activities
  • y – Cycle through years
  • t – Cycle through activity types
  • q – Quit

Docker

# NRC
docker build -t runningpage:latest . --build-arg app=NRC --build-arg nikerefresh_token=""

Garmin

docker build -t runningpage:latest . --build-arg app=Garmin --build-arg secretstring=""

Garmin-CN

docker build -t runningpage:latest . --build-arg app=Garmin-CN --build-arg secretstring=""

Strava

docker build -t runningpage:latest . --build-arg app=Strava --build-arg clientid="" --build-arg clientsecret="" --build-arg refreshtoken=""

NiketoStrava

docker build -t runningpage:latest . --build-arg app=NiketoStrava --build-arg nikerefreshtoken="" --build-arg clientid="" --build-arg clientsecret="" --build-arg refreshtoken=""

Keep

docker build -t runningpage:latest . --build-arg app=Keep --build-arg keepph --build-arg keep_password=""

run

docker run -itd -p 80:80 running_page:latest

visit

Open your browser and visit localhost:80

Imperial Units

  • add --build-arg VITEUSEIMPERIAL=true to docker build ...
  • add --units imperial flag to each python3 runpage/gensvg.py ... command in the Dockerfile

Local sync data

Modifying Mapbox token

If you use English please change IS_CHINESE = false in src/utils/const.ts
Suggested changes to your own Mapbox token
const MAPBOX_TOKEN =
  'pk.eyJ1IjoieWlob25nMDYxOCIsImEiOiJja2J3M28xbG4wYzl0MzJxZm0ya2Fua2p2In0.PNKfkeQwYuyGOTT_x9BJ4Q';

Change Default Map Tile Style

In addition to using the default map tile style, you can customize the map display by modifying the following configurations in src/utils/const.ts:
const MAPTILEVENDOR = 'mapcn'; // Default (free!)
const MAPTILESTYLE = 'osm-bright';
const MAPTILEACCESS_TOKEN = ''; // Not needed for MapCN

Currently supported MAPTILEVENDOR options include:

  • "mapcn" - MapCN map services (FREE, no token required) ⭐ DEFAULT & RECOMMENDED
  • "mapbox" - Mapbox map services (requires token, has costs)
  • "maptiler" - MapTiler map services (free tier available)
  • "stadiamaps" - Stadia Maps services (free tier available)
Using MapCN (Default) MapCN is a free map tile provider and is now the default. No configuration needed!

Available MapCN styles:

  • osm-bright - Light OpenStreetMap style (default)
  • osm-liberty - Alternative light style
  • dark-matter - Dark theme style
No access token required! 🎉

Attribution

When using MapCN (Carto Basemaps), please ensure you comply with their attribution requirements:

The project template already includes appropriate attribution in the map display.

Using Other Providers

If you prefer Mapbox, MapTiler, or Stadia Maps, you can change the vendor:
const MAPTILEVENDOR = 'mapbox'; // or 'maptiler' or 'stadiamaps'
const MAPTILESTYLE = 'dark-v10'; // style for chosen vendor
const MAPTILEACCESSTOKEN = 'youraccesstokenhere';

Each MAPTILEVENDOR provides multiple MAPTILESTYLE options. Ensure the style matches your selected vendor. For available MAPTILESTYLE names, refer to the definitions in src/utils/const.ts.

When using "mapbox", "maptiler" or "stadiamaps", you must configure an ACCESS_TOKEN. The default token may cause quota limit issues if not replaced.

Custom your page

  • Find src/static/site-metadata.ts in the repository directory, find the following content, and change it to what you want.
siteMetadata: {
  siteTitle: 'Running Page', #website title
  siteUrl: 'https://yihong.run', #website url
  logo: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQTtc69JxHNcmN1ETpMUX4dozAgAN6iPjWalQ&usqp=CAU', #logo img
  description: 'Personal site and blog',
  navLinks: [
    {
      name: 'Blog', #navigation name
      url: 'https://yihong.run/running', #navigation url
    },
    {
      name: 'About',
      url: 'https://github.com/yihong0618/running_page/blob/master/README-CN.md',
    },
  ],
},
  • Modifying styling in src/utils/const.ts
// styling: set to false if you want to disable dash-line route
const USEDASHLINE = true;
// styling: route line opacity: [0, 1]
const LINE_OPACITY = 0.4;
// styling: set to true if you want to display only the routes without showing the map
// Note: This config only affects the page display; please refer to "privacy protection" below for data protection
// update for now 2024/11/17 the privacy mode is true
const PRIVACY_MODE = true;
// update for now 2024/11/17 the lights on default is false
// styling: set to false if you want to make light off as default, only effect when PRIVACY_MODE = false
const LIGHTS_ON = false;
// set to true if you want to show the 'Elevation Gain' column
const SHOWELEVATIONGAIN = true;
  • To use Google Analytics, you need to modify the configuration in the src/utils/analytics.ts file.
const USEGOOGLEANALYTICS = false;
const GOOGLEANALYTICSTRACKING_ID = '';
privacy protection,setting flowing env:
# ignore distance for each polyline start and end.
IGNORESTARTEND_RANGE = 200

ignore meters for each point in below polyline.

IGNORE_RANGE = 200

a polyline include point you want to ignore.

IGNORE_POLYLINE = ktjrFoemeU~IorGq}DeB

Do filter before saving to database, you will lose some data, but you can protect your privacy, when you using public repo. enable for set 1, disable via unset.

IGNOREBEFORESAVING =

You can using Google map Interactive Polyline Encoder Utility, to making your IGNOREPOLYLINE.

Download your running data

Download your running data and do not forget to generate svg in total page

GPX

Make your GPX data


Copy all your gpx files to GPX_OUT or new gpx files

python runpage/gpxsync.py

TCX

Make your TCX data


Copy all your tcx files to TCX_OUT or new tcx files

python runpage/tcxsync.py

FIT

Make your FIT data


Copy all your tcx files to FIT_OUT or new fit files

python runpage/fitsync.py

Garmin

Get your Garmin data


  • If you only want to sync type running add args --only-run
  • If you only want tcx files add args --tcx
  • If you only want fit files add args --fit
  • If you are using Garmin as a data source, it is recommended that you pull the code to your local environment to run and obtain the Garmin secret.
The Python version must be >=3.8

Get Garmin Secret

Enter the following command in the terminal

# to get secret_string
python runpage/getgarmin_secret.py ${your email} ${your password}

Execute Garmin Sync Script

Copy the Secret output in the terminal,If you are using GitHub, please configure GARMINSECRETSTRING in GitHub Action.

# use this secret_string
python runpage/garminsync.py ${secret_string}

example:

python runpage/getgarmin_secret.py xxxxxxxxxxx

only-run:

python runpage/garminsync.py xxxxxxxxxxxxxx(secret_string) --only-run

Garmin-CN(China)

Get your Garmin-CN data


  • If you only want to sync type running add args --only-run
  • If you only want tcx files add args --tcx
  • If you only want fit files add args --fit
  • If you are using Garmin as a data source, it is recommended that you pull the code to your local environment to run and obtain the Garmin secret.
The Python version must be >=3.10

Get Garmin CN Secret

Enter the following command in the terminal

# to get secret_string
python runpage/getgarmin_secret.py ${your email} ${your password} --is-cn

get<em>garmin</em>cn_secret

Execute Garmin CN Sync Script

Copy the Secret output in the terminal,If you are using GitHub, please configure GARMINSECRETSTRING_CN in GitHub Action. get</em>garmin_secret

example:

python runpage/garminsync.py xxxxxxxxx(secret_string) --is-cn

only-run:

python runpage/garminsync.py xxxxxxxxxxxxxx(secret_string)  --is-cn --only-run

Garmin-CN to Garmin

Sync your Garmin-CN data to Garmin


  • If you only want to sync type running add args --only-run
The Python version must be >=3.10

Get Garmin CN Secret

Enter the following command in the terminal

# to get secret_string
python runpage/getgarmin_secret.py ${your email} ${your password} --is-cn

Get Garmin Secret

Enter the following command in the terminal

# to get secret_string
python runpage/getgarmin_secret.py ${your email} ${your password}

Sync Garmin CN to Garmin

Enter the following command in the terminal

# to sync garmin-cn to garmin-global
python runpage/garminsynccnglobal.py ${garmincnsecretstring} ${garminsecret_string}

Nike Run Club New

Get your Nike Run Club data


Please note:Due to the discontinuation of Nike Run Club in mainland China, you can only log in through a VPN. Before starting, please ensure that you are using a global non-mainland China proxy, allowing you to access nike.com instead of nike.com.cn, as shown in the following image.

nike.com

login
  • after successful login,openF12->Application->localstorage-> copy the content of "access_token" from the value of keyhttps://www.nike.com.
  • Execute in the root directory , you should be able to see the image below, and then you can log into your account on the mobile as usual:
python runpage/nikesync.py ${access_token}

tg<em>image</em>166091873

if you want to automate the submission of NRC data, you can refer to issue692.

If you've previously synced activities and want to continue syncing new ones, with --continue-sync args

python runpage/nikesync.py ${access_token} --continue-sync

Nike Run Club

Get your Nike Run Club data


Please note: When you choose to deploy running_page on your own server, due to Nike has blocked some IDC's IP band, maybe your server cannot sync Nike Run Club's data correctly and display 403 error, then you have to change another way to host it.

Get Nike's refresh_token

ALL need to do outside GFW.

example img

  • Login from this website, open F12 -> XHR -> get the refreshtoken from login api.
  • copy this refresh_token and use it in GitHub Secrets or in command line
  • Execute in the root directory:
python runpage/nikesync.py ${nike refresh_token}

example:

python runpage/nikesync.py eyJhbGciThiMTItNGIw

example img

Strava

Get your Strava data


[!NOTE]
Strava updated its Developer Program in June 2026. If you use Strava as the data source, or upload activities to Strava before syncing, check your app tier in the Strava API settings dashboard. Standard Tier developers need a Strava subscription to access the API; existing Standard Tier developers are affected from June 30, 2026. See Strava's announcement for details.

My API Application

Created successfully:


Created Successfully

  • Use the link below to request all permissions: Replace ${your_id} in the link with My API Application Client ID
https://www.strava.com/oauth/authorize?clientid=${yourid}&responsetype=code&redirecturi=http://localhost/exchangetoken&approvalprompt=force&scope=readall,profile:readall,activity:read_all,profile:write,activity:write

Example:

https://www.strava.com/oauth/authorize?clientid=115321&responsetype=code&redirecturi=http://localhost/exchangetoken&approvalprompt=force&scope=readall,profile:readall,activity:readall,profile:write,activity:write

get</em>all_permissions

  • Get the code value in the link

example:

http://localhost/exchangetoken?state=&code=1dab37edd9970971fb502c9efdd087f4f3471e6e&scope=read,activity:write,activity:readall,profile:write,profile:readall,readall

code value:

1dab37edd9970971fb502c9efdd087f4f3471e6

get_code

  • Use ClientidClientsecretCode get refresh_token: Execute in Terminal/iTerm
curl -X POST https://www.strava.com/oauth/token \
   -F client_id=${Your Client ID} \
   -F client_secret=${Your Client Secret} \
   -F code=${Your Code} \
   -F granttype=authorizationcode

example:

curl -X POST https://www.strava.com/oauth/token \
   -F client_id=12345 \
   -F client_secret=b21d0bfb377998ed1ac3b0 \
   -F code=d09b58abface48003 \
   -F granttype=authorizationcode

get</em>refresh_token

  • Sync Strava data
> The first time you synchronize Strava data you need to change line 12 of the code False to True in strava_sync.py, and then change it to False after it finishes running. > If you only want to sync type running add args --only-run
python runpage/stravasync.py ${clientid} ${clientsecret} ${refresh_token}

References: - - -

TCXtoStrava

upload all tcx files to strava


  • follow the strava steps
  • copy all your tcx files to TCX_OUT
  • Execute in the root directory:
python runpage/tcxtostravasync.py ${clientid} ${clientsecret}  ${stravarefreshtoken}

example:

python runpage/tcxtostravasync.py xxx xxx xxx
   or
   python runpage/tcxtostravasync.py xxx xxx xxx --all
  • if you want to all files add args --all

TCXtoGarmin

upload all tcx files to garmin


  • follow the garmin steps
  • copy all your tcx files to TCX_OUT
  • Execute in the root directory:
python3 runpage/tcxtogarminsync.py ${{ secrets.GARMINSECRETSTRING_CN }} --is-cn

example:

python runpage/tcxtogarminsync.py xxx --is-cn
   or Garmin Global
   python runpage/tcxtogarminsync.py xxx
  • if you want to all files add args --all

GPXtoStrava

upload all gpx files to strava


  • follow the strava steps
  • copy all your gpx files to GPX_OUT
  • Execute in the root directory:
python runpage/gpxtostravasync.py ${clientid} ${clientsecret}  ${stravarefreshtoken}

example:

python runpage/gpxtostravasync.py xxx xxx xxx
   or
   python runpage/tcxtostravasync.py xxx xxx xxx --all
  • if you want to all files add args --all

NiketoStrava

Get your Nike Run Club data and upload to strava


  • follow the nike and strava steps
  • Execute in the root directory:
python runpage/niketostravasync.py ${nikerefreshtoken} ${clientid} ${clientsecret} ${stravarefreshtoken}

example:

python runpage/niketostravasync.py eyJhbGciThiMTItNGIw  xxx xxx xxx

GarmintoStrava

Get your Garmin data and upload to strava


  • finish garmin and strava setup
  • Execute in the root directory:
python runpage/garmintostravasync.py  ${clientid} ${clientsecret} ${stravarefreshtoken} ${garminsecretstring} --is-cn

e.g.

python runpage/garmintostravasync.py  xxx xxx xxx xx

StravatoGarmin

Get your Strava data and upload to Garmin


  • finish garmin and strava setup, at the same time, you need to add additional strava config in GitHub Actions secret: secrets.STRAVAEMAIL,secrets.STRAVAPASSWORD,secrets.STRAVAJWT, Note: STRAVAJWT is superior to STRAVAEMAIL and STRAVAPASSWORD ,STRAVAJWT is the stravaremember_token field of the Strava web login Cookie.
  • Execute in the root directory:
python runpage/stravatogarminsync.py ${{ secrets.STRAVACLIENTID }} ${{ secrets.STRAVACLIENTSECRET }} ${{ secrets.STRAVACLIENTREFRESHTOKEN }}  ${{ secrets.GARMINSECRETSTRING }} ${{ secrets.STRAVAEMAIL }} ${{ secrets.STRAVA_PASSWORD }}

if your garmin account region is China, you need to execute the command:

python runpage/stravatogarminsync.py ${{ secrets.STRAVACLIENTID }} ${{ secrets.STRAVACLIENTSECRET }} ${{ secrets.STRAVACLIENTREFRESHTOKEN }}  ${{ secrets.GARMINSECRETSTRINGCN }} ${{ secrets.STRAVAEMAIL }} ${{ secrets.STRAVAPASSWORD }}  ${{ secrets.STRAVA_JWT }} --is-cn

If you want to add Garmin Device during sync, you should add --usefakegarmindevice argument, this will add a Garmin Device (Garmin Forerunner 245 by default, and you can change device in garmindevice_adaptor.py) in synced Garmin workout record, this is essential when you want to sync the workout record to other APP like Keep, JoyRun etc.

image

the final command will be:

python runpage/stravatogarminsync.py ${{ secrets.STRAVACLIENTID }} ${{ secrets.STRAVACLIENTSECRET }} ${{ secrets.STRAVACLIENTREFRESHTOKEN }}  ${{ secrets.GARMINSECRETSTRINGCN }} ${{ secrets.STRAVAEMAIL }} ${{ secrets.STRAVAPASSWORD }} ${{ secrets.STRAVAJWT }}--usefakegarmindevice

ps: when initializing for the first time, if you have a large amount of strava data, some data may fail to upload, just retry several times.

COROS

Get your COROS data


  • If you only want to sync type running add args --only-run

Enter the following command in the terminal

python runpage/corossync.py 'your coros account' 'your coros password'

Modify rundatasync.yml env.RUN_TYPE: coros

Set the Coros account information in github action

  • configure the COROSACCOUNT , COROSPASSWORD
github-action

iGPSPORT

Get your iGPSPORT data

Enter the following command in the terminal

python3 runpage/igpsportsync.py 'your igpsport phone' 'password' --with-gpx

You can replace with-gpx with with-fit to acquire data in fit format.

Joyrun

Get your Joyrun data

Enter the following command in the terminal

python3 runpage/joyrunsync.py 'your joyrun phone' 'verication code' --with-gpx

You can replace with-gpx with with-tcx to acquire data in tcx format.

Komoot

Get your Komoot data

Enter the following command in the terminal

python3 runpage/komootsync.py 'your komoot email' 'password' --with-gpx

| Parameter | Description | | --- | --- | | mail | Login using specified email address | | password | Use provided password and skip interactive prompt | | -n, --anonymous | Skip authentication, no interactive prompt (valid only with -d) | | --with-gpx | Download all tours as GPX | | -r, --remove-deleted | Remove GPX files (from --output dir) without corresponding tour in Komoot (deleted and previous versions) | | --start-date=YYYY-MM-DD | Filter tours on or after specified date | | --end-date=YYYY-MM-DD | Filter tours on or before specified date | | -e, --no-poi | Do not include highlights as POIs |

Onelap

Get your Onelap data

Enter the following command in the terminal

python3 runpage/onelapsync.py 'your onelap phone' 'password' --with-fit

Intervals.icu

Get your Intervals.icu data


Sync running activities from Intervals.icu. Downloads original FIT/GPX files.

  • Log in to Intervals.icu, go to SettingsDeveloper Settings to find your Athlete ID and create an API Key.
  • Execute in the root directory:
python runpage/intervalsicusync.py ${athleteid} ${api_key}

If you want to sync all historical data (default is last 6 months):

python runpage/intervalsicusync.py ${athleteid} ${api_key} --all

To specify a custom start date:

python runpage/intervalsicusync.py ${athleteid} ${api_key} --start-date 2024-01-01

If your data comes from a Huawei/China device using the GCJ-02 coordinate system, add --gcj02 to fix the coordinate offset (converts GCJ-02 to WGS-84 in downloaded FIT/GPX/TCX files):

python runpage/intervalsicusync.py ${athleteid} ${api_key} --gcj02

GitHub Actions

  • Change RUNTYPE to intervalsicu in the rundatasync.yml file
  • Add INTERVALSICUATHLETEID and INTERVALSICUAPIKEY to your GitHub repository secrets

Total Data Analysis

Running data display

python runpage/gensvg.py -h

usage: gen_svg.py [-h] [--gpx-dir DIR] [--output FILE] [--language LANGUAGE] [--year YEAR] [--title TITLE] [--athlete NAME] [--special FILE] [--type TYPE] [--background-color COLOR] [--track-color COLOR] [--track-color2 COLOR] [--text-color COLOR] [--special-color COLOR] [--special-color2 COLOR] [--units UNITS] [--verbose] [--logfile FILE] [--special-distance DISTANCE] [--special-distance2 DISTANCE] [--min-distance DISTANCE] [--use-localtime] [--from-db] [--github-style GITHUB_STYLE] [--circular-rings] [--circular-ring-color COLOR] [--empty-data-color COLOR] [--birth YYYY-MM]

options: -h, --help show this help message and exit --gpx-dir DIR Directory containing GPX files (default: current directory). --output FILE Name of generated SVG image file (default: "poster.svg"). --language LANGUAGE Language (default: english). --year YEAR Filter tracks by year; "NUM", "NUM-NUM", "all" (default: all years) --title TITLE Title to display. --athlete NAME Athlete name to display (default: "John Doe"). --special FILE Mark track file from the GPX directory as special; use multiple times to mark multiple tracks. --type TYPE Type of poster to create (default: "grid", available: "grid", "circular", "github", "monthoflife"). --background-color COLOR Background color of poster (default: "#222222"). --track-color COLOR Color of tracks (default: "#4DD2FF"). --track-color2 COLOR Secondary color of tracks (default: none). --text-color COLOR Color of text (default: "#FFFFFF"). --special-color COLOR Special track color (default: "#FFFF00"). --special-color2 COLOR Secondary color of special tracks (default: none). --units UNITS Distance units; "metric", "imperial" (default: "metric"). --verbose Verbose logging. --logfile FILE --special-distance DISTANCE Special Distance1 by km and color with the special_color --special-distance2 DISTANCE Special Distance2 by km and corlor with the special_color2 --min-distance DISTANCE min distance by km for track filter --use-localtime Use utc time or local time --from-db activities db file --github-style GITHUB_STYLE github svg style; "align-firstday", "align-monday" (default: "align-firstday"). --birth YYYY-MM Birth date in format YYYY-MM

Circular Type Options: --circular-rings Draw distance rings. --circular-ring-color COLOR Color of distance rings.

Github Type Options: --empty-data-color COLOR Color for empty dates in github style poster (default: #444444)

Generate github style svg show

python runpage/gensvg.py --from-db --title "${{ env.TITLE }}" --type github --athlete "${{ env.ATHLETE }}" --special-distance 10 --special-distance2 20 --special-color yellow --special-color2 red --output assets/github.svg --use-localtime --min-distance 0.5

If you want to change the display color of empty data(only github style), please use --empty-data-color:

python runpage/gensvg.py --from-db --title "${{ env.TITLE }}" --type github --athlete "${{ env.ATHLETE }}" --special-distance 10 --special-distance2 20 --special-color yellow --special-color2 red --output assets/github.svg --use-localtime --min-distance 0.5 ----empty-data-color grey

Generate grid style svg show

python runpage/gensvg.py --from-db --title "${{ env.TITLE_GRID }}" --type grid --athlete "${{ env.ATHLETE }}"  --output assets/grid.svg --min-distance 10.0 --special-color yellow --special-color2 red --special-distance 20 --special-distance2 40 --use-localtime
python runpage/gensvg.py --from-db --type circular --use-localtime

Generate a "Runner Month of Life" visualization as if your entire life consisted of only 1000 months.

python3 runpage/gensvg.py --from-db --type monthoflife --birth 1989-03 --special-distance 10 --special-distance2 20 --special-color '#f9d367'  --special-color2 '#f0a1a8' --output assets/mol.svg --use-localtime --athlete yihong0618 --title 'Runner Month of Life'

Generate your share png using GPT gpt-image-1(last one)

python runpage/autosharesync.py --apikey xxxxxxxxx  --base_url xxxxxxxx

If you want to generate a share png for a date

python runpage/autosharesync.py --apikey xxxxxxxxx --base_url xxxxxxxx --date 2023-11-11

If you want to auto gen in ci you can refer this link

For more display effects, see:

server(recommendation vercel)

Use Vercel to deploy


  • vercel connects to your GitHub repo.

image

  • import repo

image

  • Awaiting completion of deployment
  • Visits

Use Cloudflare to deploy


  • Click Workers & Pages on the left side.
  • Click Create application and select Pages tab, connect your GitHub account and select running_page Repo, then click Begin setup.
  • Scroll down to Build settings, choose Create React App from Framework preset, and set Build output directory to dist.
  • Scroll down, click Environment variables (advanced), then add a variable like the below:
> Variable name = PYTHON_VERSION, Value = 3.11
  • Click Save and Deploy

Deploy to GitHub Pages


  • Go to repository's Settings -> GitHub Pages -> Source, choose GitHub Actions
  • Go to the repository's Actions -> Workflows -> All Workflows, choose Run Data Sync from the left panel, and click Run workflow.
- The Run Data Sync will update data and then trigger the Publish GitHub Pages workflow - Make sure the workflow runs without errors.
  • Open your website to check on the results
- note if the website doesn't reflect the latest data, please refresh it by F5. - Some browsers (e.g. Chrome) won't refresh if there is a cache, you then need to use Ctrl+F5 (Windows) or Shift+Cmd+r (Mac) to force clearing the cache and reload the page.
  • make sure you have write permissions in Workflow permissions settings.
  • If you want to deploy your runningpage to xxx.github.io instead of xxx.github.io/runningpage or redirect your GitHub Pages to a custom domain, you need to do three things:
- Rename your forked running_page repository to xxx.github.io, where xxx is your GitHub username - Modify the Build module in gh-pages.yml, remove ${{ github.event.repository.name }} and change to run: PATH_PREFIX=/ pnpm build - In src/static/site-metadata.ts, set siteUrl: '' or your custom domain URL

GitHub Actions

Modifying information in GitHub Actions


Actions source code The following steps need to be taken

  • change to your app type and info

image

  • Add your secret in repo Settings > Secrets (add only the ones you need).

image

  • My secret is as follows

image

  • Go to repository's Settings -> Code and automation -> Actions ->General, Scroll to the bottom, find Workflow permissions, choose the first option Read and write permissions, click Save.

Shortcuts

Automate with iOS Shortcuts


Take the keep app as an example. Close the app after running, and then automatically trigger Actions to update the data.

  • Get actions id (need to apply token)
curl https://api.github.com/repos/yihong0618/running_page/actions/workflows -H "Authorization: token d8xxxxxxxxxx" # change to your config

get-action-id

  • Binding shortcut instruction
1. Get it via icloud running-page-shortcuts-template

2. Modify the dictionary parameters in the following figure

  • Automation

Storing Data Files in GitHub Cache

Storing Data Files in GitHub Cache


When SAVEDATAINGITHUBCACHE is set to true in the rundatasync.yml file, the script can store fetched and intermediate data files in the GitHub Action Cache. This helps keep your GitHub commit history and directory clean.

If you are deploying using GitHub Pages, it is recommended to set this value to true, and set BUILDGHPAGES to true.

Fit file

supported manufacturer:

  • [x] Garmin
  • [x] magene

TODO

  • [x] Complete this document.
  • [x] Support Garmin, Garmin China
  • [x] support for nike+strava
  • [x] Support English
  • [x] Refine the code
  • [x] add new features
  • [ ] tests
  • [ ] support the world map
  • [ ] support multiple types, like hiking, biking~
  • [ ] support for Zeep life

Contribution

  • Any Issues welcome.
  • PR need to disscuss first(For LLM year 2026)
  • You can PR share your Running page in README I will merge it.
Before submitting PR:
  • Format Python code with black (black .)

Special thanks

Recommended Forks

Support

Just enjoy it~

Raycast Extension

FAQ

  • Strava API limit
Strava API Rate Limit Exceeded. Retry after 100 seconds
  Strava API Rate Limit Timeout. Retry in 799.491622 seconds
  • vercel git ignore gh-pages:
you can change settings -> build -> Ignored Build Step -> Custom command
if [ "$VERCELGITCOMMIT_REF" != "gh-pages" ]; then exit 1; else exit 0;
🔗 More in this category

© 2026 GitRepoTrend · yihong0618/running_page · Updated daily from GitHub