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 %}
+
+
+
+
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+
+ {% for message in messages %}
+ -
+
{{ message }}
+
+ {% endfor %}
+
+ {% 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))