Files
docker-py-revanced/src/patches.py
T
2023-08-26 11:30:23 +05:30

224 lines
8.7 KiB
Python

"""Revanced Patches."""
import contextlib
import json
from pathlib import Path
from typing import Any, ClassVar, Dict, List, Self, Tuple
from loguru import logger
from src.app import APP
from src.config import RevancedConfig
from src.exceptions import AppNotFoundError, PatchesJsonLoadError
class Patches(object):
"""Revanced Patches."""
revanced_package_names: ClassVar[Dict[str, str]] = {
"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.adobe.lrmobile": "lightroom",
"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.
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
msg = f"App {app} not supported officially yet. Please provide package name in env to proceed."
raise AppNotFoundError(msg)
@staticmethod
def support_app() -> Dict[str, str]:
"""The function returns a dictionary of supported app IDs.
Returns
-------
a dictionary of supported apps.
"""
return Patches.revanced_package_names
def fetch_patches(self: Self, config: RevancedConfig, app: APP) -> None:
"""The function fetches patches from a JSON file.
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: Self, config: RevancedConfig, app: APP) -> None:
self.patches_dict: Dict[str, Any] = {"universal_patch": []}
self.fetch_patches(config, app)
def get(self: Self, app: str) -> Tuple[List[Dict[str, str]], str]:
"""The function `get` returns all patches and version for a given application.
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: Self, app: APP, parser: Any, patches: List[Dict[str, str]]) -> None:
"""The function `include_exclude_patch` includes and excludes patches for a given app.
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: Self, app: "APP") -> List[Dict[str, str]]:
"""The function `get_app_configs` returns configurations for a given app.
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.app_version = recommended_version
app.experiment = experiment
return total_patches
class PatchLoader(object):
"""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 Path(file_name).open() as f:
return json.load(f)
except FileNotFoundError as e:
msg = "File not found"
raise PatchesJsonLoadError(msg, file_name=file_name) from e