📝 Gen patches json

This commit is contained in:
Nikhil Badyal
2024-12-23 12:27:43 +05:30
committed by Nikhil Badyal
parent e97ffbef67
commit 8f17bf9f2b
9 changed files with 123 additions and 86 deletions
+1 -2
View File
@@ -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
+1 -2
View File
@@ -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", [])
+6 -4
View File
@@ -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)
+11 -36
View File
@@ -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
+104
View File
@@ -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
-3
View File
@@ -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(),