import base64 import logging import re from collections import OrderedDict from requests import Session # Modified code based on DashLt's spoofbuz logger = logging.getLogger(__name__) _SEED_TIMEZONE_REGEX = re.compile( r'[a-z]\.initialSeed\("(?P[\w=]+)",window\.utimezone\.(?P[a-z]+)\)' ) _INFO_EXTRAS_REGEX = r'name:"\w+/(?P{timezones})",info:"(?P[\w=]+)",extras:"(?P[\w=]+)"' _APP_ID_REGEX = re.compile( r'production:{api:{appId:"(?P\d{9})",appSecret:"\w{32}"' ) _BUNDLE_URL_REGEX = re.compile( r'' ) _BASE_URL = "https://play.qobuz.com" _BUNDLE_URL_REGEX = re.compile( r'' ) class Bundle: def __init__(self): self._session = Session() logger.debug("Getting logging page") response = self._session.get(f"{_BASE_URL}/login") response.raise_for_status() bundle_url_match = _BUNDLE_URL_REGEX.search(response.text) if not bundle_url_match: raise NotImplementedError("Bundle URL found") bundle_url = bundle_url_match.group(1) logger.debug("Getting bundle") response = self._session.get(_BASE_URL + bundle_url) response.raise_for_status() self._bundle = response.text def get_app_id(self): match = _APP_ID_REGEX.search(self._bundle) if not match: raise NotImplementedError("Failed to match APP ID") return match.group("app_id") def get_secrets(self): logger.debug("Getting secrets") seed_matches = _SEED_TIMEZONE_REGEX.finditer(self._bundle) secrets = OrderedDict() for match in seed_matches: seed, timezone = match.group("seed", "timezone") secrets[timezone] = [seed] keypairs = list(secrets.items()) secrets.move_to_end(keypairs[1][0], last=False) info_extras_regex = _INFO_EXTRAS_REGEX.format( timezones="|".join([timezone.capitalize() for timezone in secrets]) ) info_extras_matches = re.finditer(info_extras_regex, self._bundle) for match in info_extras_matches: timezone, info, extras = match.group("timezone", "info", "extras") secrets[timezone.lower()] += [info, extras] for secret_pair in secrets: secrets[secret_pair] = base64.standard_b64decode( "".join(secrets[secret_pair])[:-44] ).decode("utf-8") return secrets