Compare commits

..

36 commits
1.0 ... main

Author SHA1 Message Date
4da95d0a7d
build: Updated version to 1.4.1 2025-03-24 13:25:33 -05:00
c914b920d7
fix: Send notification on success 2025-03-24 13:23:08 -05:00
454db3c28a
fix: Unnecessary error 2025-03-24 13:17:42 -05:00
3401d50aa6
docs: Change recommendation to uv 2025-03-20 17:33:15 -05:00
b0cedf0486
build: Switch to semver 2025-03-20 17:29:56 -05:00
5479faed46
build: Added proper license tag 2025-03-20 03:48:30 -05:00
e4694801a5
build: Removed setuptools 2025-03-20 03:43:18 -05:00
2da875f2fd
build: Added a lockfile and created new tagged version 2025-03-20 03:25:13 -05:00
ee3eef71aa
fix: Warnings and errors reported by ruff 2025-03-19 18:08:10 -05:00
ad125bfb7b
fix: Work with the new API 2024-11-13 23:49:11 -06:00
64ab240a8a
fix: Need a space for the token to work 2024-11-06 21:52:06 -06:00
8a3e6fa35f
new: Support access tokens for ntfy 2024-11-06 21:45:19 -06:00
Sayantan Santra
c38881922d
Merge pull request #3 from HereticWay/fix-check-if-apk-in-present-vers
Fix: Check if apk already exists in present_vers before trying to access it
2024-04-27 08:56:28 -05:00
HereticWay
25fc3e4310 fix: Check if apk already exists in present_vers before trying to access it 2024-04-27 15:21:33 +02:00
8b70217909
docs: Minor changes to README 2024-04-18 23:57:14 -05:00
bbfb630102
build: Bumped version to 1.3 2024-04-10 23:10:14 -05:00
9195565385
fix: Work with the changed download links for GmsCore, fixes #2 2024-04-10 22:45:41 -05:00
e2234078d9
chg: Changed the email on headers 2024-04-03 20:33:01 -05:00
2934f8b0f7
fix: Work around packaging.version changing the version string slightly 2024-04-01 17:52:51 -05:00
f4cd430e9d
fix: Some other places still need requests 2024-04-01 16:59:28 -05:00
6cbe6454a3
chg: Use cloudscraper to get around cloudflare blocks 2024-04-01 16:52:54 -05:00
336b6470ca
chg: Switch to ReVanced/GmsCore for microG 2024-03-28 16:10:41 -05:00
Sayantan Santra
2b3d7d6281
build: Update version number 2024-03-03 22:11:54 -06:00
e3a869cb7e
chg: Use the apk name while printing out version info 2024-03-03 02:30:08 -06:00
64e59fef33
fix: Add next() to get the first entry from the filter 2024-03-02 19:09:20 -06:00
03d07c13b1
fix: Pulling the signature files instead of the executables 2024-03-02 19:01:33 -06:00
24bc63e1a2
fix: Do not check for apk updates with checkonly flag 2024-03-02 18:53:49 -06:00
1973a08642
fix: Use correct variable name 2024-01-14 09:15:07 -06:00
1804932e50
fix: Update URL for APKPure 2024-01-14 08:57:23 -06:00
a0ef6a5566
fix: Do not check online when required version is already on disk 2024-01-14 08:53:07 -06:00
941c635c30
chg: Print required version once chosen 2023-12-16 13:54:31 +05:30
b66ef99227
chg: Updated user agent 2023-12-16 13:04:59 +05:30
c981c4be8f
docs: Fix typo 2023-12-05 02:20:19 -06:00
76c3dc7c3b
fix: Provide keystore alias 2023-11-25 19:43:32 -06:00
3858faf970
fix: NoneType error 2023-10-17 23:51:20 -05:00
8e126d4d45
fix: Check required version 2023-10-17 23:46:00 -05:00
10 changed files with 843 additions and 281 deletions

View file

