142 lines
3.5 KiB
Python
142 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from constants import SRC_FILE
|
|
|
|
# Canonical key order - matches the output of generate_app_entry() in add-app.py
|
|
KEY_ORDER = [
|
|
"id",
|
|
"url",
|
|
"author",
|
|
"name",
|
|
"preferredApkIndex",
|
|
"additionalSettings",
|
|
"categories",
|
|
"allowIdChange",
|
|
"overrideSource",
|
|
"meta",
|
|
]
|
|
|
|
# Canonical key order for additionalSettings - source-specific keys first,
|
|
# then common keys, grouped logically. Matches DEFAULT_ADDITIONAL_SETTINGS
|
|
# in add-app.py with source-specific keys prepended.
|
|
SETTINGS_KEY_ORDER = [
|
|
# GitHub/Codeberg source-specific
|
|
"includePrereleases",
|
|
"fallbackToOlderReleases",
|
|
"filterReleaseTitlesByRegEx",
|
|
"filterReleaseNotesByRegEx",
|
|
"verifyLatestTag",
|
|
"sortMethodChoice",
|
|
"useLatestAssetDateAsReleaseDate",
|
|
"releaseTitleAsVersion",
|
|
"github-creds",
|
|
"GHReqPrefix",
|
|
# HTML source-specific
|
|
"intermediateLink",
|
|
"customLinkFilterRegex",
|
|
"filterByLinkText",
|
|
"matchLinksOutsideATags",
|
|
"skipSort",
|
|
"reverseSort",
|
|
"sortByLastLinkSegment",
|
|
"versionExtractWholePage",
|
|
"requestHeader",
|
|
"defaultPseudoVersioningMethod",
|
|
# Common keys
|
|
"trackOnly",
|
|
"versionExtractionRegEx",
|
|
"matchGroupToUse",
|
|
"versionDetection",
|
|
"releaseDateAsVersion",
|
|
"useVersionCodeAsOSVersion",
|
|
"apkFilterRegEx",
|
|
"invertAPKFilter",
|
|
"autoApkFilterByArch",
|
|
"appName",
|
|
"appAuthor",
|
|
"shizukuPretendToBeGooglePlay",
|
|
"allowInsecure",
|
|
"exemptFromBackgroundUpdates",
|
|
"skipUpdateNotifications",
|
|
"about",
|
|
"refreshBeforeDownload",
|
|
"includeZips",
|
|
"zippedApkFilterRegEx",
|
|
]
|
|
|
|
# Fields to backfill with defaults when missing
|
|
DEFAULTS: dict[str, object] = {
|
|
"allowIdChange": False,
|
|
}
|
|
|
|
|
|
def _order_dict(d: dict[str, Any], key_order: list[str]) -> dict[str, Any]:
|
|
ordered: dict[str, Any] = {}
|
|
for key in key_order:
|
|
if key in d:
|
|
ordered[key] = d[key]
|
|
# Preserve any unexpected keys at the end (safety net)
|
|
for key in d:
|
|
if key not in ordered:
|
|
ordered[key] = d[key]
|
|
return ordered
|
|
|
|
|
|
def normalize_app(app: dict) -> dict:
|
|
for key, default in DEFAULTS.items():
|
|
if key not in app:
|
|
app[key] = default
|
|
|
|
# Normalize additionalSettings key order if it's a dict
|
|
settings = app.get("additionalSettings")
|
|
if isinstance(settings, dict):
|
|
app["additionalSettings"] = _order_dict(settings, SETTINGS_KEY_ORDER)
|
|
|
|
return _order_dict(app, KEY_ORDER)
|
|
|
|
|
|
def normalize(input_path: str) -> int:
|
|
path = Path(input_path)
|
|
if not path.exists():
|
|
print(f"Error: {path} not found.")
|
|
return 1
|
|
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
apps = data.get("apps", [])
|
|
if not apps:
|
|
print("No apps found in file.")
|
|
return 1
|
|
|
|
changes = 0
|
|
for i, app in enumerate(apps):
|
|
normalized = normalize_app(app)
|
|
|
|
# Check if anything changed (key order or new defaults)
|
|
if list(app.keys()) != list(normalized.keys()) or app != normalized:
|
|
changes += 1
|
|
|
|
apps[i] = normalized
|
|
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
f.write("\n")
|
|
|
|
if changes:
|
|
print(f"Normalized {changes} app(s) in {path}")
|
|
else:
|
|
print(f"All {len(apps)} apps already normalized")
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
input_file = sys.argv[1] if len(sys.argv) > 1 else SRC_FILE
|
|
sys.exit(normalize(input_file))
|