diff --git a/README.md b/README.md index 767c7c44..00e347c9 100644 --- a/README.md +++ b/README.md @@ -235,28 +235,11 @@ To obtain your current profile:
Expand * Get CLIENT_ID and CLIENT_SECRET from https://www.strava.com/settings/api - -
Using launcher (Windows and macOS only) - -* Set the authorization callback domain of your API application to ``launcher.zwift.com`` -* Create a ``strava-api.txt`` file in the ``storage`` directory containing your client ID and secret -``` -CLIENT_ID -CLIENT_SECRET -``` -* Use the "Settings - Strava" button in the launcher window to authorize. - -
- -
Using strava_auth script - +* __NOTE:__ instead of performing the steps below you can instead set the authorization callback domain of your API application to ``launcher.zwift.com`` and use the "Settings - Strava" button in the launcher window (Windows and macOS only). * Run ``scripts/strava_auth.py --client-id CLIENT_ID --client-secret CLIENT_SECRET`` * Or, if using the Windows zoffline.exe version without Python installed you can run ``strava_auth.exe`` obtained from https://github.com/zoffline/zwift-offline/releases/tag/zoffline_helper in place of ``scripts/strava_auth.py`` * Open http://localhost:8000/ and authorize. * Move the resulting ``strava_token.txt`` (saved in whatever directory you ran ``strava_auth.py`` in) into the ``storage/1`` directory. - -
- * If testing, ride at least 300 meters, shorter activities won't be uploaded.
diff --git a/cdn/static/web/launcher/settings.html b/cdn/static/web/launcher/settings.html index 11ccdef9..a46b899e 100644 --- a/cdn/static/web/launcher/settings.html +++ b/cdn/static/web/launcher/settings.html @@ -12,13 +12,7 @@

Logged in as {{ username }}

{% endif %} Power curves Zwift - {% if api %} - {% if not token %} - Strava - {% else %} - Remove Strava token - {% endif %} - {% endif %} + Strava Garmin Intervals diff --git a/cdn/static/web/launcher/strava.html b/cdn/static/web/launcher/strava.html new file mode 100644 index 00000000..a051b445 --- /dev/null +++ b/cdn/static/web/launcher/strava.html @@ -0,0 +1,52 @@ +{% extends "./layout.html" %} +{% block content %} +

Strava

+ {% if username != "zoffline" %} +

Logged in as {{ username }}

