-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.py
More file actions
211 lines (173 loc) · 6.91 KB
/
auth.py
File metadata and controls
211 lines (173 loc) · 6.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""
Browser authentication and cookie detection for MyFitnessPal.
Handles automatic cookie detection from Firefox, Edge, and Chrome.
If no cookies found, opens browser for manual login.
Can also clear cookies to force fresh login.
"""
import time
import webbrowser
from pathlib import Path
import os
import sqlite3
import browser_cookie3
try:
import config
except ImportError:
print("ERROR: config.py not found!")
print("Please copy config.example.py to config.py and fill in your settings.")
raise
def find_firefox_cookie_file():
"""
Find Firefox cookie file on Linux (standard, Snap, or Flatpak).
Returns:
str: Path to cookies.sqlite file, or None if not found
"""
import platform
home = Path.home()
paths = []
if platform.system() == 'Linux':
# Linux Firefox locations
paths = [
home / '.mozilla' / 'firefox', # Standard Firefox
home / 'snap' / 'firefox' / 'common' / '.mozilla' / 'firefox', # Snap Firefox
home / '.var' / 'app' / 'org.mozilla.firefox' / '.mozilla' / 'firefox', # Flatpak Firefox
]
else:
# Windows Firefox locations (fallback)
paths = [
Path(os.environ.get('APPDATA', '')) / 'Mozilla' / 'Firefox',
Path(os.environ.get('LOCALAPPDATA', '')) / 'Mozilla' / 'Firefox',
]
# Microsoft Store Firefox location
local_app_data = os.environ.get('LOCALAPPDATA', '')
if local_app_data:
packages_dir = Path(local_app_data) / 'Packages'
if packages_dir.exists():
mozilla_packages = [p for p in packages_dir.iterdir()
if 'mozilla' in p.name.lower() and 'firefox' in p.name.lower()]
for pkg in mozilla_packages:
store_path = pkg / 'LocalCache' / 'Roaming' / 'Mozilla' / 'Firefox'
if store_path.exists():
paths.insert(0, store_path)
# Look for cookies.sqlite in profiles
for firefox_path in paths:
if not firefox_path.exists():
continue
# Check for Profiles subdirectory (standard structure)
profiles_dir = firefox_path / 'Profiles'
if profiles_dir.exists():
for profile in profiles_dir.iterdir():
cookie_file = profile / 'cookies.sqlite'
if cookie_file.exists():
print(f" Found cookie file: {cookie_file}")
return str(cookie_file)
# Also check directly in firefox_path (some installations)
for item in firefox_path.iterdir():
if item.is_dir():
cookie_file = item / 'cookies.sqlite'
if cookie_file.exists():
print(f" Found cookie file: {cookie_file}")
return str(cookie_file)
print(" No Firefox cookie file found in standard locations.")
return None
def get_working_cookies():
"""
Get cookies from Firefox only.
Returns:
tuple: (cookies, "Firefox") or (None, None) if no cookies found
"""
# Try Firefox with Microsoft Store support
cookie_file = find_firefox_cookie_file()
if cookie_file:
try:
cookies = browser_cookie3.firefox(
cookie_file=cookie_file,
domain_name='www.myfitnesspal.com'
)
cookie_list = list(cookies)
if cookie_list:
return cookies, "Firefox"
except:
pass
# Try standard Firefox
try:
cookies = browser_cookie3.firefox(domain_name='www.myfitnesspal.com')
cookie_list = list(cookies)
if cookie_list:
return cookies, "Firefox"
except:
pass
return None, None
def wait_for_login():
"""
Poll for cookies after browser login.
Returns:
myfitnesspal.Client: Authenticated client
Raises:
TimeoutError: If login doesn't complete within timeout
"""
import myfitnesspal
start_time = time.time()
poll_interval = 5
cookies_found = False
while True:
elapsed = time.time() - start_time
if elapsed > config.MAX_LOGIN_WAIT_SECONDS:
raise TimeoutError(
f"Timeout waiting for login. Please log in to MyFitnessPal "
f"in your browser and try again."
)
cookies, browser_name = get_working_cookies()
# Only try to authenticate when we FIRST detect cookies
# Don't keep retrying - that triggers rate limiting
if cookies and not cookies_found:
cookies_found = True
print(f"\n\nCookies detected in {browser_name}!")
print("Attempting authentication...")
try:
client = myfitnesspal.Client(cookiejar=cookies)
print("Authentication successful!")
return client
except Exception as e:
print(f"\nAuthentication failed: {str(e)}")
print("\nThis usually means MyFitnessPal is blocking API access.")
print("Possible solutions:")
print(" 1. Wait 30-60 minutes and try again")
print(" 2. Clear MyFitnessPal cookies in your browser and log in fresh")
print(" 3. Try from a different network/IP")
raise
if not cookies:
remaining = int(config.MAX_LOGIN_WAIT_SECONDS - elapsed)
print(f"Waiting for login... ({remaining}s remaining)", end='\r')
time.sleep(poll_interval)
def ensure_logged_in():
"""
Ensure user is logged in to MyFitnessPal via Firefox cookies.
Tries Firefox cookies first. If not found or invalid, opens Firefox
for user to log in, then waits for valid cookies.
Returns:
myfitnesspal.Client: Authenticated client
Raises:
TimeoutError: If user doesn't log in within MAX_LOGIN_WAIT_SECONDS
"""
import myfitnesspal
print("Checking for Firefox cookies...")
cookies, browser_name = get_working_cookies()
if cookies:
print(f"Found cookies in {browser_name}!")
try:
client = myfitnesspal.Client(cookiejar=cookies)
print("Authentication successful!")
return client
except Exception as e:
print(f"\nWarning: Firefox cookies found but authentication failed: {e}")
print("Opening Firefox for fresh login...")
# Fall through to open browser
else:
print("\nNo Firefox cookies found.")
print("Opening Firefox for login...")
# Open Firefox and wait for login
webbrowser.get('firefox').open(config.MFP_LOGIN_URL)
print(f"Please log in to MyFitnessPal in Firefox.")
print(f"Waiting up to {config.MAX_LOGIN_WAIT_SECONDS} seconds...\n")
return wait_for_login()