Files
docker-py-revanced/src/patches.py
T
2023-08-24 20:13:02 +05:30

242 lines
9.0 KiB
Python

"""Revanced Patches."""
import contextlib
import json
from typing import Any, Dict, List, Tuple
from loguru import logger
from src.app import APP
from src.config import RevancedConfig
from src.exceptions import AppNotFound, PatchesJsonLoadFailed
class Patches(object):
"""Revanced Patches."""
revanced_package_names = {
"com.reddit.frontpage": "reddit",
"com.ss.android.ugc.trill": "tiktok",
"com.twitter.android": "twitter",
"de.dwd.warnapp": "warnwetter",
"com.spotify.music": "spotify",
"com.awedea.nyx": "nyx-music-player",
"ginlemon.iconpackstudio": "icon_pack_studio",
"com.ticktick.task": "ticktick",
"tv.twitch.android.app": "twitch",
"com.myprog.hexedit": "hex-editor",
"co.windyapp.android": "windy",
"org.totschnig.myexpenses": "my-expenses",
"com.backdrops.wallpapers": "backdrops",
"com.ithebk.expensemanager": "expensemanager",
"net.dinglisch.android.taskerm": "tasker",
"net.binarymode.android.irplus": "irplus",
"com.vsco.cam": "vsco",
"com.zombodroid.MemeGenerator": "meme-generator-free",
"com.teslacoilsw.launcher": "nova_launcher",
"eu.faircode.netguard": "netguard",
"com.instagram.android": "instagram",
"com.nis.app": "inshorts",
"com.facebook.orca": "messenger",
"com.google.android.apps.recorder": "grecorder",
"tv.trakt.trakt": "trakt",
"com.candylink.openvpn": "candyvpn",
"com.sony.songpal.mdr": "sonyheadphone",
"com.dci.dev.androidtwelvewidgets": "androidtwelvewidgets",
"io.yuka.android": "yuka",
"free.reddit.news": "relay",
"com.rubenmayayo.reddit": "boost",
"com.andrewshu.android.reddit": "rif",
"com.laurencedawson.reddit_sync": "sync",
"ml.docilealligator.infinityforreddit": "infinity",
"me.ccrama.redditslide": "slide",
"com.onelouder.baconreader": "bacon",
"com.google.android.youtube": "youtube",
"com.google.android.apps.youtube.music": "youtube_music",
"com.mgoogle.android.gms": "microg",
"jp.pxv.android": "pixiv",
}
@staticmethod
def get_package_name(app: str) -> str:
"""The function `get_package_name` takes an app name as input and
returns the corresponding package name, or raises an exception if the
app is not supported.
Parameters
----------
app : str
The `app` parameter is a string that represents the name of an app.
Returns
-------
a string, which is the package name corresponding to the given app name.
"""
for package, app_name in Patches.revanced_package_names.items():
if app_name == app:
return package
raise AppNotFound(
f"App {app} not supported officially yet. Please provide package name in env to proceed."
)
@staticmethod
def support_app() -> Dict[str, str]:
"""The function `support_app()` returns a dictionary of supported app
IDs.
Returns
-------
a dictionary of supported apps.
"""
return Patches.revanced_package_names
def fetch_patches(self, config: RevancedConfig, app: APP) -> None:
"""The function fetches patches from a JSON file and organizes them
based on compatibility with different applications.
Parameters
----------
config : RevancedConfig
The `config` parameter is of type `RevancedConfig` and represents the configuration for the
application.
app : APP
The `app` parameter is of type `APP`. It represents an instance of the `APP` class.
"""
self.patches_dict[app.app_name] = []
patch_loader = PatchLoader()
patches = patch_loader.load_patches(
f'{config.temp_folder}/{app.resource["patches_json"]}'
)
for patch in patches:
if not patch["compatiblePackages"]:
p = {x: patch[x] for x in ["name", "description"]}
p["app"] = "universal"
p["version"] = "all"
self.patches_dict["universal_patch"].append(p)
else:
for compatible_package, version in [
(x["name"], x["versions"]) for x in patch["compatiblePackages"]
]:
if app.package_name == compatible_package:
p = {x: patch[x] for x in ["name", "description"]}
p["app"] = compatible_package
p["version"] = version[-1] if version else "all"
self.patches_dict[app.app_name].append(p)
app.no_of_patches = len(self.patches_dict[app.app_name])
def __init__(self, config: RevancedConfig, app: APP) -> None:
self.patches_dict: Dict[str, Any] = {"universal_patch": []}
self.fetch_patches(config, app)
def get(self, app: str) -> Tuple[List[Dict[str, str]], str]:
"""The function `get` retrieves all patches for a given application and
returns them along with the latest version.
Parameters
----------
app : str
The `app` parameter is a string that represents the name of the application for which you want
to retrieve patches.
Returns
-------
a tuple containing two elements. The first element is a list of dictionaries representing
patches for the given app. The second element is a string representing the version of the
patches.
"""
patches = self.patches_dict[app]
version = "latest"
with contextlib.suppress(StopIteration):
version = next(i["version"] for i in patches if i["version"] != "all")
return patches, version
def include_exclude_patch(
self, app: APP, parser: Any, patches: List[Dict[str, str]]
) -> None:
"""The function `include_exclude_patch` includes and excludes patches
for a given app based on certain conditions.
Parameters
----------
app : APP
The "app" parameter is the name of the app for which the patches are being included or
excluded.
parser : Any
The `parser` parameter is an object of type `Any`, which means it can be any type of object. It
is used to perform parsing operations.
patches : List[Dict[str, str]]
A list of dictionaries, where each dictionary represents a patch and contains the following
keys:
"""
for patch in patches:
normalized_patch = patch["name"].lower().replace(" ", "-")
parser.include(
normalized_patch
) if normalized_patch not in app.exclude_request else parser.exclude(
normalized_patch
)
for normalized_patch in app.include_request:
parser.include(
normalized_patch
) if normalized_patch not in self.patches_dict["universal_patch"] else ()
def get_app_configs(self, app: "APP") -> List[Dict[str, str]]:
"""The function `get_app_configs` retrieves configurations for a given
app, including patches, version information, and whether it is
experimental.
Parameters
----------
app : "APP"
The "app" parameter is the name of the application for which you want to get the
configurations.
Returns
-------
the total_patches, which is a list of dictionaries containing information about the patches for
the given app. Each dictionary in the list contains the keys "Patches", "Version", and
"Experimental".
"""
experiment = False
total_patches, recommended_version = self.get(app=app.app_name)
if app.app_version:
logger.debug(f"Picked {app} version {app.app_version:} from env.")
if (
app.app_version == "latest"
or app.app_version > recommended_version
or app.app_version < recommended_version
):
experiment = True
recommended_version = app.app_version
app.set_recommended_version(recommended_version, experiment)
return total_patches
class PatchLoader:
"""Patch Loader."""
@staticmethod
def load_patches(file_name: str) -> Any:
"""The function `load_patches` loads patches from a file and returns
them.
Parameters
----------
file_name : str
The `file_name` parameter is a string that represents the name or path of the file from which
the patches will be loaded.
Returns
-------
the patches loaded from the file.
"""
try:
with open(file_name) as f:
patches = json.load(f)
return patches
except FileNotFoundError as e:
raise PatchesJsonLoadFailed("File not found", file_name=file_name) from e