support customizing the 'User-Agent' value
This commit is contained in:
parent
cbe9e74704
commit
33b8354867
@ -18,6 +18,10 @@ log = logging.getLogger(__name__)
|
|||||||
DEFAULT_MAX_RETRIES = 7
|
DEFAULT_MAX_RETRIES = 7
|
||||||
"""The default maximum number of retries to make when fetching a single activity."""
|
"""The default maximum number of retries to make when fetching a single activity."""
|
||||||
|
|
||||||
|
DEFAULT_USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'
|
||||||
|
"""The default `User-Agent` to use for HTTP requests when none is supplied by
|
||||||
|
the user.
|
||||||
|
"""
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
"""Parse CLI arguments.
|
"""Parse CLI arguments.
|
||||||
@ -59,6 +63,9 @@ def parse_args() -> argparse.Namespace:
|
|||||||
help=("The maximum number of retries to make on failed attempts to fetch an activity. "
|
help=("The maximum number of retries to make on failed attempts to fetch an activity. "
|
||||||
"Exponential backoff will be used, meaning that the delay between successive attempts "
|
"Exponential backoff will be used, meaning that the delay between successive attempts "
|
||||||
"will double with every retry, starting at one second. DEFAULT: {}").format(DEFAULT_MAX_RETRIES))
|
"will double with every retry, starting at one second. DEFAULT: {}").format(DEFAULT_MAX_RETRIES))
|
||||||
|
parser.add_argument(
|
||||||
|
"--user-agent", type=str, default=DEFAULT_USER_AGENT,
|
||||||
|
help="A value to use for the `User-Agent` request header. Use an authentic browser agent string to prevent being blocked by Garmin. A tool such as `user_agent` (`ua`) can be used to generate such values.")
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
@ -70,6 +77,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
incremental_backup(username=args.username,
|
incremental_backup(username=args.username,
|
||||||
password=args.password,
|
password=args.password,
|
||||||
|
user_agent_fn=lambda:DEFAULT_USER_AGENT,
|
||||||
backup_dir=args.backup_dir,
|
backup_dir=args.backup_dir,
|
||||||
export_formats=args.format,
|
export_formats=args.format,
|
||||||
ignore_errors=args.ignore_errors,
|
ignore_errors=args.ignore_errors,
|
||||||
|
@ -81,18 +81,27 @@ class GarminClient(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, username, password):
|
def __init__(self, username, password, user_agent_fn=None):
|
||||||
"""Initialize a :class:`GarminClient` instance.
|
"""Initialize a :class:`GarminClient` instance.
|
||||||
|
|
||||||
:param username: Garmin Connect user name or email address.
|
:param username: Garmin Connect user name or email address.
|
||||||
:type username: str
|
:type username: str
|
||||||
:param password: Garmin Connect account password.
|
:param password: Garmin Connect account password.
|
||||||
:type password: str
|
:type password: str
|
||||||
|
:keyword user_agent_fn: A function that, when called, produces a
|
||||||
|
`User-Agent` string to be used as `User-Agent` for the remainder of the
|
||||||
|
session. If set to None, the default user agent of the http request
|
||||||
|
library is used.
|
||||||
|
:type user_agent_fn: Callable[[], str]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
|
self._user_agent_fn = user_agent_fn
|
||||||
|
|
||||||
self.session = None
|
self.session = None
|
||||||
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.connect()
|
self.connect()
|
||||||
return self
|
return self
|
||||||
@ -118,7 +127,15 @@ class GarminClient(object):
|
|||||||
"embed": "false",
|
"embed": "false",
|
||||||
"_csrf": self._get_csrf_token(),
|
"_csrf": self._get_csrf_token(),
|
||||||
}
|
}
|
||||||
headers = {'origin': 'https://sso.garmin.com'}
|
headers = {
|
||||||
|
'origin': 'https://sso.garmin.com',
|
||||||
|
}
|
||||||
|
if self._user_agent_fn:
|
||||||
|
user_agent = self._user_agent_fn()
|
||||||
|
if not user_agent:
|
||||||
|
raise ValueError("user_agent_fn didn't produce a value")
|
||||||
|
headers['User-Agent'] = user_agent
|
||||||
|
|
||||||
auth_response = self.session.post(
|
auth_response = self.session.post(
|
||||||
SSO_SIGNIN_URL, headers=headers, params=self._auth_params(), data=form_data)
|
SSO_SIGNIN_URL, headers=headers, params=self._auth_params(), data=form_data)
|
||||||
log.debug("got auth response: %s", auth_response.text)
|
log.debug("got auth response: %s", auth_response.text)
|
||||||
|
@ -3,7 +3,7 @@ import getpass
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import List
|
from typing import Callable, List
|
||||||
|
|
||||||
import garminexport.backup
|
import garminexport.backup
|
||||||
from garminexport.backup import supported_export_formats
|
from garminexport.backup import supported_export_formats
|
||||||
@ -15,6 +15,7 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def incremental_backup(username: str,
|
def incremental_backup(username: str,
|
||||||
password: str = None,
|
password: str = None,
|
||||||
|
user_agent_fn: Callable[[],str] = None,
|
||||||
backup_dir: str = os.path.join(".", "activities"),
|
backup_dir: str = os.path.join(".", "activities"),
|
||||||
export_formats: List[str] = None,
|
export_formats: List[str] = None,
|
||||||
ignore_errors: bool = False,
|
ignore_errors: bool = False,
|
||||||
@ -23,6 +24,11 @@ def incremental_backup(username: str,
|
|||||||
|
|
||||||
:param username: Garmin Connect user name
|
:param username: Garmin Connect user name
|
||||||
:param password: Garmin Connect user password. Default: None. If not provided, would be asked interactively.
|
:param password: Garmin Connect user password. Default: None. If not provided, would be asked interactively.
|
||||||
|
:keyword user_agent_fn: A function that, when called, produces a
|
||||||
|
`User-Agent` string to be used as `User-Agent` for the remainder of the
|
||||||
|
session. If set to None, the default user agent of the http request
|
||||||
|
library is used.
|
||||||
|
:type user_agent_fn: Callable[[], str]
|
||||||
:param backup_dir: Destination directory for downloaded activities. Default: ./activities/".
|
:param backup_dir: Destination directory for downloaded activities. Default: ./activities/".
|
||||||
:param export_formats: List of desired output formats (json_summary, json_details, gpx, tcx, fit).
|
:param export_formats: List of desired output formats (json_summary, json_details, gpx, tcx, fit).
|
||||||
Default: `None` which means all supported formats will be backed up.
|
Default: `None` which means all supported formats will be backed up.
|
||||||
@ -34,6 +40,7 @@ def incremental_backup(username: str,
|
|||||||
The activities are stored in a local directory on the user's computer.
|
The activities are stored in a local directory on the user's computer.
|
||||||
The backups are incremental, meaning that only activities that aren't already
|
The backups are incremental, meaning that only activities that aren't already
|
||||||
stored in the backup directory will be downloaded.
|
stored in the backup directory will be downloaded.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# if no --format was specified, all formats are to be backed up
|
# if no --format was specified, all formats are to be backed up
|
||||||
export_formats = export_formats if export_formats else supported_export_formats
|
export_formats = export_formats if export_formats else supported_export_formats
|
||||||
@ -50,7 +57,7 @@ def incremental_backup(username: str,
|
|||||||
delay_strategy=ExponentialBackoffDelayStrategy(initial_delay=timedelta(seconds=1)),
|
delay_strategy=ExponentialBackoffDelayStrategy(initial_delay=timedelta(seconds=1)),
|
||||||
stop_strategy=MaxRetriesStopStrategy(max_retries))
|
stop_strategy=MaxRetriesStopStrategy(max_retries))
|
||||||
|
|
||||||
with GarminClient(username, password) as client:
|
with GarminClient(username, password, user_agent_fn) as client:
|
||||||
# get all activity ids and timestamps from Garmin account
|
# get all activity ids and timestamps from Garmin account
|
||||||
log.info("scanning activities for %s ...", username)
|
log.info("scanning activities for %s ...", username)
|
||||||
activities = set(retryer.call(client.list_activities))
|
activities = set(retryer.call(client.list_activities))
|
||||||
|
Loading…
Reference in New Issue
Block a user