From b6e2bc5541614fa5d8459fd300865cb6491d5402 Mon Sep 17 00:00:00 2001 From: Anas HAFSI Date: Mon, 16 Feb 2026 12:52:03 +0100 Subject: [PATCH 1/2] feat(script): add Strava to Fitbod OAuth live container --- README.md | 34 ++++++- strava-fitbod-oauth-live-container.user.js | 111 +++++++++++++++++++++ 2 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 strava-fitbod-oauth-live-container.user.js diff --git a/README.md b/README.md index cd2ce8e..8769c44 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,21 @@ A userscript fork of [Open-In-Apollo](https://github.com/AnthonyGress/Open-In-Apollo) by Anthony Gress to support Apollo running within [Live Container](https://github.com/AnthonyGress/Open-In-Apollo). Supports Live Container **only**, for normal sideloaded Apollo see original script. -I've also adapted this to a few other scripts for Twitter, Youtube, Instagram and uploaded here. Thanks to https://gist.github.com/ryuya0124/e6e59ceba8f03bf40f3b5accedc52e44 for Twitter script. +I've also adapted this to a few other scripts for Twitter, Youtube, Instagram, Strava/Fitbod and uploaded here. Thanks to https://gist.github.com/ryuya0124/e6e59ceba8f03bf40f3b5accedc52e44 for Twitter script. -## Downloads +## Available Userscripts -- Safari extension to run user script (free): https://apps.apple.com/us/app/userscripts/id1463298887 +### Strava ↔ Fitbod OAuth (Featured) +- **[Strava to Fitbod OAuth Live Container](strava-fitbod-oauth-live-container.user.js)** - Enables Strava authorization within Fitbod in Live Container + - Intercepts OAuth flow and redirects directly to Live Container + - No intermediate web pages or 404 errors + - Seamless integration experience -- Userscript code: [Open In Apollo Live Container Userscript](https://github.com/nathandaven/Open-In-Apollo-Live-Container/raw/525f547ed2cbca1d8edaeee8a9a8c52715521224/open-in-apollo-live-container.user.js) +### Other App Scripts +- [Open In Apollo Live Container](https://github.com/nathandaven/Open-In-Apollo-Live-Container/raw/525f547ed2cbca1d8edaeee8a9a8c52715521224/open-in-apollo-live-container.user.js) - Reddit links +- [Open In Twitter Live Container](open-in-twitter-live-container.user.js) - Twitter/X links +- [Open In Instagram Live Container](open-in-instagram-live-container.user.js) - Instagram links +- [Open In YouTube Live Container](open-in-youtube-live-container.user.js) - YouTube links ## How to install @@ -29,4 +37,22 @@ I've also adapted this to a few other scripts for Twitter, Youtube, Instagram an - Tested on iOS 18.2.1, with Apollo 1.15.11 + ApolloPatcher 0.0.9 and Live Container 3.1.59-release +### Strava ↔ Fitbod OAuth Connection +**What it does**: Enables linking your Strava account to Fitbod when running inside Live Container. + +**Why it's needed**: Fitbod uses deep links (`fitbod://`) for OAuth callbacks, but Safari can't open these URLs directly. This script intercepts the authorization flow and redirects to Live Container instead. + +**How to use**: +1. Install the [Strava OAuth userscript](strava-fitbod-oauth-live-container.user.js) +2. In Fitbod (within Live Container), go to Settings → Link Strava +3. Click "Authorize" on the Strava page +4. Script automatically captures the OAuth response and opens Fitbod +5. Your Strava account is now linked! + +**Technical flow**: +1. Intercepts form submission on Strava OAuth page +2. Submits authorization request via fetch +3. Captures the `fitbod://` redirect URL from response headers +4. Converts to `livecontainer://open-web-page?url=...` +5. Redirects directly to Fitbod in Live Container diff --git a/strava-fitbod-oauth-live-container.user.js b/strava-fitbod-oauth-live-container.user.js new file mode 100644 index 0000000..df395db --- /dev/null +++ b/strava-fitbod-oauth-live-container.user.js @@ -0,0 +1,111 @@ +// ==UserScript== +// @name Strava to Fitbod OAuth Live Container +// @version 1.0.0 +// @author AnasHafsi +// @description Intercepts Strava OAuth and redirects directly to Fitbod in Live Container +// @match *://www.strava.com/oauth/* +// @match *://www.strava.com/oauth/authorize* +// @match *://www.strava.com/oauth/mobile/authorize* +// @downloadURL https://github.com/nathandaven/Open-In-Live-Container/raw/refs/heads/main/strava-fitbod-oauth-live-container.user.js +// @updateURL https://github.com/nathandaven/Open-In-Live-Container/raw/refs/heads/main/strava-fitbod-oauth-live-container.user.js +// @homepage https://github.com/nathandaven/Open-In-Apollo-Live-Container/tree/main +// @grant none +// ==/UserScript== + +(function () { + 'use strict'; + function utf8_to_b64(str) { + return window.btoa(unescape(encodeURIComponent(str))); + } + + function extractRedirectUri(formAction) { + const match = formAction.match(/redirect_uri=([^&]+)/); + if (match) { + return decodeURIComponent(match[1]); + } + return null; + } + + // Intercept form submission and redirect directly to Live Container + function interceptForm() { + try { + const form = document.querySelector('form[action*="redirect_uri"]'); + if (!form) { + console.log('No authorization form found'); + return false; + } + + console.log('Found authorization form'); + + const formAction = form.getAttribute('action'); + const redirectUri = extractRedirectUri(formAction); + + if (!redirectUri || !redirectUri.includes('fitbod://')) { + console.log('Not a Fitbod OAuth flow, skipping'); + return false; + } + + form.addEventListener('submit', async function (e) { + e.preventDefault(); + + const formData = new FormData(form); + const actionUrl = new URL(form.action, window.location.origin); + + const params = new URLSearchParams(actionUrl.search); + for (const [key, value] of formData.entries()) { + params.set(key, value); + } + + try { + const response = await fetch(actionUrl.pathname + '?' + params.toString(), { + method: 'POST', + credentials: 'include', + redirect: 'manual' + }); + const location = response.headers.get('Location'); + + if (location) { + if (location.startsWith('fitbod://')) { + // Convert to Live Container URL + const liveContainerUrl = `livecontainer://open-web-page?url=${utf8_to_b64(location)}`; + window.location.href = liveContainerUrl; + } else if (location.startsWith('https://fitbod.me')) { + // Fallback: convert https to fitbod:// then to Live Container + const fitbodUrl = location.replace('https://', 'fitbod://'); + const liveContainerUrl = `livecontainer://open-web-page?url=${utf8_to_b64(fitbodUrl)}`; + window.location.href = liveContainerUrl; + } else { + console.error('Unexpected redirect URL:', location); + alert('Unexpected OAuth redirect. Check console for details.'); + } + } else { + alert('OAuth authorization failed - no redirect found'); + } + } catch (error) { + console.error('Error during OAuth submission:', error); + alert('Error during authorization: ' + error.message); + } + }); + return true; + + } catch (error) { + console.error('Error setting up form interceptor:', error); + return false; + } + } + + // Run when DOM is ready + function init() { + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', interceptForm); + } else { + interceptForm(); + } + + // Retry a few times in case of dynamic loading + setTimeout(interceptForm, 500); + setTimeout(interceptForm, 1000); + } + + init(); +})(); \ No newline at end of file From 52ced1909bc45dc41858a910edc3c34c5790ef21 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 17:26:41 +0000 Subject: [PATCH 2/2] docs: add Spotify userscript to README - Added Spotify to Other App Scripts list - Updated intro to mention Spotify support https://claude.ai/code/session_01AEzTJhK3JSHgavcpN5hcg4 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8769c44..cc42dcc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A userscript fork of [Open-In-Apollo](https://github.com/AnthonyGress/Open-In-Apollo) by Anthony Gress to support Apollo running within [Live Container](https://github.com/AnthonyGress/Open-In-Apollo). Supports Live Container **only**, for normal sideloaded Apollo see original script. -I've also adapted this to a few other scripts for Twitter, Youtube, Instagram, Strava/Fitbod and uploaded here. Thanks to https://gist.github.com/ryuya0124/e6e59ceba8f03bf40f3b5accedc52e44 for Twitter script. +I've also adapted this to a few other scripts for Twitter, Youtube, Instagram, Spotify, Strava/Fitbod and uploaded here. Thanks to https://gist.github.com/ryuya0124/e6e59ceba8f03bf40f3b5accedc52e44 for Twitter script. ## Available Userscripts @@ -17,6 +17,7 @@ I've also adapted this to a few other scripts for Twitter, Youtube, Instagram, S - [Open In Twitter Live Container](open-in-twitter-live-container.user.js) - Twitter/X links - [Open In Instagram Live Container](open-in-instagram-live-container.user.js) - Instagram links - [Open In YouTube Live Container](open-in-youtube-live-container.user.js) - YouTube links +- [Open In Spotify Live Container](open-in-spotify-live-container.user.js) - Spotify links ## How to install