+ {% endif %} +
+
+ Back + {% if cid or cs %} + Remove credentials + {% endif %} + {% if token %} + Remove authorization + {% elif cid and cs %} + Authorize + {% endif %} +
+
+
+
+
+
+
+ + +
+
+ + +
+
+
+
+ +
+
+
+ {% with messages = get_flashed_messages() %} + {% if messages %} + + {% endif %} + {% endwith %} +
+
+{% endblock %} diff --git a/zwift_offline.py b/zwift_offline.py index 3507a05f..bbc434e4 100644 --- a/zwift_offline.py +++ b/zwift_offline.py @@ -128,12 +128,6 @@ with warnings.catch_warnings(): from stravalib.client import Client -STRAVA_API_FILE = "%s/strava-api.txt" % STORAGE_DIR -if os.path.exists(STRAVA_API_FILE): - with open(STRAVA_API_FILE) as f: - STRAVA_CLIENT_ID = int(f.readline().rstrip('\r\n')) - STRAVA_CLIENT_SECRET = f.readline().rstrip('\r\n') - from tokens import * # Android uses https for cdn @@ -710,11 +704,27 @@ def reset(username): return render_template("reset.html", username=current_user.username) -@app.route("/strava", methods=['GET']) +@app.route("/strava//", methods=["GET", "POST"]) +@login_required +def strava(username): + profile_dir = '%s/%s' % (STORAGE_DIR, current_user.player_id) + api = '%s/strava_api.bin' % profile_dir + token = os.path.isfile('%s/strava_token.txt' % profile_dir) + if request.method == "POST": + if request.form['client_id'] == "" or request.form['client_secret'] == "": + flash("Client ID and secret can't be empty.") + return render_template("strava.html", username=current_user.username, token=token) + encrypt_credentials(api, (request.form['client_id'], request.form['client_secret'])) + cred = decrypt_credentials(api) + return render_template("strava.html", username=current_user.username, cid=cred[0], cs=cred[1], token=token) + + +@app.route("/strava_auth", methods=['GET']) @login_required -def strava(): +def strava_auth(): + cred = decrypt_credentials('%s/%s/strava_api.bin' % (STORAGE_DIR, current_user.player_id)) client = Client() - url = client.authorization_url(client_id=STRAVA_CLIENT_ID, + url = client.authorization_url(client_id=cred[0], redirect_uri='https://launcher.zwift.com/authorization', scope=['activity:write']) return redirect(url) @@ -723,13 +733,14 @@ def strava(): @app.route("/authorization", methods=["GET", "POST"]) @login_required def authorization(): - try: + try: + cred = decrypt_credentials('%s/%s/strava_api.bin' % (STORAGE_DIR, current_user.player_id)) client = Client() code = request.args.get('code') - token_response = client.exchange_code_for_token(client_id=STRAVA_CLIENT_ID, client_secret=STRAVA_CLIENT_SECRET, code=code) + token_response = client.exchange_code_for_token(client_id=int(cred[0]), client_secret=cred[1], code=code) with open(os.path.join(STORAGE_DIR, str(current_user.player_id), 'strava_token.txt'), 'w') as f: - f.write(str(STRAVA_CLIENT_ID) + '\n') - f.write(STRAVA_CLIENT_SECRET + '\n') + f.write(cred[0] + '\n') + f.write(cred[1] + '\n') f.write(token_response['access_token'] + '\n') f.write(token_response['refresh_token'] + '\n') f.write(str(token_response['expires_at']) + '\n') @@ -737,7 +748,7 @@ def authorization(): except Exception as exc: logger.warning('Strava: %s' % repr(exc)) flash("Strava authorization canceled.") - return redirect(url_for('settings', username=current_user.username)) + return redirect(url_for('strava', username=current_user.username)) def encrypt_credentials(file, cred): @@ -945,9 +956,7 @@ def settings(username): if os.path.isfile(achievements_file): stat = os.stat(achievements_file) achievements = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(stat.st_mtime)) - api = os.path.isfile(STRAVA_API_FILE) - token = os.path.isfile(os.path.join(profile_dir, 'strava_token.txt')) - return render_template("settings.html", username=current_user.username, profile=profile, achievements=achievements, api=api, token=token) + return render_template("settings.html", username=current_user.username, profile=profile, achievements=achievements) @app.route("/download/", methods=["GET"]) @@ -968,14 +977,15 @@ def download_avatarLarge(player_id): @app.route("/delete/", methods=["GET"]) @login_required def delete(filename): - player_id = current_user.player_id credentials = ['garmin_credentials.bin', 'zwift_credentials.bin', 'intervals_credentials.bin'] - if filename not in ['profile.bin', 'achievements.bin', 'strava_token.txt'] and filename not in credentials: + strava = ['strava_api.bin', 'strava_token.txt'] + if filename not in ['profile.bin', 'achievements.bin'] + credentials + strava: return '', 403 - profile_dir = os.path.join(STORAGE_DIR, str(player_id)) - delete_file = os.path.join(profile_dir, filename) + delete_file = os.path.join(STORAGE_DIR, str(current_user.player_id), filename) if os.path.isfile(delete_file): os.remove("%s" % delete_file) + if filename in strava: + return redirect(url_for('strava', username=current_user.username)) if filename in credentials: flash("Credentials removed.") return redirect(url_for('settings', username=current_user.username))