From 8f17bf9f2ba8c3dd18dc0f0028b884f814bce588 Mon Sep 17 00:00:00 2001 From: Nikhil Badyal Date: Mon, 23 Dec 2024 12:27:43 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Gen=20patches=20json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 8 ---- .env.my | 27 ------------ README.md | 4 -- src/app.py | 3 +- src/config.py | 3 +- src/parser.py | 10 +++-- src/patches.py | 47 +++++--------------- src/patches_gen.py | 104 +++++++++++++++++++++++++++++++++++++++++++++ src/utils.py | 3 -- 9 files changed, 123 insertions(+), 86 deletions(-) create mode 100644 src/patches_gen.py diff --git a/.env.example b/.env.example index adef50c..c2b4b50 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,6 @@ EXTRA_FILES=https://github.com/inotia00/VancedMicroG/releases/latest@VancedMicro PATCH_APPS=youtube,youtube_revancify_red,youtube_revancify_blue,youtube_mmt,youtube_music,reddit GLOBAL_CLI_DL=https://github.com/revanced/revanced-cli GLOBAL_PATCHES_DL=https://github.com/revanced/revanced-patches -GLOBAL_PATCHES_JSON_DL=https://api.revanced.app/v4/patches/list #Example EXISTING_DOWNLOADED_APKS=twitter @@ -12,7 +11,6 @@ PERSONAL_ACCESS_TOKEN=ghp_asample_token #YouTube: YOUTUBE_CLI_DL=https://github.com/inotia00/revanced-cli YOUTUBE_PATCHES_DL=https://github.com/YT-Advanced/ReX-patches -YOUTUBE_PATCHES_JSON_DL=https://github.com/YT-Advanced/ReX-patches YOUTUBE_EXCLUDE_PATCH=custom-branding-icon-revancify-blue,custom-branding-icon-revancify-red,custom-branding-icon-mmt,custom-branding-youtube-name,enable-debug-logging #Example @@ -24,7 +22,6 @@ YOUTUBE_REVANCIFY_RED_PACKAGE_NAME=com.google.android.youtube YOUTUBE_REVANCIFY_RED_DL_SOURCE=https://www.apkmirror.com/apk/google-inc/youtube/ YOUTUBE_REVANCIFY_RED_CLI_DL=https://github.com/inotia00/revanced-cli YOUTUBE_REVANCIFY_RED_PATCHES_DL=https://github.com/YT-Advanced/ReX-patches -YOUTUBE_REVANCIFY_RED_PATCHES_JSON_DL=https://github.com/YT-Advanced/ReX-patches YOUTUBE_REVANCIFY_RED_EXCLUDE_PATCH=custom-branding-icon-revancify-blue,custom-branding-icon-mmt,custom-branding-youtube-name,enable-debug-logging #YouTube Revancify Blue: @@ -32,7 +29,6 @@ YOUTUBE_REVANCIFY_BLUE_PACKAGE_NAME=com.google.android.youtube YOUTUBE_REVANCIFY_BLUE_DL_SOURCE=https://www.apkmirror.com/apk/google-inc/youtube/ YOUTUBE_REVANCIFY_BLUE_CLI_DL=https://github.com/inotia00/revanced-cli YOUTUBE_REVANCIFY_BLUE_PATCHES_DL=https://github.com/YT-Advanced/ReX-patches -YOUTUBE_REVANCIFY_BLUE_PATCHES_JSON_DL=https://github.com/YT-Advanced/ReX-patches YOUTUBE_REVANCIFY_BLUE_EXCLUDE_PATCH=custom-branding-icon-revancify-red,custom-branding-icon-mmt,custom-branding-youtube-name,enable-debug-logging #YouTube MMT: @@ -40,13 +36,11 @@ YOUTUBE_MMT_PACKAGE_NAME=com.google.android.youtube YOUTUBE_MMT_DL_SOURCE=https://www.apkmirror.com/apk/google-inc/youtube/ YOUTUBE_MMT_CLI_DL=https://github.com/inotia00/revanced-cli YOUTUBE_MMT_PATCHES_DL=https://github.com/YT-Advanced/ReX-patches -YOUTUBE_MMT_PATCHES_JSON_DL=https://github.com/YT-Advanced/ReX-patches YOUTUBE_MMT_EXCLUDE_PATCH=custom-branding-icon-revancify-blue,custom-branding-icon-revancify-red,custom-branding-youtube-name,enable-debug-logging #YouTube Music: YOUTUBE_MUSIC_CLI_DL=https://github.com/inotia00/revanced-cli YOUTUBE_MUSIC_PATCHES_DL=https://github.com/YT-Advanced/ReX-patches -YOUTUBE_MUSIC_PATCHES_JSON_DL=https://github.com/YT-Advanced/ReX-patches YOUTUBE_MUSIC_EXCLUDE_PATCH=custom-branding-icon-mmt,custom-branding-icon-revancify-blue,custom-branding-icon-revancify-red,custom-branding-music-name,enable-compact-dialog,enable-debug-logging #Example @@ -55,11 +49,9 @@ YOUTUBE_MUSIC_VERSION=6.15.52 #Reddit REDDIT_CLI_DL=https://github.com/inotia00/revanced-cli REDDIT_PATCHES_DL=https://github.com/YT-Advanced/ReX-patches -REDDIT_PATCHES_JSON_DL=https://github.com/YT-Advanced/ReX-patches #Example #Twitter TWITTER_VERSION=latest TWITTER_CLI_DL=local://cli.jar TWITTER_PATCHES_DL=local://patches.jar -TWITTER_PATCHES_JSON_DL=local://patches.json diff --git a/.env.my b/.env.my index 6f84692..d4bda96 100644 --- a/.env.my +++ b/.env.my @@ -6,13 +6,10 @@ GLOBAL_PATCHES_DL=https://github.com/revanced/revanced-patches/releases/latest #YouTube: YOUTUBE_PATCHES_DL=https://github.com/inotia00/revanced-patches/latest-prerelease -YOUTUBE_PATCHES_JSON_DL=https://github.com/inotia00/revanced-patches/latest-prerelease YOUTUBE_EXCLUDE_PATCH=custom-branding-icon-youtube,custom-branding-name-youtube,enable-debug-logging,hide-fullscreen-button #YouTube Music: YOUTUBE_MUSIC_PATCHES_DL=https://github.com/inotia00/revanced-patches/latest-prerelease -YOUTUBE_MUSIC_PATCHES_JSON_DL=https://github.com/inotia00/revanced-patches/latest-prerelease - YOUTUBE_MUSIC_EXCLUDE_PATCH=custom-branding-icon-youtube-music,custom-branding-name-youtube-music,enable-compact-dialog,enable-debug-logging,enable-old-player-layout YOUTUBE_MUSIC_VERSION=latest @@ -22,27 +19,3 @@ REDDIT_EXCLUDE_PATCH=change-package-name #Twitter: TWITTER_PATCHES_DL=https://github.com/crimera/piko/releases/latest - - -#Global: -#EXTRA_FILES=https://github.com/ReVanced/GmsCore/releases/latest@Revanced-Microg.apk -#PATCH_APPS=youtube,youtube_music,reddit,instagram -# -#YOUTUBE_EXCLUDE_PATCH=custom-branding -# -#FACEBOOK_VERSION=latest -#YOUTUBE_MUSIC_VERSION=latest -#REDDIT_VERSION=latest -#TWITTER_VERSION=latest -#INSTAGRAM_VERSION=latest -# -# -#SOUNDCLOUD_DL_SOURCE=https://apkpure.net/-/com.soundcloud.android -#SOUNDCLOUD_PACKAGE_NAME=com.soundcloud.android -# -#REDDIT_DL_SOURCE=https://apkpure.net/-/com.reddit.frontpage -#REDDIT_PACKAGE_NAME=com.reddit.frontpage -# -# -#TWITTER_DL_SOURCE=https://apkpure.net/-/com.twitter.android -#TWITTER_PACKAGE_NAME=com.twitter.android diff --git a/README.md b/README.md index 2225626..7cf8a01 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,6 @@ You can use any of the following methods to build. | [GLOBAL_CLI_DL*](#global-resources) | DL for CLI to be used for patching apps. | [Revanced CLI](https://github.com/revanced/revanced-cli) | | [GLOBAL_PATCHES_DL*](#global-resources) | DL for Patches to be used for patching apps. | [Revanced Patches](https://github.com/revanced/revanced-patches) | | [GLOBAL_SPACE_FORMATTED_PATCHES*](#global-resources) | Whether patches are space formatted. | True | -| [GLOBAL_PATCHES_JSON_DL*](#global-resources) | DL for Patches Json to be used for patching apps. | [Revanced Patches](https://github.com/revanced/revanced-patches) | | [GLOBAL_KEYSTORE_FILE_NAME*](#global-keystore-file-name) | Key file to be used for signing apps | [Builder's own key](https://github.com/nikhilbadyal/docker-py-revanced/blob/main/apks/revanced.keystore) | | [GLOBAL_OLD_KEY*](#global-keystore-file-name) | Whether key was generated with cli v4(new) or not |
[Builder's v3(old) own key](https://github.com/nikhilbadyal/docker-py-revanced/blob/main/apks/revanced.keystore) | | [GLOBAL_OPTIONS_FILE*](#global-options-file) | Options file to be used | [Builder's default file](https://github.com/nikhilbadyal/docker-py-revanced/blob/main/apks/options.json) | @@ -140,7 +139,6 @@ You can use any of the following methods to build. |:------------------------------------------------------------|:--------------------------------------------------------------------------------------------:|:-------------------------------| | [*APP_NAME*_CLI_DL](#global-resources) | DL for CLI to be used for patching **APP_NAME**. | GLOBAL_CLI_DL | | [*APP_NAME*_PATCHES_DL](#global-resources) | DL for Patches to be used for patching **APP_NAME**. | GLOBAL_PATCHES_DL | -| [*APP_NAME*_PATCHES_JSON_DL](#global-resources) | DL for Patches Json to be used for patching **APP_NAME**. | GLOBAL_PATCHES_JSON_DL | | [*APP_NAME*_SPACE_FORMATTED_PATCHES](#global-resources) | Whether patches are space formatted. **APP_NAME**. | GLOBAL_SPACE_FORMATTED_PATCHES | | [*APP_NAME*_KEYSTORE_FILE_NAME](#global-keystore-file-name) | Key file to be used for signing **APP_NAME**. | GLOBAL_KEYSTORE_FILE_NAME | | [*APP_NAME*_OLD_KEY](#global-keystore-file-name) | Whether key used was generated with cli > v4(new)

**APP_NAME**.

| GLOBAL_OLK_KEY | @@ -293,7 +291,6 @@ You can use any of the following methods to build. ```dotenv GLOBAL_CLI_DL=https://github.com/revanced/revanced-cli GLOBAL_PATCHES_DL=https://github.com/revanced/revanced-patches - GLOBAL_PATCHES_JSON_DL=https://api.revanced.app/v4/patches/list ``` Resources downloaded from envs and will be used for patching for any **APP_NAME**. Unless provided different resource for the individual app.

@@ -304,7 +301,6 @@ You can use any of the following methods to build. ```dotenv YOUTUBE_CLI_DL=https://github.com/inotia00/revanced-cli YOUTUBE_PATCHES_DL=https://github.com/inotia00/revanced-patches - YOUTUBE_PATCHES_JSON_DL=https://api.revanced.app/v4/patches/list ``` With the config tool will try to patch YouTube with resources from inotia00 while other global resource will used for patching other apps.
diff --git a/src/app.py b/src/app.py index 48fdb12..78b4d37 100644 --- a/src/app.py +++ b/src/app.py @@ -32,7 +32,6 @@ class APP(object): 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.patches_json_dl = config.env.str(f"{app_name}_PATCHES_JSON_DL".upper(), config.global_patches_json_dl) self.exclude_request: list[str] = config.env.list(f"{app_name}_EXCLUDE_PATCH".upper(), []) self.include_request: list[str] = config.env.list(f"{app_name}_INCLUDE_PATCH".upper(), []) self.resource: dict[str, dict[str, str]] = {} @@ -45,6 +44,7 @@ class APP(object): self.download_source = config.env.str(f"{app_name}_DL_SOURCE".upper(), "") self.package_name = package_name self.old_key = config.env.bool(f"{app_name}_OLD_KEY".upper(), config.global_old_key) + self.patches: list[dict[Any, Any]] = [] self.space_formatted = config.env.bool( f"{app_name}_SPACE_FORMATTED_PATCHES".upper(), config.global_space_formatted, @@ -149,7 +149,6 @@ class APP(object): download_tasks = [ ("cli", self.cli_dl, config, ".*jar"), ("patches", self.patches_dl, config, ".*rvp"), - ("patches_json", self.patches_json_dl, config, ".*"), ] # Using a ThreadPoolExecutor for parallelism diff --git a/src/config.py b/src/config.py index b584fe1..d43711b 100644 --- a/src/config.py +++ b/src/config.py @@ -5,7 +5,7 @@ from typing import Self from environs import Env -from src.utils import default_build, default_cli, default_patches, default_patches_json, resource_folder +from src.utils import default_build, default_cli, default_patches, resource_folder class RevancedConfig(object): @@ -22,7 +22,6 @@ class RevancedConfig(object): self.dry_run = env.bool("DRY_RUN", False) self.global_cli_dl = env.str("GLOBAL_CLI_DL", default_cli) self.global_patches_dl = env.str("GLOBAL_PATCHES_DL", default_patches) - self.global_patches_json_dl = env.str("GLOBAL_PATCHES_JSON_DL", default_patches_json) self.global_keystore_name = env.str("GLOBAL_KEYSTORE_FILE_NAME", "revanced.keystore") self.global_options_file = env.str("GLOBAL_OPTIONS_FILE", "options.json") self.global_archs_to_build = env.list("GLOBAL_ARCHS_TO_BUILD", []) diff --git a/src/parser.py b/src/parser.py index db63bca..908789a 100644 --- a/src/parser.py +++ b/src/parser.py @@ -84,11 +84,10 @@ class Parser(object): """ try: name = name.lower().replace(" ", "-") - patch_index = self._PATCHES.index(name) indices = [i for i in range(len(self._PATCHES)) if self._PATCHES[i] == name] for patch_index in indices: if self._PATCHES[patch_index - 1] == "-e": - self._PATCHES[patch_index - 1] = "-i" + self._PATCHES[patch_index - 1] = "-d" else: self._PATCHES[patch_index - 1] = "-e" except ValueError: @@ -99,8 +98,10 @@ class Parser(object): def exclude_all_patches(self: Self) -> None: """The function `exclude_all_patches` exclude all the patches.""" for idx, item in enumerate(self._PATCHES): - if item == "-i": - self._PATCHES[idx] = "-e" + if idx == 0: + continue + if item == "-e": + self._PATCHES[idx] = "-d" def include_exclude_patch( self: Self, @@ -193,6 +194,7 @@ class Parser(object): excluded = set(possible_archs) - set(app.archs_to_build) for arch in excluded: args.extend(("--rip-lib", arch)) + args.extend(("-f",)) start = perf_counter() logger.debug(f"Sending request to revanced cli for building with args java {args}") process = Popen(["java", *args], stdout=PIPE) diff --git a/src/patches.py b/src/patches.py index d7932de..da5a758 100644 --- a/src/patches.py +++ b/src/patches.py @@ -1,15 +1,14 @@ """Revanced Patches.""" import contextlib -import json -from pathlib import Path -from typing import Any, ClassVar, Self +from typing import ClassVar, Self from loguru import logger from src.app import APP from src.config import RevancedConfig -from src.exceptions import AppNotFoundError, PatchesJsonLoadError +from src.exceptions import AppNotFoundError +from src.patches_gen import convert_command_output_to_json class Patches(object): @@ -125,22 +124,23 @@ class Patches(object): 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_file = app.resource["patches_json"]["file_name"] - patches = patch_loader.load_patches(f"{config.temp_folder}/{patches_file}") + app.patches = convert_command_output_to_json( + f"{config.temp_folder}/{app.resource["cli"]["file_name"]}", + f"{config.temp_folder}/{app.resource["patches"]["file_name"]}", + ) - for patch in patches: + for patch in app.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, versions in patch["compatiblePackages"].items(): + 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", "use"]} + p = {x: patch[x] for x in ["name", "description"]} p["app"] = compatible_package - p["version"] = versions[-1] if versions else "all" + 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]) @@ -199,28 +199,3 @@ class Patches(object): 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 diff --git a/src/patches_gen.py b/src/patches_gen.py new file mode 100644 index 0000000..abd3e49 --- /dev/null +++ b/src/patches_gen.py @@ -0,0 +1,104 @@ +"""Generate patches using cli.""" + +import re +import subprocess +from pathlib import Path +from typing import Any + + +def convert_command_output_to_json( + jar_file_name: str, + patches_file: str, +) -> list[dict[Any, Any]]: + """ + Runs the ReVanced CLI command, processes the output, and saves it as a sorted JSON file. + + Args: + jar_file_name (str): Name or path of the JAR file to run. + patches_file (str): The patches file name or path to pass to the command. + """ + + def run_command_and_capture_output(patches_command: list[str]) -> Any: + result = subprocess.run(patches_command, capture_output=True, text=True, check=True) + return result.stdout + + def parse_text_to_json(text: str) -> list[dict[Any, Any]]: + # Split the data into individual sections based on "Name:" + sections = re.split(r"(?=Name:)", text) + result = [] + + for section in sections: + # Extract the name + name_match = re.search(r"Name: (.*?)\n", section) + name = name_match.group(1).strip() if name_match else None + + # Extract the description + description_match = re.search(r"Description: (.*?)\n", section) + description = description_match.group(1).strip() if description_match else "" + + # Extract the enabled state + enabled_match = re.search(r"Enabled: (true|false)", section, re.IGNORECASE) + enabled = enabled_match.group(1).lower() == "true" if enabled_match else False + + # Extract compatible packages + compatible_packages = [] + if "Compatible packages:" in section: + package_sections = re.split(r"\s*Package name: ", section.split("Compatible packages:")[1]) + for package_section in package_sections[1:]: # Skip the initial split + package_name = package_section.split("\n")[0].strip() + versions_match = re.search(r"Compatible versions:\s*((?:\d+\.\d+\.\d+\s*)+)", package_section) + versions = versions_match.group(1).split() if versions_match else [] + compatible_packages.append({"name": package_name, "versions": versions if versions else None}) + + # Extract options + options = [] + if "Options:" in section: + options_section = section.split("Options:")[1] + option_matches = re.findall( + r"Title: (.*?)\n\s*Description: (.*?)\n\s*Required: (true|false)\n\s*Key: (.*?)\n\s*Default: (.*?)\n(?:\s*Possible values:\s*(.*?))?\s*Type: (.*?)\n", # noqa: E501 + options_section, + re.DOTALL, + ) + for match in option_matches: + option = { + "title": match[0].strip(), + "description": match[1].strip(), + "required": match[2].lower() == "true", + "key": match[3].strip(), + "default": match[4].strip(), + "possible_values": [v.strip() for v in match[5].split() if v.strip()] if match[5] else [], + "type": match[6].strip(), + } + options.append(option) + + # Append the parsed data + result.append( + { + "name": name, + "description": description, + "compatiblePackages": compatible_packages if compatible_packages else None, + "use": enabled, + "options": options, + }, + ) + + return result + + # Run the command + command = ["java", "-jar", jar_file_name, "list-patches", "-ipuv", patches_file] + output = run_command_and_capture_output(command) + + parsed_data = parse_text_to_json(output) + + # Filter out invalid entries where "name" is None + parsed_data = [entry for entry in parsed_data if entry["name"] is not None] + + # Sort the data by the "name" field + parsed_data.sort(key=lambda x: x["name"]) + + with Path("patches.json").open("w") as file: + import json + + json.dump(parsed_data, file, indent=2) + + return parsed_data diff --git a/src/utils.py b/src/utils.py index ff9b9dc..2f9a886 100644 --- a/src/utils.py +++ b/src/utils.py @@ -38,7 +38,6 @@ request_header = { } default_cli = "https://github.com/revanced/revanced-cli/releases/latest" default_patches = "https://github.com/revanced/revanced-patches/releases/latest" -default_patches_json = "https://api.revanced.app/v4/patches/list" bs4_parser = "html.parser" changelog_file = "changelog.md" changelog_json_file = "changelog.json" @@ -52,7 +51,6 @@ time_zone = "Asia/Kolkata" app_version_key = "app_version" patches_version_key = "patches_version" cli_version_key = "cli_version" -patches_json_version_key = "patches_json_version" implement_method = "Please implement the method" status_code_200 = 200 resource_folder = "apks" @@ -267,7 +265,6 @@ def save_patch_info(app: "APP", updates_info: dict[str, Any]) -> dict[str, Any]: app_version_key: app.app_version, patches_version_key: app.resource["patches"]["version"], cli_version_key: app.resource["cli"]["version"], - patches_json_version_key: app.resource["patches_json"]["version"], "ms_epoch_since_patched": datetime_to_ms_epoch(datetime.now(timezone(time_zone))), "date_patched": datetime.now(timezone(time_zone)), "app_dump": app.for_dump(),