def api_profiles_activities_id(player_id, activity_id): if not request.stream: return '', 400 activity_id = int(activity_id) & 0xffffffffffffffff activity = activity_pb2.Activity() activity.ParseFromString(request.stream.read()) update_protobuf_in_db('activity', activity, activity_id) response = '{"id":%s}' % activity_id if request.args.get('upload-to-strava') != 'true': return response, 200 try: from stravalib.client import Client except ImportError: logger.warn( "stravalib is not installed. Skipping Strava upload attempt.") return response, 200 strava = Client() try: with open('%s/strava_token.txt' % STORAGE_DIR, 'r') as f: strava.access_token = f.read().rstrip('\r\n') except: logger.warn( "Failed to read %s/strava_token.txt. Skipping Strava upload attempt." ) return response, 200 try: # See if there's internet to upload to Strava strava.upload_activity(BytesIO(activity.fit), data_type='fit', name=activity.name) # XXX: assume the upload succeeds on strava's end. not checking on it. except: logger.warn("Strava upload failed. No internet?") return response, 200
def api_profiles_activities_id(player_id, activity_id): if not request.stream: return '', 400 activity_id = int(activity_id) & 0xffffffffffffffff activity = activity_pb2.Activity() activity.ParseFromString(request.stream.read()) update_protobuf_in_db('activity', activity, activity_id) response = '{"id":%s}' % activity_id if request.args.get('upload-to-strava') != 'true': return response, 200 # Unconditionally *try* and upload to strava and garmin since profile may # not be properly linked to strava/garmin (i.e. no 'upload-to-strava' call # will occur with these profiles). strava_upload(player_id, activity) garmin_upload(player_id, activity) return response, 200
def api_profiles_activities_id(player_id, activity_id): if not request.stream: return '', 400 activity_id = int(activity_id) & 0xffffffffffffffff activity = activity_pb2.Activity() activity.ParseFromString(request.stream.read()) update_protobuf_in_db('activity', activity, activity_id) response = '{"id":%s}' % activity_id if request.args.get('upload-to-strava') != 'true': return response, 200 if os.path.exists(ENABLEGHOSTS_FILE): urlopen("http://cdn.zwift.com/saveghost?%s" % quote(activity.name)) # Unconditionally *try* and upload to strava and garmin since profile may # not be properly linked to strava/garmin (i.e. no 'upload-to-strava' call # will occur with these profiles). strava_upload(player_id, activity) garmin_upload(player_id, activity) return response, 200
def api_profiles_activities(player_id): if request.method == 'POST': if not request.stream: return '', 400 activity = activity_pb2.Activity() activity.ParseFromString(request.stream.read()) activity.id = get_id('activity') insert_protobuf_into_db('activity', activity) return '{"id": %ld}' % activity.id, 200 # request.method == 'GET' activities = activity_pb2.Activities() cur = g.db.cursor() cur.execute("SELECT * FROM activity WHERE player_id = ?", (str(player_id), )) for row in cur.fetchall(): activity = activities.activities.add() row_to_protobuf(row, activity, exclude_fields=['fit']) return activities.SerializeToString(), 200
def api_profiles_activities(player_id): if request.method == 'POST': if not request.stream: return '', 400 activity = activity_pb2.Activity() activity.ParseFromString(request.stream.read()) activity.id = get_id('activity') insert_protobuf_into_db('activity', activity) return '{"id": %ld}' % activity.id, 200 # request.method == 'GET' activities = activity_pb2.Activities() cur = g.db.cursor() # Select every column except 'fit' - despite being a blob python 3 treats it like a utf-8 string and tries to decode it cur.execute( "SELECT id, player_id, f3, name, f5, f6, start_date, end_date, distance, avg_heart_rate, max_heart_rate, avg_watts, max_watts, avg_cadence, max_cadence, avg_speed, max_speed, calories, total_elevation, strava_upload_id, strava_activity_id, f23, fit_filename, f29, date FROM activity WHERE player_id = ?", (str(player_id), )) for row in cur.fetchall(): activity = activities.activities.add() row_to_protobuf(row, activity, exclude_fields=['fit']) return activities.SerializeToString(), 200
def main(argv): global args access_token = None cookies = None print( 'WARNING: remove zoffline IP from hosts file before running this script' ) parser = argparse.ArgumentParser(description='Zwift activity uploader') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') parser.add_argument('--dont-check-certificates', action='store_false', dest='verifyCert', default=True) parser.add_argument('-u', '--user', help='Zwift user name') parser.add_argument('-a', '--activity', help='Activity file') args = parser.parse_args() activity = activity_pb2.Activity() if args.activity: activity_file = args.activity else: activity_file = input("Enter activity file: ") if not os.path.isfile(activity_file): print('Activity file not found') sys.exit() with open(activity_file, 'rb') as fd: try: activity.ParseFromString(fd.read()) except: print('Could not parse activity file') sys.exit() if args.user: username = args.user else: username = input("Enter Zwift login (e-mail): ") if not sys.stdin.isatty( ): # This terminal cannot support input without displaying text print( '*WARNING* The current shell (%s) cannot support hidden text entry.' % os.name) print('Your password entry WILL BE VISIBLE.') print( 'If you are running a bash shell under windows, try executing this program via winpty:' ) print('>winpty python %s' % argv[0]) password = input("Enter password (will be shown):") else: password = getpass.getpass("Enter password: ") session = requests.session() access_token, refresh_token = login(session, username, password) activity.player_id = get_player_id(session, access_token) ret = upload_activity(session, access_token, activity) print(ret) logout(session, refresh_token)