-
-
Save VeryCrazyDog/c4b1006d7d86a29395861ccac185cfec to your computer and use it in GitHub Desktop.
Download all workouts from sports-tracker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// VCD: Change folder and file name, add metadata file, script shall be executed in Google Chrome. | |
// 'Show more' button shall be pressed until all workout are shown on page. You might have | |
// to manually filter out some control characters in the generated script. Works on 2020-11-21. | |
// | |
// Bilan: My fork fixed some api changes, added images downloading and informative gpx file names. Works on 2020/11/02. | |
// | |
// You can then upload it to Strava using this oneliner: | |
// find * -name '*.gpx' -print0 | while read -d $'\0' i; do ID=`echo $i | sed 's/.*id--//' | sed 's/--activity.*//'`; ACTIVITY=`echo $i | sed 's/.*--activity--//' | sed 's/--title.*//'`; NAME=`echo $i | sed 's/--file.gpx//' | sed 's/.*--title--//'`" ($ID/$ACTIVITY)"; echo "\n$NAME\n"; curl -X POST https://www.strava.com/api/v3/uploads -H "Authorization: Bearer ___TOKEN___" -F file=@"$i" -F data_type="gpx" -F description="SportsTracker import" -F name="$NAME" -F external_id="$i"; sleep 10;done | |
// | |
// Original description: | |
// based entirely on this blog post: | |
// http://druss.co/2016/04/export-all-workouts-from-sports-tracker/ | |
// unfortunately the original script no longer works, moslty because jQuery is | |
// no longer available on sports-tracker pages. | |
// | |
// I've compiled the changes proposed in the comments in the script below. | |
// to use the script, login to your sports-tracker account | |
// change URL to http://www.sports-tracker.com/diary/workout-list | |
// open browser console (Cmd-Shift-I) | |
// paste the script, hit enter - it'll run and print something like this to the cosole: | |
// curl -o SportsTracker-<..id..>.gpx "http://www.sports-tracker.com/apiserver....." | |
// right-click on the console and save the contents to a file, call it download-all-workouts.sh | |
// open terminal, change to the directory where you saved the contents of the console | |
// edit the file - remove the javascript at the beginning of the file leaving only curl commands | |
// fix permissions: | |
// $>chmod +x download-all-workouts.sh | |
// run the script: | |
// $>./download-all-workouts.sh | |
const key = "sessionkey="; | |
const valueStartIndex = document.cookie.indexOf(key) + key.length; | |
const token = document.cookie.substring(valueStartIndex, document.cookie.indexOf(';', valueStartIndex)); | |
const activities = { | |
0: "Walking", | |
1: "Run", | |
2: "Cycling", | |
11: "Hiking", | |
13: "alpineski", | |
14: "rowing", | |
15: "rowing", | |
26: "Motorsports", | |
}; | |
function formatDate(value) { | |
return `${value.getFullYear()}${(value.getMonth() + 1).toString().padStart(2, '0')}${value.getDate().toString().padStart(2, '0')}` | |
} | |
async function downloadOne(item) { | |
const href = item.href; | |
const id = href.substr(href.lastIndexOf('/') + 1, 24); | |
const url = 'https://api.sports-tracker.com/apiserver/v1/workout/exportGpx/' + id + '?token=' + token; | |
const activityTypeId = item.querySelector(".activity-icon").getAttribute('activity-icon'); | |
const activityType = activities[activityTypeId] ?? 'Unknown'; | |
const description = item.querySelector(".description").textContent.trim(); | |
const activityDateText = item.querySelector(".date").textContent.trim(); | |
const activityDate = new Date(activityDateText); | |
const duration = item.querySelector(".duration").textContent.trim(); | |
const distance = item.querySelector(".distance").textContent.trim(); | |
const avgSpeed = item.querySelector(".avg-speed").textContent.trim(); | |
const avgPace = item.querySelector(".avg-pace").textContent.trim(); | |
const heartRate = item.querySelector(".hr").textContent.trim(); | |
const energy = item.querySelector(".energy").textContent.trim(); | |
const cadence = item.querySelector(".cadence").textContent.trim(); | |
const folderName = `${formatDate(activityDate)}-${activityType}-${id}`; | |
const filename = `${folderName}.gpx`; | |
console.log(`mkdir '${folderName}'`); | |
console.log(`curl -o '${folderName}/${filename}' '${url}';`); | |
console.log(`echo 'Activity Type: ${activityType}' > '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Description: ${description}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Activity Date: ${activityDateText}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Duration: ${duration}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Distance: ${distance}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Average Speed: ${avgSpeed}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Average Pace: ${avgPace}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Heart Rate: ${heartRate}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Energy: ${energy}' >> '${folderName}/Metadata.txt';`); | |
console.log(`echo 'Cadence: ${cadence}' >> '${folderName}/Metadata.txt';`); | |
await downloadImages(id, folderName); | |
} | |
async function downloadImages(id, folderName) { | |
const imagesUrl = 'https://api.sports-tracker.com/apiserver/v1/images/workout/' + id + '?token=' + token; | |
const imagesApiResponse = await fetch(imagesUrl); | |
const images = (await imagesApiResponse.json()).payload; | |
for (let i = 0; i < images.length; i++) { | |
let image = images[i]; | |
const imageDate = new Date(image.timestamp); | |
const filename = `${formatDate(imageDate)}-${image.location.x}-${image.location.y}-${image.key}.jpg`; | |
let url = `https://api.sports-tracker.com/apiserver/v1/image/scale/${image.key}.jpg?width=${image.width}&height=${image.height}`; | |
console.log(`curl -o "${folderName}/${filename}" "${url}";`); | |
} | |
} | |
async function loopThroughItems(items) { | |
for (let i = 0; i < items.length; i++) { | |
await downloadOne(items[i]); | |
} | |
} | |
const items = document.querySelectorAll("ul.diary-list__workouts li a"); | |
setTimeout(() => { | |
console.log('Discovered workouts:', items.length) | |
console.log('-------------- Script Start --------------') | |
loopThroughItems(items).then(() => { | |
console.log('-------------- Script End --------------') | |
console.log(`Finished processing ${items.length} workouts`) | |
}); | |
}, 1000) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment