diff --git a/README.md b/README.md index 901f633..d040cd6 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Usage: `python gcexport.py [how_many] [format] [directory]` `[how_many]` specifies the number of recent activities you wish to download. You may also specify `all` to download everything. The default is `1`. -`[format]` specifies the desired export format. Valid formats are `gpx`, `tcx` or `original`. The default is `gpx`. When using `original`, a ZIP file is exported that contains the initial input format (e.g., FIT files). +`[format]` specifies the desired export format. Valid formats are `gpx`, `tcx`, `original` or `json`. The default is `gpx`. When using `original`, a ZIP file is exported that contains the initial input format (e.g., FIT files). When using `json`, a JSON file is created with all the metadata. `[directory]` specifies the output directory for the CSV file and the GPX files. The default is a subdirectory with the format `YYYY-MM-DD_garmin_connect_export`. If the directory does not exist, it will be created. If it does exist, activities with existing GPX files will be skipped and the CSV file will be appended to. This should make it easy to restart failed downloads without repeating work. This also allows you to specify a master directory so that this script can be run regularly (to maintain an up-to-date backup) without re-downloading everything. diff --git a/gcexport.py b/gcexport.py index fcea013..26f54fa 100755 --- a/gcexport.py +++ b/gcexport.py @@ -24,6 +24,7 @@ import urllib2, cookielib, json from fileinput import filename +import json if len(argv) > 4: raise Exception('Too many arguments.') @@ -36,8 +37,8 @@ if len(argv) > 2: data_format = argv[2].lower() - if data_format != 'gpx' and data_format != 'tcx' and data_format != 'original': - raise Exception('Format can only be "gpx," "tcx," or "original."') + if data_format not in ['gpx', 'tcx', 'original', 'json']: + raise Exception('Format can only be "gpx", "tcx", "original", or "json".') else: data_format = 'gpx' @@ -141,7 +142,7 @@ def http_req(url, post=None, headers={}): # Query Garmin Connect result = http_req(url_gc_search + urlencode(search_params)) json_results = json.loads(result) # TODO: Catch possible exceptions here. - + search = json_results['results']['search'] @@ -169,6 +170,9 @@ def http_req(url, post=None, headers={}): filename = activities_directory + '/activity_' + a['activity']['activityId'] + '.tcx' download_url = url_gc_tcx_activity + a['activity']['activityId'] + '?full=true' file_mode = 'w' + elif data_format == 'json': + filename = activities_directory + '/activity_' + a['activity']['activityId'] + '.json' + file_mode = 'w' else: filename = activities_directory + '/activity_' + a['activity']['activityId'] + '.zip' download_url = url_gc_original_activity + a['activity']['activityId'] @@ -184,23 +188,27 @@ def http_req(url, post=None, headers={}): # should pick up where it left off. print '\tDownloading file...', - try: - data = http_req(download_url) - except urllib2.HTTPError as e: - # Handle expected (though unfortunate) error codes; die on unexpected ones. - if e.code == 500 and data_format == 'tcx': - # Garmin will give an internal server error (HTTP 500) when downloading TCX files if the original was a manual GPX upload. - # Writing an empty file prevents this file from being redownloaded, similar to the way GPX files are saved even when there are no tracks. - # One could be generated here, but that's a bit much. Use the GPX format if you want actual data in every file, as I believe Garmin provides a GPX file for every activity. - print 'Writing empty file since Garmin did not generate a TCX file for this activity...', - data = '' - elif e.code == 404 and data_format == 'original': - # For manual activities (i.e., entered in online without a file upload), there is no original file. - # Write an empty file to prevent redownloading it. - print 'Writing empty file since there was no original activity data...', - data = '' - else: - raise Exception('Failed. Got an unexpected HTTP error (' + str(e.code) + ').') + if data_format != 'json': + try: + data = http_req(download_url) + except urllib2.HTTPError as e: + # Handle expected (though unfortunate) error codes; die on unexpected ones. + if e.code == 500 and data_format == 'tcx': + # Garmin will give an internal server error (HTTP 500) when downloading TCX files if the original was a manual GPX upload. + # Writing an empty file prevents this file from being redownloaded, similar to the way GPX files are saved even when there are no tracks. + # One could be generated here, but that's a bit much. Use the GPX format if you want actual data in every file, as I believe Garmin provides a GPX file for every activity. + print 'Writing empty file since Garmin did not generate a TCX file for this activity...', + data = '' + elif e.code == 404 and data_format == 'original': + # For manual activities (i.e., entered in online without a file upload), there is no original file. + # Write an empty file to prevent redownloading it. + print 'Writing empty file since there was no original activity data...', + data = '' + else: + raise Exception('Failed. Got an unexpected HTTP error (' + str(e.code) + ').') + + else: + data = json.dumps(a) save_file = open(filename, file_mode) save_file.write(data)