Skip to content

Instantly share code, notes, and snippets.

@bryder
Forked from wadecourtney/gcdownloader.py
Created June 6, 2014 18:29
Show Gist options
  • Save bryder/f0b388cb65e77cf9fab7 to your computer and use it in GitHub Desktop.
Save bryder/f0b388cb65e77cf9fab7 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import http.client
http.client.HTTPConnection.debuglevel = 0
#import urllib.request
from urllib.request import Request, urlopen, HTTPCookieProcessor
from urllib.parse import urlencode
import argparse
import sys,os
import errno
import json
GC_LOGIN = "https://connect.garmin.com/signin"
GC_ACTIVITIES = "http://connect.garmin.com/proxy/activity-search-service-1.0/json/activities?start=%d"
GC_DL = {
'kml': "http://connect.garmin.com/proxy/activity-service-1.0/kml/activity/%d?full=true",
'tcx': "http://connect.garmin.com/proxy/activity-service-1.1/tcx/activity/%d?full=true",
'gpx': "http://connect.garmin.com/proxy/activity-service-1.1/gpx/activity/%d?full=true"
}
def login(username, password):
# Fields from the login form on:
# https://connect.garmin.com/signin
data = {
'login': 'login',
'login:loginUsernameField': username,
'login:password': password,
'login:rememberMe': 'on',
'login:signInButton': 'Sign In',
'javax.faces.ViewState': 'j_id1'
}
# Make an initial request to the login page to obtain cookies. These
# cookies MUST be included with the login request.
loginResponse = urlopen(GC_LOGIN)
cookies = get_cookies(loginResponse)
# POST headers
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': cookies
}
# Create a login request
loginRequest = Request(GC_LOGIN, data=bytes(urlencode(data), 'utf-8'),
headers=headers)
# Send request
response = urlopen(loginRequest)
return cookies
def get_cookies(response):
cookies = []
for header in response.getheaders():
if header[0] == 'Set-Cookie':
cookies.append(header[1].split(';')[0])
return bytes(';'.join(cookies), 'utf-8')
def get_activities(cookies, args):
counter = int(args.START)
numDownloaded = 0
loop = True
while loop:
# make a request to the search service
activitySearch = Request(GC_ACTIVITIES % counter,
headers={'Cookie': cookies})
response = urlopen(activitySearch)
data = response.read()
# Parse json data
results = json.loads(data.decode('utf-8'))
if 'activities' not in results['results']:
break
for activity in results['results']['activities']:
print(str(counter) + "\tDownloading: "
+ activity['activity']['activityName']['value'])
if args.TYPE == 'kml':
if 'endLatitude' in activity['activity']:
# Make sure that a file exists by checking
# a gps coord
download(cookies, activity['activity']['activityId'])
numDownloaded += 1
else:
print("\t\t* No KML file exists for this activity.")
else:
download(cookies, activity['activity']['activityId'])
numDownloaded += 1
counter += 1
if (int(args.NUM_DOWNLOAD) != -1) and \
(numDownloaded >= int(args.NUM_DOWNLOAD)):
loop = False
break
if (int(results['results']['search']['totalFound']) == counter):
loop = False
break
if numDownloaded == 1:
print("\nDownloaded " + str(numDownloaded) + " activity.")
else:
print("\nDownloaded " + str(numDownloaded) + " activities.")
def download(cookies, activityid):
# Download request
request = Request(GC_DL[args.TYPE] % int(activityid),
headers={'Cookie': cookies})
response = urlopen(request)
data = response.read()
# Write data to file
f = open(args.DIRECTORY + activityid + '.' + args.TYPE,
'wt', encoding='utf-8')
f.write(data.decode('utf-8'))
f.close()
if __name__ == "__main__":
print("Garmin Connect Activity Downloader")
print("This program has essentially no error checking; it is not guaranteed to fail gracefully! Use at your own rsik\n")
parser = argparse.ArgumentParser(
description="Download Garmin Connect Activities")
parser.add_argument('-u', '--user', required = True,
dest='GC_USERNAME', help='Garmin Connnect Username')
parser.add_argument('-N', dest='NUM_DOWNLOAD', default='-1',
help='Number of activities to download')
parser.add_argument('-D', '--dir', dest='DIRECTORY', default='',
help='Directory to install activities to. Defaults to current directory.')
parser.add_argument('-t', '--type', dest='TYPE', default='kml',
help='Download type: kml, tcx, gpx')
parser.add_argument('--start', dest="START", default=0,
help='Activity number to start with')
args = parser.parse_args()
# Check arguments
# Download file type
if args.TYPE not in ['kml', 'tcx', 'gpx']:
print("Invalid download format.")
sys.exit(0)
# Download directory
if args.DIRECTORY:
if ((args.DIRECTORY.rfind('/') == 0) or \
(args.DIRECTORY.rfind('/') > 0)):
if args.DIRECTORY[-1] != '/':
args.DIRECTORY = args.DIRECTORY + '/'
if not os.path.exists(args.DIRECTORY):
try:
os.makedirs(args.DIRECTORY)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
else:
print("Invalid directory")
sys.exit(0)
print("Logging in with: " + args.GC_USERNAME)
GC_PASSWORD = input("Password: ")
cookies = login(args.GC_USERNAME, GC_PASSWORD)
print("Downloading activities...")
get_activities(cookies, args)
print("\nFinished!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment