Files
docker-py-revanced/src/app.py
T
2023-08-22 17:02:57 +05:30

170 lines
6.8 KiB
Python

"""Class to represent apk to be patched."""
import concurrent
import hashlib
import pathlib
from concurrent.futures import ThreadPoolExecutor
from typing import Dict
from loguru import logger
from src.config import RevancedConfig
from src.exceptions import PatchingFailed
from src.utils import slugify
class APP(object):
"""Patched APK."""
def __init__(self, app_name: str, config: RevancedConfig):
self.app_name = app_name
self.app_version = config.env.str(f"{app_name}_VERSION".upper(), None)
self.experiment = False
self.cli_dl = config.env.str(f"{app_name}_CLI_DL".upper(), config.global_cli_dl)
self.patches_dl = config.env.str(
f"{app_name}_PATCHES_DL".upper(), config.global_patches_dl
)
self.integrations_dl = config.env.str(
f"{app_name}_INTEGRATIONS_DL".upper(), config.global_integrations_dl
)
self.patches_json_dl = config.env.str(
f"{app_name}_PATCHES_JSON_DL".upper(), config.global_patches_json_dl
)
self.exclude_request = config.env.list(f"{app_name}_EXCLUDE_PATCH".upper(), [])
self.include_request = config.env.list(f"{app_name}_INCLUDE_PATCH".upper(), [])
self.resource: Dict[str, str] = {}
self.no_of_patches = 0
self.keystore_name = config.env.str(
f"{app_name}_KEYSTORE_FILE_NAME".upper(), config.global_keystore_name
)
self.archs_to_build = config.env.list(
f"{app_name}_ARCHS_TO_BUILD".upper(), config.global_archs_to_build
)
self.download_file_name = None
self.download_patch_resources(config)
def get_output_file_name(self) -> str:
"""The function returns a string representing the output file name for
an APK file appended with version.
Returns
-------
a string that represents the output file name for an APK file.
"""
return f"Re-{self.app_name}-{slugify(self.app_version)}-output.apk"
def set_recommended_version(self, version: str, exp: bool = False) -> None:
"""The function sets the recommended version and experiment flag for an
app.
Parameters
----------
version : str
The version parameter is a string that represents the recommended version of the app.
exp : bool, optional
The "exp" parameter is a boolean flag that indicates whether the specified version is for an
experimental or regular release. If "exp" is set to True, it means the version is for an
experimental release. If "exp" is set to False or not provided, it means the version is for
"""
self.app_version = version
self.experiment = exp
def __str__(self: "APP") -> str:
attrs = vars(self)
return ", ".join([f"{key}: {value}" for key, value in attrs.items()])
@staticmethod
def download(
url: str, config: RevancedConfig, assets_filter: str, file_name: str = ""
) -> str:
"""The `download` function downloads a file from a given URL using a
specified configuration and filters the assets based on a given filter.
Parameters
----------
url : str
The `url` parameter is a string that represents the URL of the resource you want to download.
It can be a URL from GitHub or a local file URL.
config : RevancedConfig
The `config` parameter is an instance of the `RevancedConfig` class. It is used to provide
configuration settings for the download process.
assets_filter : str
The `assets_filter` parameter is a string that is used to filter the assets to be downloaded
from a GitHub repository. It is used when the `url` parameter starts with "https://github". The
`assets_filter` string is matched against the names of the assets in the repository, and only
file_name : str
The `file_name` parameter is a string that represents the name of the file that will be
downloaded. If no value is provided for `file_name`, the function will generate a filename based
on the URL of the file being downloaded.
Returns
-------
a string, which is the file name of the downloaded file.
"""
from src.downloader.download import Downloader
url = url.strip()
if url.startswith("https://github"):
from src.downloader.github import Github
url = Github.patch_resource(url, assets_filter, config)
elif url.startswith("local://"):
return url.split("/")[-1]
if not file_name:
extension = pathlib.Path(url).suffix
file_name = APP.generate_filename(url) + extension
Downloader(None, config).direct_download(url, file_name) # type: ignore
return file_name
def download_patch_resources(self, config: RevancedConfig) -> None:
"""The function `download_patch_resources` downloads various resources
for patching in parallel using a ThreadPoolExecutor.
Parameters
----------
config : RevancedConfig
The `config` parameter is an instance of the `RevancedConfig` class. It is used to provide
configuration settings for the resource download tasks.
"""
logger.info("Downloading resources for patching.")
# Create a list of resource download tasks
download_tasks = [
("cli", self.cli_dl, config, ".*jar"),
("integrations", self.integrations_dl, config, ".*apk"),
("patches", self.patches_dl, config, ".*jar"),
("patches_json", self.patches_json_dl, config, ".*json"),
]
# Using a ThreadPoolExecutor for parallelism
with ThreadPoolExecutor(4) as executor:
futures = {
resource_name: executor.submit(self.download, *args)
for resource_name, *args in download_tasks
}
# Wait for all tasks to complete
concurrent.futures.wait(futures.values())
# Retrieve results from completed tasks
for resource_name, future in futures.items():
try:
self.resource[resource_name] = future.result()
except Exception as e:
raise PatchingFailed(e) from e
@staticmethod
def generate_filename(url: str) -> str:
"""The function `generate_filename` takes a URL as input and returns a
hashed version of the URL as the filename.
Parameters
----------
url : str
The `url` parameter is a string that represents a URL.
Returns
-------
the encoded URL as a string.
"""
encoded_url: str = hashlib.sha256(url.encode()).hexdigest()
return encoded_url