@ -1,12 +1,16 @@
# Revanced Builder # Revanced Builder
This repo will allow one to build [ReVanced](https://github.com/revanced/) apps automatically, send notifications (and possibly share the builds with friends). It uses [Gotify](https://gotify.net), [ntfy.sh](https://ntfy.sh) or [telegram.sh](https://github.com/fabianonline/telegram.sh) to send messages. Make sure that `Java >=17` is installed and selected as default. This repo will allow one to build [ReVanced](https://github.com/revanced/) apps
automatically, send notifications (and possibly share the builds with friends).
It uses [Gotify](https://gotify.net), [ntfy.sh](https://ntfy.sh) or
[telegram.sh](https://github.com/fabianonline/telegram.sh) to send messages.
Make sure that `Java >=17` is installed and selected as default.
## Installation ## Installation
Recommended way is to use [`pipx`](https://github.com/pypa/pipx) to install the program. Recommended way is to use [`uv`](https://github.com/astral-sh/uv) to install the program.
``` ```
pipx install git+https://github.com/SinTan1729/ReVancedBuilder uv tool install git+https://github.com/SinTan1729/ReVancedBuilder
``` ```
And then you can update/reinstall the program using `pipx reinstall ReVancedBuilder`. And then you can update/reinstall the program using `uv tool update ReVancedBuilder`.
## How to use ## How to use
Just run `ReVancedBuilder <working-directory> (force/experimental/checkonly/buildonly)`. Just run `ReVancedBuilder <working-directory> (force/experimental/checkonly/buildonly)`.
@ -50,12 +54,28 @@ It might be a good idea to set it up to run periodically. There are a few ways o
``` ```
## Notes ## Notes
- If you installed it using `pipx`, you can figure out the full location of the program by running `which ReVancedBuilder`. - If you installed it using `uv`, you can figure out the full location of the
- This app needs some config files to run. Download all the config files inside `exampl_configs` directory, namely `build_config`, `chosen_patches` (optional), and `notification_config` (optional, needed only if you want to send notifications) and move them to your working directory. Then, you should modify these files to your liking. program by running `which ReVancedBuilder`.
- The script will download the **automatically selected compatible version** (unless version is specified in `build_config`) (using compatibility of patches as listed [here](https://revanced.app/patches)) of Youtube on APKPure, **NOT** latest official version on Google Play. - This app needs some config files to run. Download all the config files inside
- **Under no circumstances** will any APKs be uploaded to this repository as that might attract legal problems. `example_configs` directory, namely `build_config`, `chosen_patches`
- If you enable telegram notifications, make sure to fill up the config options inside the `build_config` file. For more information about the config, take at look at the repos of `telegram.sh` and `telegram-upload` provided above. (optional), and `notification_config` (optional, needed only if you want to
- It can also run a post script (if exists), specified in the `build_config` file. The `timestamp` is passed as `$1`. send notifications) and move them to your working directory. Then, you should
- In the current configuration, the script only builds YouTube ReVanced and YouTube Music ReVanced (both nonroot), but it's easy to add support for any other ReVanced app using the `build_config` file. The config files are self-explanatory. modify these files to your liking.
- All the packages are pulled from [APKPure](https://apkpure.com) and GitHub (the [`revanced/*`](https://github.com/revanced) repos). - The script will download the **automatically selected compatible version**,
using compatibility of patches as listed [here](https://revanced.app/patches)
of Youtube on APKPure, **NOT** latest official version on Google Play (unless
version is specified in `build_config`).
- **Under no circumstances** will any APKs be uploaded to this repository as
that might attract legal problems.
- If you enable telegram notifications, make sure to fill up the config options
inside the `build_config` file. For more information about the config, take at
look at the repos of `telegram.sh` and `telegram-upload` provided above.
- It can also run a post script (if exists), specified in the `build_config`
file. The `timestamp` is passed as `$1`.
- In the current configuration, the script only builds YouTube ReVanced and
YouTube Music ReVanced (both nonroot), but it's easy to add support for any
other ReVanced app using the `build_config` file. The config files are
self-explanatory.
- All the packages are pulled from [APKPure](https://apkpure.com) and GitHub
(the [`revanced/*`](https://github.com/revanced) repos).

View file

@ -50,5 +50,9 @@ root = true
output_name = YouTube_Music_ReVanced_root output_name = YouTube_Music_ReVanced_root
keystore = revanced-ytm-root.keystore keystore = revanced-ytm-root.keystore
[gmscore]
# If you use a Xiaomi or Huawei device, change the variant to alt
variant=regular
[post_script] [post_script]
# file = ./post_script.sh # file = ./post_script.sh

View file

@ -23,3 +23,5 @@
enabled = false enabled = false
url = url url = url
topic = topic topic = topic
token = token

View file

@ -1,18 +1,14 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project] [project]
name = "ReVancedBuilder" name = "ReVancedBuilder"
authors = [{ name = "Sayantan Santra", email = "sayantan.santra689@gmail.com" }] authors = [{ name = "Sayantan Santra", email = "sayantan.santra689@gmail.com" }]
description = "A tool to automatically build latest releases of ReVanced apps" description = "A tool to automatically build latest releases of ReVanced apps"
license = "GPL-3.0-only"
readme = "README.md" readme = "README.md"
requires-python = ">=3.6" requires-python = ">=3.6"
keywords = ["revanced", "patch"] keywords = ["revanced", "patch"]
license = { file = "LICENSE" }
classifiers = ["Programming Language :: Python :: 3"] classifiers = ["Programming Language :: Python :: 3"]
dependencies = ["requests", "packaging", "bs4"] dependencies = ["cloudscraper", "requests", "packaging", "bs4"]
version = "1.0" version = "1.4.1"
[project.scripts] [project.scripts]
ReVancedBuilder = "ReVancedBuilder:ReVancedBuilder" ReVancedBuilder = "ReVancedBuilder:ReVancedBuilder"

157
src/ReVancedBuilder/APKPure_dl.py Normal file → Executable file
View file

@ -1,64 +1,66 @@
#!/usr/bin/env python3\ #!/usr/bin/env python3\
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu> # SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra689@gmail.com>
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
import os import os
import sys
import json
from packaging.version import Version import cloudscraper as scraper
import requests as req
from bs4 import BeautifulSoup as bs from bs4 import BeautifulSoup as bs
from packaging.version import Version
from ReVancedBuilder.Cleanup import err_exit from ReVancedBuilder.Cleanup import err_exit
# Determine the best version available to download # Determine the best version available to download
def apkpure_best_match(version, soup): def apkpure_best_match(version, soup, appstate, apk):
try: try:
vers_list = [Version(x['data-dt-version']) vers_list_str = [
for x in soup.css.select(f"a[data-dt-apkid^=\"b/APK/\"]")] x["data-dt-version"] for x in soup.css.select('a[data-dt-apkid^="b/APK/"]')
except: ]
err_exit( except Exception as ex:
f" There was some error getting list of versions of {apk}...", appstate) err_exit(f" There was some error getting list of versions of {apk}: {ex}", appstate)
if version != '0': vers_list = map(lambda x: Version(x), vers_list_str)
if version != "0":
vers_list = filter(lambda x: x <= Version(version), vers_list) vers_list = filter(lambda x: x <= Version(version), vers_list)
return str(max(vers_list)) max_ver = max(vers_list)
return next(filter(lambda x: Version(x) == max_ver, vers_list_str))
# Download an apk from APKPure.com
def apkpure_dl(apk, appname, version, hard_version, session, present_vers, flag): # Download an apk from apkpure.net
def apkpure_dl(apk, appname, version, hard_version, session, present_vers, flag, appstate):
res = session.get(f"https://apkpure.com/{appname}/{apk}/versions") res = session.get(f"https://apkpure.com/{appname}/{apk}/versions")
res.raise_for_status() res.raise_for_status()
soup = bs(res.text, 'html.parser') soup = bs(res.text, "html.parser")
try: try:
if present_vers[apk] == version and flag != 'force' and os.path.isfile(apk+'.apk'): if present_vers[apk] == version and flag != "force" and os.path.isfile(apk + ".apk"):
print( print(f"Recommended version {version} of {apk} is already present.")
f"Recommended version {version} of {apk} is already present.")
return return
except KeyError: except KeyError:
pass pass
if not hard_version: if not hard_version:
apkpure_version = apkpure_best_match(version, soup) apkpure_version = apkpure_best_match(version, soup, appstate, apk)
if version not in [apkpure_version, '0']: if version not in [apkpure_version, "0"]:
print(f"Required version {version} not found in APKPure, choosing version {apkpure_version} instead.") print(
version=apkpure_version f"Required version {version} not found in APKPure, choosing version {apkpure_version} instead."
)
version = apkpure_version
try: try:
if present_vers[apk] == version and flag != 'force' and os.path.isfile(apk+'.apk'): if present_vers[apk] == version and flag != "force" and os.path.isfile(apk + ".apk"):
print( print(f"Recommended version {version} of {apk} is already present.")
f"Recommended version {version} of {apk} is already present.")
return return
except KeyError: except KeyError:
pass pass
if flag == 'checkonly' and present_vers[apk] != version: if flag == "checkonly" and present_vers[apk] != version:
print(f"{apk} has an update ({present_vers[apk]} -> {version})") print(f"{apk} has an update ({present_vers[apk]} -> {version})")
return return
@ -66,81 +68,88 @@ def apkpure_dl(apk, appname, version, hard_version, session, present_vers, flag)
# Get the version code # Get the version code
try: try:
ver_code=soup.css.select( ver_code = soup.css.select(f'a[data-dt-version="{version}"][data-dt-apkid^="b/APK/"]')[0][
f"a[data-dt-version=\"{version}\"][data-dt-apkid^=\"b/APK/\"]")[0]['data-dt-versioncode'] "data-dt-versioncode"
except: ]
err_exit( except Exception as ex:
f" There was some error while downloading {apk}...", appstate) err_exit(f" There was some error while downloading {apk}: {ex}", appstate)
res=session.get( res = session.get(f"https://d.apkpure.com/b/APK/{apk}?versionCode={ver_code}", stream=True)
f"https://d.apkpure.com/b/APK/{apk}?versionCode={ver_code}", stream=True)
res.raise_for_status() res.raise_for_status()
with open(apk+'.apk', 'wb') as f: with open(apk + ".apk", "wb") as f:
for chunk in res.iter_content(chunk_size=8192): for chunk in res.iter_content(chunk_size=8192):
f.write(chunk) f.write(chunk)
print(" Done!") print(" Done!")
# Download apk files, if needed # Download apk files, if needed
def get_apks(appstate): def get_apks(appstate):
present_vers=appstate['present_vers'] present_vers = appstate["present_vers"]
build_config=appstate['build_config'] build_config = appstate["build_config"]
flag=appstate['flag'] flag = appstate["flag"]
print('Downloading required apk files from APKPure...') print("Downloading required apk files from APKPure...")
# Create a cloudscraper session
session = scraper.create_scraper()
# Get latest patches using the ReVanced API # Get latest patches using the ReVanced API
try: try:
# Get the first result # Get the first result
patches_json=next(filter( patches = session.get("https://api.revanced.app/v4/patches/list").json()
lambda x: x['repository'] == 'revanced/revanced-patches', appstate['tools'])) except session.exceptions.RequestException as e:
patches=req.get(patches_json['browser_download_url']).json()
except req.exceptions.RequestException as e:
err_exit(f"Error fetching patches, {e}", appstate) err_exit(f"Error fetching patches, {e}", appstate)
session=req.Session()
session.headers.update(
{'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0'})
for app in build_config: for app in build_config:
# Check if we need to build an app # Check if we need to build an app
if not build_config[app].getboolean('build'): if not build_config[app].getboolean("build"):
continue continue
try: try:
apk=build_config[app]['apk'] apk = build_config[app]["apk"]
pretty_name=build_config[app]['pretty_name'] pretty_name = build_config[app]["pretty_name"]
apkpure_appname=build_config[app]['apkpure_appname'] apkpure_appname = build_config[app]["apkpure_appname"]
except: except Exception as ex:
err_exit(f"Invalid config for {app} in build_config!", appstate) err_exit(f"Invalid config for {app} in build_config!: {ex}", appstate)
print(f"Checking {pretty_name}...") print(f"Checking {pretty_name}...")
try: try:
required_ver=build_config[app]['version'] required_ver = build_config[app]["version"]
required_ver[0] hard_version = True
hard_version=True print(f"Using version {required_ver} of {apk} from build_config.")
print(f"Using version {required_ver} of {app} from build_config.") except Exception as ex:
except: print(f"Dealing with exception: {ex}")
hard_version=False hard_version = False
compatible_vers=[] compatible_vers = []
for patch in patches: for patch in patches:
for pkg in patch['compatiblePackages']: try:
if pkg['name'] == apk: compatible_vers.append(patch["compatiblePackages"][apk][-1])
try: except (KeyError, TypeError):
compatible_vers.append(pkg['versions'][-1]) pass
except IndexError:
pass
if not compatible_vers: if not compatible_vers:
required_ver=Version('0') required_ver = Version("0")
else: else:
required_ver=min(map(lambda x: Version(x), compatible_vers)) required_ver = min(map(lambda x: Version(x), compatible_vers))
required_ver = next(filter(lambda x: Version(x) == required_ver, compatible_vers))
apkpure_dl(apk, apkpure_appname, str(required_ver), print(f"Chosen required version of {apk} is {required_ver}.")
hard_version, session, present_vers, flag)
present_vers.update({apk: str(required_ver)}) if apk in appstate["present_vers"] and appstate["present_vers"][apk] == required_ver:
print("It's already present on disk, so skipping download.")
else:
apkpure_dl(
apk,
apkpure_appname,
required_ver,
hard_version,
session,
present_vers,
flag,
appstate,
)
appstate['present_vers']=present_vers present_vers.update({apk: required_ver})
appstate["present_vers"] = present_vers
return appstate return appstate

30
src/ReVancedBuilder/Cleanup.py Normal file → Executable file
View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu> # SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra689@gmail.com>
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
import os import os
@ -13,28 +13,28 @@ from ReVancedBuilder.Notifications import send_notif
def move_apps(appstate): def move_apps(appstate):
build_config = appstate['build_config'] build_config = appstate["build_config"]
print = appstate['logger'].info print = appstate["logger"].info
try: try:
os.mkdir('archive') os.mkdir("archive")
except FileExistsError: except FileExistsError:
pass pass
for app in build_config: for app in build_config:
if not build_config[app].getboolean('build'): if not build_config[app].getboolean("build"):
continue continue
name = build_config[app]['output_name'] name = build_config[app]["output_name"]
final_name = f"{name}_{appstate['timestamp']}.apk" final_name = f"{name}_{appstate['timestamp']}.apk"
try: try:
os.rename(name+'.apk', 'archive/'+final_name) os.rename(name + ".apk", "archive/" + final_name)
except FileNotFoundError: except FileNotFoundError:
pass pass
# sys.exit('There was an error moving the final apk files!') # sys.exit('There was an error moving the final apk files!')
# Do some cleanup, keep only the last 3 build's worth of files and a week worth of logs # Do some cleanup, keep only the last 3 build's worth of files and a week worth of logs
with os.scandir('archive') as dir: with os.scandir("archive") as dir:
files = [] files = []
for f in dir: for f in dir:
if name in f.name: if name in f.name:
@ -43,10 +43,10 @@ def move_apps(appstate):
files.reverse() files.reverse()
for f in files[3:]: for f in files[3:]:
os.remove(f) os.remove(f)
print('Deleted old build '+f.name) print("Deleted old build " + f.name)
# Delete logs older than 7 days # Delete logs older than 7 days
with os.scandir('logs') as dir: with os.scandir("logs") as dir:
now = time.time() now = time.time()
for f in dir: for f in dir:
if f.stat().st_ctime < now - 7 * 86400: if f.stat().st_ctime < now - 7 * 86400:
@ -54,18 +54,18 @@ def move_apps(appstate):
def err_exit(msg, appstate, code=1): def err_exit(msg, appstate, code=1):
print = appstate['logger'].info print = appstate["logger"].info
try: try:
appstate['notification_config'] appstate["notification_config"]
if appstate['flag'] != 'checkonly': if appstate["flag"] != "checkonly":
send_notif(appstate, error=True) send_notif(appstate, error=True)
except: except KeyError:
pass pass
if msg: if msg:
print(f"ERROR: {msg}") print(f"ERROR: {msg}")
# Delete the lockfile # Delete the lockfile
os.remove('lockfile') os.remove("lockfile")
sys.exit(code) sys.exit(code)

64
src/ReVancedBuilder/JAVABuilder.py Normal file → Executable file
View file

@ -1,12 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu> # SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra689@gmail.com>
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
import os
import sys
import configparser as cp import configparser as cp
import json import json
import os
import subprocess import subprocess
from ReVancedBuilder.Cleanup import err_exit from ReVancedBuilder.Cleanup import err_exit
@ -15,59 +14,58 @@ from ReVancedBuilder.Cleanup import err_exit
def build_apps(appstate): def build_apps(appstate):
build_config = appstate['build_config'] build_config = appstate["build_config"]
flag = appstate['flag'] flag = appstate["flag"]
print = appstate['logger'].info print = appstate["logger"].info
chosen_patches = cp.ConfigParser() chosen_patches = cp.ConfigParser()
chosen_patches.read('chosen_patches') chosen_patches.read("chosen_patches")
try: try:
included_patches = json.loads(chosen_patches['patches']['included']) included_patches = json.loads(chosen_patches["patches"]["included"])
except: except (KeyError, ValueError):
included_patches = [] included_patches = []
try: try:
excluded_patches = json.loads(chosen_patches['patches']['excluded']) excluded_patches = json.loads(chosen_patches["patches"]["excluded"])
except Exception as e: except (KeyError, ValueError):
excluded_patches = [] excluded_patches = []
for app in build_config: for app in build_config:
# Check if we need to build an app # Check if we need to build an app
if not build_config[app].getboolean('build'): if not build_config[app].getboolean("build"):
continue continue
# Build the command to be run # Build the command to be run
cmd = 'java -jar revanced-cli.jar patch -b revanced-patches.jar -m revanced-integrations.apk' cmd = "java -jar revanced-cli.jar patch -p revanced-patches.rvp"
try: try:
root = build_config[app].getboolean('root') root = build_config[app].getboolean("root")
except: except cp.Error:
root = False root = False
if root: if root:
cmd += ' --mount -e microg-support' cmd += ' --mount -e "GmsCore support"'
for item in included_patches: for item in included_patches:
cmd += f" -i {item}" cmd += f" -i {item}"
for item in excluded_patches: for item in excluded_patches:
cmd += f" -e {item}" cmd += f" -e {item}"
if flag == 'experimental': if flag == "experimental":
cmd += ' --experimental' cmd += " --experimental"
try: try:
keystore = build_config[app]['keystore'] keystore = build_config[app]["keystore"]
if not root: if not root:
cmd += f" --keystore {keystore}" cmd += f" --keystore {keystore} --keystore-entry-alias=alias --keystore-entry-password=ReVanced --keystore-password=ReVanced"
except: except KeyError:
pass pass
try: try:
apk = build_config[app]['apk'] apk = build_config[app]["apk"]
pretty_name = build_config[app]['pretty_name'] pretty_name = build_config[app]["pretty_name"]
apkpure_appname = build_config[app]['apkpure_appname'] output_name = build_config[app]["output_name"]
output_name = build_config[app]['output_name'] except KeyError:
except:
err_exit(f"Invalid config for {app} in build_config!", appstate) err_exit(f"Invalid config for {app} in build_config!", appstate)
cmd += f" -o {output_name}.apk {apk}.apk" cmd += f" -o {output_name}.apk {apk}.apk"
@ -78,17 +76,17 @@ def build_apps(appstate):
print(f"Building {pretty_name} (nonroot) using '{cmd}'") print(f"Building {pretty_name} (nonroot) using '{cmd}'")
try: try:
with subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout as output: with subprocess.Popen(
cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
).stdout as output:
for line in output: for line in output:
line_utf = line.decode('utf-8').strip('\n') line_utf = line.decode("utf-8").strip("\n")
if line_utf: if line_utf:
print(line_utf) print(line_utf)
except Exception as e: except Exception as e:
err_exit( err_exit(f"There was an error while building {pretty_name}!\n{e}", appstate)
f"There was an error while building {pretty_name}!\n{e}", appstate)
try: try:
os.rename(output_name+'.apk', output_name+'.apk') os.rename(output_name + ".apk", output_name + ".apk")
except FileNotFoundError: except FileNotFoundError:
err_exit( err_exit(f"There was an error while building {pretty_name}!", appstate)
f"There was an error while building {pretty_name}!", appstate)

112
src/ReVancedBuilder/Notifications.py Normal file → Executable file
View file

@ -1,97 +1,103 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu> # SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra689@gmail.com>
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
import json import json
import re import re
import requests as req
import subprocess import subprocess
import requests as req
def send_notif(appstate, error=False): def send_notif(appstate, error=False):
print = appstate['logger'].info print = appstate["logger"].info
timestamp = appstate['timestamp'] timestamp = appstate["timestamp"]
if error: if error:
msg = f"There was an error during build! Please check the logs.\nTimestamp: {timestamp}" msg = f"There was an error during build! Please check the logs.\nTimestamp: {timestamp}"
else: else:
notification_config = appstate['notification_config'] build_config = appstate["build_config"]
build_config = appstate['build_config'] present_vers = appstate["present_vers"]
present_vers = appstate['present_vers']
flag = appstate['flag']
msg = json.dumps(present_vers, indent=0) msg = json.dumps(present_vers, indent=0)
msg = re.sub('("|\{|\}|,)', '', msg).strip('\n') msg = re.sub('("|\{|\}|,)', "", msg).strip("\n")
msg = msg.replace('revanced-', 'ReVanced ') msg = msg.replace("revanced-", "ReVanced ")
msg = msg.replace('cli', 'CLI') msg = msg.replace("cli", "CLI")
msg = msg.replace('integrations', 'Integrations') msg = msg.replace("integrations", "Integrations")
msg = msg.replace('patches', 'Patches') msg = msg.replace("patches", "Patches")
msg = msg.replace('VancedMicroG', 'Vanced microG')
for app in build_config: for app in build_config:
if not build_config[app].getboolean('build'): if not build_config[app].getboolean("build"):
continue continue
msg = msg.replace( msg = msg.replace(build_config[app]["apk"], build_config[app]["pretty_name"])
build_config[app]['apk'], build_config[app]['pretty_name'])
msg += '\nTimestamp: ' + timestamp msg += "\nTimestamp: " + timestamp
if appstate['microg_updated']: if appstate["gmscore_updated"]:
msg += '\nVanced microG was updated.' msg += "\nGmsCore was updated."
config = appstate['notification_config'] config = appstate["notification_config"]
for entry in config: for entry in config:
if not config[entry].getboolean('enabled'): if not config[entry].getboolean("enabled"):
continue continue
encoded_title = '⚙⚙⚙ ReVanced Build ⚙⚙⚙'.encode('utf-8') encoded_title = "⚙⚙⚙ ReVanced Build ⚙⚙⚙".encode("utf-8")
if entry == 'ntfy': if entry == "ntfy":
print('Sending notification through ntfy.sh...') print("Sending notification through ntfy.sh...")
try: try:
url = config[entry]['url'] url = config[entry]["url"]
topic = config[entry]['topic'] topic = config[entry]["topic"]
except: except KeyError:
print('URL or TOPIC not provided!') print("URL or TOPIC not provided!")
continue
headers = {
"Icon": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Revanced-logo-round.svg/240px-Revanced-logo-round.svg.png",
"Title": encoded_title,
}
try:
token = config[entry]["token"]
headers["Authorization"] = "Bearer " + token
except KeyError:
continue continue
headers = {'Icon': 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Revanced-logo-round.svg/240px-Revanced-logo-round.svg.png',
'Title': encoded_title}
try: try:
req.post(f"{url}/{topic}", msg, headers=headers) req.post(f"{url}/{topic}", msg, headers=headers)
except Exception as e: except Exception as ex:
print('Failed!' + str(e)) print(f"Failed with exception: {ex}")
elif entry == 'gotify': elif entry == "gotify":
print('Sending notification through Gotify...') print("Sending notification through Gotify...")
try: try:
url = config[entry]['url'] url = config[entry]["url"]
token = config[entry]['token'] token = config[entry]["token"]
except: except KeyError:
print('URL or TOKEN not provided!') print("URL or TOKEN not provided!")
continue continue
data = {'Title': encoded_title, 'message': msg, 'priority': '5'} data = {"Title": encoded_title, "message": msg, "priority": "5"}
try: try:
req.post(f"{url}/message?token={token}", data) req.post(f"{url}/message?token={token}", data)
except Exception as e: except Exception as e:
print('Failed!' + str(e)) print("Failed!" + str(e))
elif entry == 'telegram': elif entry == "telegram":
print('Sending notification through Telegram...') print("Sending notification through Telegram...")
try: try:
chat = config[entry]['chat'] chat = config[entry]["chat"]
token = config[entry]['token'] token = config[entry]["token"]
except: except KeyError:
print('CHAT or TOKEN not provided!') print("CHAT or TOKEN not provided!")
continue continue
cmd = f"./telegram.sh -t {token} -c {chat} -T {encoded_title} -M \"{msg}\"" cmd = f'./telegram.sh -t {token} -c {chat} -T {encoded_title} -M "{msg}"'
try: try:
with subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout as output: with subprocess.Popen(
cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
).stdout as output:
for line in output: for line in output:
line_utf = line.decode('utf-8').strip('\n') line_utf = line.decode("utf-8").strip("\n")
if line_utf: if line_utf:
print(line_utf) print(line_utf)
except Exception as e: except Exception as ex:
err_exit(f"Failed!\n{e}", appstate) print(f"Failed to send notification with exception: {ex}")
else: else:
print('Don\'t know how to send notifications to ' + entry) print("Don't know how to send notifications to " + entry)

View file

@ -1,83 +1,107 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu> # SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra689@gmail.com>
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
import sys
import os
import configparser as cp import configparser as cp
import json import json
import logging import logging
import os
import subprocess import subprocess
import sys
from datetime import datetime
import requests as req import requests as req
from packaging.version import Version from packaging.version import Version
from datetime import datetime
from ReVancedBuilder.APKPure_dl import apkpure_best_match, apkpure_dl, get_apks from ReVancedBuilder.APKPure_dl import get_apks
from ReVancedBuilder.Cleanup import err_exit, move_apps, send_notif
from ReVancedBuilder.JAVABuilder import build_apps from ReVancedBuilder.JAVABuilder import build_apps
from ReVancedBuilder.Notifications import send_notif
from ReVancedBuilder.Cleanup import move_apps, err_exit
# Update the ReVanced tools, if needed # Update the ReVanced tools, if needed
def update_tools(appstate): def update_tools(appstate):
for item in ['revanced-cli', 'revanced-integrations', 'revanced-patches']: for item in ["revanced-cli", "revanced-patches"]:
print(f"Checking updates for {item}...") print(f"Checking updates for {item}...")
tools = appstate['tools'] tools = appstate["tools"]
*_, tool = filter(lambda x: x['repository'] == 'revanced/'+item, tools) # Get the last result tool = next(
latest_ver = Version(tool['version']) filter(
lambda x: x["repository"] == "revanced/" + item
and x["content_type"] not in ["application/pgp-keys", "application/json"],
tools,
)
)
latest_ver = Version(tool["version"])
try: try:
present_ver = Version(appstate['present_vers'][item]) present_ver = Version(appstate["present_vers"][item])
except KeyError: except KeyError:
present_ver = Version('0') present_ver = Version("0")
output_file = item+os.path.splitext(tool['name'])[1] output_file = item + os.path.splitext(tool["name"])[1]
if flag == 'force' or not os.path.isfile(output_file) or present_ver < latest_ver: if flag == "force" or not os.path.isfile(output_file) or present_ver < latest_ver:
appstate['up-to-date'] = False appstate["up-to-date"] = False
print(f"{item} has an update ({str(present_ver)} -> {str(latest_ver)})") print(f"{item} has an update ({str(present_ver)} -> {str(latest_ver)})")
if flag != 'checkonly': if flag != "checkonly":
print(f"Downloading {output_file}...") print(f"Downloading {output_file}...")
res = req.get(tool['browser_download_url'], stream=True) res = req.get(tool["browser_download_url"], stream=True)
res.raise_for_status() res.raise_for_status()
with open(output_file, 'wb') as f: with open(output_file, "wb") as f:
for chunk in res.iter_content(chunk_size=8192): for chunk in res.iter_content(chunk_size=8192):
f.write(chunk) f.write(chunk)
appstate['present_vers'].update({item: str(latest_ver)}) appstate["present_vers"].update({item: str(latest_ver)})
print("Done!") print("Done!")
return appstate return appstate
# Update microG, if needed
def update_microg(appstate): # Update GmsCore, if needed
print('Checking updates for Vanced microG...') def update_gmscore(appstate):
print("Checking updates for GmsCore...")
# Pull the latest information using the ReVanced API
try: try:
data = req.get('https://api.github.com/repos/inotia00/VancedMicroG/releases/latest').json()['tag_name'] data = req.get("https://api.revanced.app/v2/gmscore/releases/latest").json()["release"]
latest_ver = Version(data)
except req.exceptions.RequestException as e: except req.exceptions.RequestException as e:
err_exit(f"Error fetching info about Vanced microG, {e}", appstate) err_exit(f"Error fetching GmsCore information, {e}", appstate)
latest_ver = Version(data["metadata"]["tag_name"])
try: try:
present_ver = Version(appstate['present_vers']['VancedMicroG']) present_ver = Version(appstate["present_vers"]["GmsCore"])
except KeyError: except KeyError:
present_ver = Version('0') present_ver = Version("0")
if flag == 'force' or not os.path.isfile('microg.apk') or present_ver < latest_ver: try:
appstate['up-to-date'] = False variant = appstate["build_config"]["gmscore"]["variant"]
print(f"Vanced microG has an update ({str(present_ver)} -> {str(latest_ver)})") except KeyError:
if flag != 'checkonly': variant = "regular"
print(f"Downloading vanced-microg.apk...")
res = req.get('https://github.com/inotia00/VancedMicroG/releases/latest/download/microg.apk', stream=True) if variant == "alt":
res.raise_for_status() gmscore_link = next(filter(lambda x: "-hw-" in x["name"], data["assets"]))[
with open('microg.apk', 'wb') as f: "browser_download_url"
for chunk in res.iter_content(chunk_size=8192): ]
f.write(chunk) else:
appstate['present_vers'].update({'VancedMicroG': str(latest_ver)}) gmscore_link = next(filter(lambda x: "-hw-" not in x["name"], data["assets"]))[
print("Done!") "browser_download_url"
appstate['microg_updated'] = True ]
if flag == "force" or not os.path.isfile("GmsCore.apk") or present_ver < latest_ver:
appstate["up-to-date"] = False
print(f"GmsCore has an update ({str(present_ver)} -> {str(latest_ver)})")
if flag != "checkonly":
print("Downloading GmsCore...")
res = req.get(gmscore_link, stream=True)
res.raise_for_status()
with open("GmsCore.apk", "wb") as f:
for chunk in res.iter_content(chunk_size=8192):
f.write(chunk)
appstate["present_vers"].update({"GmsCore": str(latest_ver)})
print("Done!")
appstate["gmscore_updated"] = True
return appstate return appstate
# ------------------------------ # ------------------------------
# The main function starts here # The main function starts here
# ------------------------------ # ------------------------------
@ -87,111 +111,116 @@ appstate = {}
# Get a timestamp # Get a timestamp
time = datetime.now() time = datetime.now()
appstate['timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ') appstate["timestamp"] = time.strftime("%Y-%m-%dT%H:%M:%SZ")
# Read arguments # Read arguments
try: try:
os.chdir(sys.argv[1]) os.chdir(sys.argv[1])
except IndexError: except IndexError:
sys.exit('Please provide a working directory as argument!') sys.exit("Please provide a working directory as argument!")
except FileNotFoundError: except FileNotFoundError:
sys.exit('Invalid working directory provided!') sys.exit("Invalid working directory provided!")
# Try to make sure only one instance is running in a given working directory # Try to make sure only one instance is running in a given working directory
try: try:
if os.path.exists('lockfile'): if os.path.exists("lockfile"):
raise FileExistsError raise FileExistsError
with open('tmplockfile', 'x') as f: with open("tmplockfile", "x") as f:
f.flush() f.flush()
os.fsync(f.fileno()) os.fsync(f.fileno())
os.replace('tmplockfile', 'lockfile') os.replace("tmplockfile", "lockfile")
except FileExistsError: except FileExistsError:
sys.exit('Another instance is already running in the same working directory!') sys.exit("Another instance is already running in the same working directory!")
# Set up logging # Set up logging
try: try:
os.mkdir('logs') os.mkdir("logs")
except FileExistsError: except FileExistsError:
pass pass
logging.basicConfig(level=logging.INFO, format='%(message)s') logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger() logger = logging.getLogger()
logger.addHandler(logging.FileHandler(f"logs/{appstate['timestamp']}.log", 'w')) logger.addHandler(logging.FileHandler(f"logs/{appstate['timestamp']}.log", "w"))
print = logger.info print = logger.info
appstate['logger'] = logger appstate["logger"] = logger
# Get the flag # Get the flag
try: try:
flag = sys.argv[2] flag = sys.argv[2]
except: except IndexError:
flag = None flag = None
if flag not in ['buildonly', 'checkonly', 'force', 'experimental', None]: if flag not in ["buildonly", "checkonly", "force", "experimental", None]:
err_exit(f"Unknown flag: {flag}", appstate) err_exit(f"Unknown flag: {flag}", appstate)
appstate['flag'] = flag appstate["flag"] = flag
appstate['microg_updated'] = False appstate["gmscore_updated"] = False
print(f"Started building ReVanced apps at {time.strftime('%d %B, %Y %H:%M:%S')}") print(f"Started building ReVanced apps at {time.strftime('%d %B, %Y %H:%M:%S')}")
print('----------------------------------------------------------------------') print("----------------------------------------------------------------------")
# Read configs # Read configs
try: try:
appstate['build_config']=cp.ConfigParser() appstate["build_config"] = cp.ConfigParser()
appstate['build_config'].read_file(open('build_config', 'r')) appstate["build_config"].read_file(open("build_config", "r"))
except FileNotFoundError: except FileNotFoundError:
err_exit('No build config provided, exiting. Please look at the GitHub page for more information:\n https://github.com/SinTan1729/ReVancedBuilder', appstate) err_exit(
"No build config provided, exiting. Please look at the GitHub page for more information:\n https://github.com/SinTan1729/ReVancedBuilder",
appstate,
)
appstate['notification_config'] = cp.ConfigParser() appstate["notification_config"] = cp.ConfigParser()
appstate['notification_config'].read('notification_config') appstate["notification_config"].read("notification_config")
# Pull the latest information using the ReVanced API # Pull the latest information using the ReVanced API
try: try:
tools = req.get('https://api.revanced.app/tools').json()['tools'] tools = req.get("https://api.revanced.app/tools").json()["tools"]
appstate['tools'] = tools appstate["tools"] = tools
except req.exceptions.RequestException as e: except req.exceptions.RequestException as e:
err_exit(f"Error fetching patch list, {e}", appstate) err_exit(f"Error fetching patch list, {e}", appstate)
try: try:
with open('versions.json', 'r') as f: with open("versions.json", "r") as f:
appstate['present_vers'] = json.load(f) appstate["present_vers"] = json.load(f)
except: except FileNotFoundError:
# We'll treat empty as 0 later # We'll treat empty as 0 later
appstate['present_vers'] = json.loads('{}') appstate["present_vers"] = json.loads("{}")
appstate['up-to-date'] = True appstate["up-to-date"] = True
# send_notif(appstate, error=False) # <,,,,,,,,<,,,,,,,,,,,,,
if flag != 'buildonly': if flag != "buildonly":
appstate = update_tools(appstate) appstate = update_tools(appstate)
appstate = update_microg(appstate) appstate = update_gmscore(appstate)
if not appstate['up-to-date'] or flag == 'force': if (not appstate["up-to-date"] and flag != "checkonly") or flag == "force":
appstate = get_apks(appstate) appstate = get_apks(appstate)
if (flag != 'checkonly' and not appstate['up-to-date']) or flag in ['force', 'buildonly']: if (flag != "checkonly" and not appstate["up-to-date"]) or flag in ["force", "buildonly"]:
build_apps(appstate) build_apps(appstate)
move_apps(appstate) move_apps(appstate)
# Update version numbers in the versions.json file # Update version numbers in the versions.json file
if appstate['up-to-date'] and flag != 'buildonly': if appstate["up-to-date"] and flag != "buildonly":
print('There\'s nothing to do.') print("There's nothing to do.")
elif flag != 'checkonly': elif flag != "checkonly":
send_notif(appstate)
try: try:
os.rename('versions.json', 'versions-old.json') os.rename("versions.json", "versions-old.json")
except FileNotFoundError: except FileNotFoundError:
pass pass
if flag != 'buildonly': if flag != "buildonly":
with open('versions.json', 'w') as f: with open("versions.json", "w") as f:
json.dump(appstate['present_vers'], f, indent=4) json.dump(appstate["present_vers"], f, indent=4)
try: try:
cmd = f"{appstate['build_config']['post_script']['file']} {appstate['timestamp']}" cmd = f"{appstate['build_config']['post_script']['file']} {appstate['timestamp']}"
print(f"Running the post command '{cmd}'") print(f"Running the post command '{cmd}'")
subprocess.run(cmd, shell=True) subprocess.run(cmd, shell=True)
except: except Exception as ex:
pass print(f"Got exception while running the build: '{ex}'")
err_exit("", appstate, 0)
send_notif(appstate)
# Delete the lockfile # Delete the lockfile
os.remove('lockfile') os.remove("lockfile")
sys.exit(0) sys.exit(0)

498
uv.lock Normal file
View file

@ -0,0 +1,498 @@
version = 1
revision = 1
requires-python = ">=3.6"
resolution-markers = [
"python_full_version >= '3.9'",
"python_full_version == '3.8.*'",
"python_full_version == '3.7.*'",
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
"python_full_version < '3.6.8'",
]
[[package]]
name = "beautifulsoup4"
version = "4.12.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
"python_full_version < '3.6.8'",
]
dependencies = [
{ name = "soupsieve", version = "2.3.2.post1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 },
]
[[package]]
name = "beautifulsoup4"
version = "4.13.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
"python_full_version == '3.8.*'",
"python_full_version == '3.7.*'",
]
dependencies = [
{ name = "soupsieve", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
{ name = "soupsieve", version = "2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" },
{ name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
{ name = "typing-extensions", version = "4.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f0/3c/adaf39ce1fb4afdd21b611e3d530b183bb7759c9b673d60db0e347fd4439/beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", size = 619516 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 },
]
[[package]]
name = "bs4"
version = "0.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "beautifulsoup4", version = "4.12.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
{ name = "beautifulsoup4", version = "4.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c9/aa/4acaf814ff901145da37332e05bb510452ebed97bc9602695059dd46ef39/bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925", size = 698 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/51/bb/bf7aab772a159614954d84aa832c129624ba6c32faa559dfb200a534e50b/bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc", size = 1189 },
]
[[package]]
name = "certifi"
version = "2025.1.31"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
]
[[package]]
name = "charset-normalizer"
version = "2.0.12"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
"python_full_version < '3.6.8'",
]
sdist = { url = "https://files.pythonhosted.org/packages/56/31/7bcaf657fafb3c6db8c787a865434290b726653c912085fbd371e9b92e1c/charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597", size = 79105 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/06/b3/24afc8868eba069a7f03650ac750a778862dc34941a4bebeb58706715726/charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df", size = 39623 },
]
[[package]]
name = "charset-normalizer"
version = "3.4.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
"python_full_version == '3.8.*'",
"python_full_version == '3.7.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 },
{ url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 },
{ url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 },
{ url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 },
{ url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 },
{ url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 },
{ url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 },
{ url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 },
{ url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 },
{ url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 },
{ url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 },
{ url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 },
{ url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 },
{ url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 },
{ url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 },
{ url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 },
{ url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 },
{ url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 },
{ url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 },
{ url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 },
{ url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 },
{ url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 },
{ url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 },
{ url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 },
{ url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 },
{ url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 },
{ url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
{ url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
{ url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
{ url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
{ url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
{ url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
{ url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
{ url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
{ url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
{ url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
{ url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
{ url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
{ url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
{ url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
{ url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
{ url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
{ url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
{ url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
{ url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
{ url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
{ url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
{ url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
{ url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
{ url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
{ url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
{ url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
{ url = "https://files.pythonhosted.org/packages/bb/a6/3a22521736a3a75263ce930a2257eb60fd2a6b8fd9293ea1271a074a202f/charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", size = 135247 },
{ url = "https://files.pythonhosted.org/packages/c2/1e/fa099993d17c00addbb76b3bfcd3ab1692fefc4e193ba738e111c09cfbe2/charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", size = 144536 },
{ url = "https://files.pythonhosted.org/packages/c2/95/8725b2b2fcb418abe048b5558af42e444161433a76b47759539fe5bb7992/charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", size = 137661 },
{ url = "https://files.pythonhosted.org/packages/10/2f/6e2c727649d5e97ee17230f4c064480c69eb563f52ba87df448647d77a99/charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", size = 138592 },
{ url = "https://files.pythonhosted.org/packages/e9/1b/0cad3a4f1906dc36a9cc89093d36b134638463e8e43479dbb96fd47d99ef/charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", size = 141250 },
{ url = "https://files.pythonhosted.org/packages/8f/aa/73a89701dd661d9b5d24d0594c42ed0f7a3aed4448cc4f8f7315d0701399/charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", size = 135234 },
{ url = "https://files.pythonhosted.org/packages/a8/00/b8b43690ecac81e5038dc6332681757123ee9c4c15eb4a6ecf0232393f59/charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", size = 142457 },
{ url = "https://files.pythonhosted.org/packages/ec/a3/2db14427b37cefbf9a9ec3d3c861e1b1721e4d4507536df102c2854966dc/charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", size = 145967 },
{ url = "https://files.pythonhosted.org/packages/0a/97/a183d752295997d95d20f2aa34d66ff6abda5af5482d0e7995dbbc9a753a/charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", size = 145129 },
{ url = "https://files.pythonhosted.org/packages/96/c6/f62d90c0e66525a374c35a1389f4290e5d7a731cd2cfacd9740272f927f9/charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", size = 138515 },
{ url = "https://files.pythonhosted.org/packages/0b/fe/4ade940a1b4bac031357b4a74fc8406a2ecd9dfc007dea850c5b7ee348de/charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", size = 93486 },
{ url = "https://files.pythonhosted.org/packages/89/17/74bb34d02d62a21cc626c7d4db475b6f2207980f6beb002051db8342b139/charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", size = 100195 },
{ url = "https://files.pythonhosted.org/packages/10/bd/6517ea94f2672e801011d50b5d06be2a0deaf566aea27bcdcd47e5195357/charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", size = 195653 },
{ url = "https://files.pythonhosted.org/packages/e5/0d/815a2ba3f283b4eeaa5ece57acade365c5b4135f65a807a083c818716582/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", size = 140701 },
{ url = "https://files.pythonhosted.org/packages/aa/17/c94be7ee0d142687e047fe1de72060f6d6837f40eedc26e87e6e124a3fc6/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", size = 150495 },
{ url = "https://files.pythonhosted.org/packages/f7/33/557ac796c47165fc141e4fb71d7b0310f67e05cb420756f3a82e0a0068e0/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", size = 142946 },
{ url = "https://files.pythonhosted.org/packages/1e/0d/38ef4ae41e9248d63fc4998d933cae22473b1b2ac4122cf908d0f5eb32aa/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", size = 144737 },
{ url = "https://files.pythonhosted.org/packages/43/01/754cdb29dd0560f58290aaaa284d43eea343ad0512e6ad3b8b5c11f08592/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", size = 147471 },
{ url = "https://files.pythonhosted.org/packages/ba/cd/861883ba5160c7a9bd242c30b2c71074cda2aefcc0addc91118e0d4e0765/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", size = 140801 },
{ url = "https://files.pythonhosted.org/packages/6f/7f/0c0dad447819e90b93f8ed238cc8f11b91353c23c19e70fa80483a155bed/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", size = 149312 },
{ url = "https://files.pythonhosted.org/packages/8e/09/9f8abcc6fff60fb727268b63c376c8c79cc37b833c2dfe1f535dfb59523b/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", size = 152347 },
{ url = "https://files.pythonhosted.org/packages/be/e5/3f363dad2e24378f88ccf63ecc39e817c29f32e308ef21a7a6d9c1201165/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", size = 149888 },
{ url = "https://files.pythonhosted.org/packages/e4/10/a78c0e91f487b4ad0ef7480ac765e15b774f83de2597f1b6ef0eaf7a2f99/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", size = 145169 },
{ url = "https://files.pythonhosted.org/packages/d3/81/396e7d7f5d7420da8273c91175d2e9a3f569288e3611d521685e4b9ac9cc/charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", size = 95094 },
{ url = "https://files.pythonhosted.org/packages/40/bb/20affbbd9ea29c71ea123769dc568a6d42052ff5089c5fe23e21e21084a6/charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", size = 102139 },
{ url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 },
{ url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 },
{ url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 },
{ url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 },
{ url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 },
{ url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 },
{ url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 },
{ url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 },
{ url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 },
{ url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 },
{ url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 },
{ url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 },
{ url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 },
{ url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
]
[[package]]
name = "cloudscraper"
version = "1.2.71"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyparsing", version = "3.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.6.8'" },
{ name = "pyparsing", version = "3.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.6.8' and python_full_version < '3.9'" },
{ name = "pyparsing", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
{ name = "requests", version = "2.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
{ name = "requests", version = "2.31.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
{ name = "requests", version = "2.32.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" },
{ name = "requests-toolbelt" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ac/25/6d0481860583f44953bd791de0b7c4f6d7ead7223f8a17e776247b34a5b4/cloudscraper-1.2.71.tar.gz", hash = "sha256:429c6e8aa6916d5bad5c8a5eac50f3ea53c9ac22616f6cb21b18dcc71517d0d3", size = 93261 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/97/fc88803a451029688dffd7eb446dc1b529657577aec13aceff1cc9628c5d/cloudscraper-1.2.71-py2.py3-none-any.whl", hash = "sha256:76f50ca529ed2279e220837befdec892626f9511708e200d48d5bb76ded679b0", size = 99652 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "packaging"
version = "21.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
"python_full_version < '3.6.8'",
]
dependencies = [
{ name = "pyparsing", version = "3.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.6.8'" },
{ name = "pyparsing", version = "3.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.6.8' and python_full_version < '3.7'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/9e/d1a7217f69310c1db8fdf8ab396229f55a699ce34a203691794c5d1cad0c/packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", size = 84848 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/8e/8de486cbd03baba4deef4142bd643a3e7bbe954a784dc1bb17142572d127/packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522", size = 40750 },
]
[[package]]
name = "packaging"
version = "24.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.7.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/ee/b5/b43a27ac7472e1818c4bafd44430e69605baefe1f34440593e0332ec8b4d/packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9", size = 147882 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", size = 53488 },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
"python_full_version == '3.8.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
[[package]]
name = "pyparsing"
version = "3.0.7"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.6.8'",
]
sdist = { url = "https://files.pythonhosted.org/packages/d6/60/9bed18f43275b34198eb9720d4c1238c68b3755620d20df0afd89424d32b/pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea", size = 884709 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/80/c1/23fd82ad3121656b585351aba6c19761926bb0db2ebed9e4ff09a43a3fcc/pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484", size = 98049 },
]
[[package]]
name = "pyparsing"
version = "3.1.4"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.8.*'",
"python_full_version == '3.7.*'",
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
]
sdist = { url = "https://files.pythonhosted.org/packages/83/08/13f3bce01b2061f2bbd582c9df82723de943784cf719a35ac886c652043a/pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032", size = 900231 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/0c/0e3c05b1c87bb6a1c76d281b0f35e78d2d80ac91b5f8f524cebf77f51049/pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c", size = 104100 },
]
[[package]]
name = "pyparsing"
version = "3.2.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 },
]
[[package]]
name = "requests"
version = "2.27.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
"python_full_version < '3.6.8'",
]
dependencies = [
{ name = "certifi", marker = "python_full_version < '3.7'" },
{ name = "charset-normalizer", version = "2.0.12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
{ name = "idna", marker = "python_full_version < '3.7'" },
{ name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/60/f3/26ff3767f099b73e0efa138a9998da67890793bfa475d8278f84a30fec77/requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61", size = 106758 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/61/08076519c80041bc0ffa1a8af0cbd3bf3e2b62af10435d269a9d0f40564d/requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d", size = 63133 },
]
[[package]]
name = "requests"
version = "2.31.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.7.*'",
]
dependencies = [
{ name = "certifi", marker = "python_full_version == '3.7.*'" },
{ name = "charset-normalizer", version = "3.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
{ name = "idna", marker = "python_full_version == '3.7.*'" },
{ name = "urllib3", version = "2.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
]
[[package]]
name = "requests"
version = "2.32.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
"python_full_version == '3.8.*'",
]
dependencies = [
{ name = "certifi", marker = "python_full_version >= '3.8'" },
{ name = "charset-normalizer", version = "3.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" },
{ name = "idna", marker = "python_full_version >= '3.8'" },
{ name = "urllib3", version = "2.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" },
{ name = "urllib3", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
]
[[package]]
name = "requests-toolbelt"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "requests", version = "2.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
{ name = "requests", version = "2.31.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
{ name = "requests", version = "2.32.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 },
]
[[package]]
name = "revancedbuilder"
version = "1.4.1"
source = { virtual = "." }
dependencies = [
{ name = "bs4" },
{ name = "cloudscraper" },
{ name = "packaging", version = "21.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
{ name = "packaging", version = "24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
{ name = "packaging", version = "24.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" },
{ name = "requests", version = "2.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7'" },
{ name = "requests", version = "2.31.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.7.*'" },
{ name = "requests", version = "2.32.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" },
]
[package.metadata]
requires-dist = [
{ name = "bs4" },
{ name = "cloudscraper" },
{ name = "packaging" },
{ name = "requests" },
]
[[package]]
name = "soupsieve"
version = "2.3.2.post1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
"python_full_version < '3.6.8'",
]
sdist = { url = "https://files.pythonhosted.org/packages/f3/03/bac179d539362319b4779a00764e95f7542f4920084163db6b0fd4742d38/soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d", size = 102814 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/16/e3/4ad79882b92617e3a4a0df1960d6bce08edfb637737ac5c3f3ba29022e25/soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759", size = 37377 },
]
[[package]]
name = "soupsieve"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.7.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/47/9e/780779233a615777fbdf75a4dee2af7a345f4bf74b42d4a5f836800b9d91/soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea", size = 101278 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/37/673d6490efc51ec46d198c75903d99de59baffdd47aea3d071b80a9e4e89/soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8", size = 36379 },
]
[[package]]
name = "soupsieve"
version = "2.6"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
"python_full_version == '3.8.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
]
[[package]]
name = "typing-extensions"
version = "4.7.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.7.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/3c/8b/0111dd7d6c1478bf83baa1cab85c686426c7a6274119aceb2bd9d35395ad/typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2", size = 72876 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", size = 33232 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
"python_full_version == '3.8.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]
[[package]]
name = "urllib3"
version = "1.26.20"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.6.8' and python_full_version < '3.7'",
"python_full_version < '3.6.8'",
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225 },
]
[[package]]
name = "urllib3"
version = "2.0.7"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.7.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/af/47/b215df9f71b4fdba1025fc05a77db2ad243fa0926755a52c5e71659f4e3c/urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", size = 282546 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/b2/b157855192a68541a91ba7b2bbcb91f1b4faa51f8bae38d8005c034be524/urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e", size = 124213 },
]
[[package]]
name = "urllib3"
version = "2.2.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.8.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 },
]
[[package]]
name = "urllib3"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
]
sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
]