#!/usr/bin/env python3 """Writes the Homebrew Channel manifest for a built ipk. The Homebrew Channel installs apps from a manifest that points at the ipk and carries its hash. Publishing means putting this file somewhere stable (a GitHub release asset, or the repository itself) and submitting the URL to webosbrew/repo. python3 tools/make-manifest.py \\ --base-url https://github.com/you/lgtv-audio-cap/releases/download/v1.0.0 Defaults to the newest ipk in out/ and writes out/manifest.json. """ import argparse import glob import hashlib import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.join(HERE, os.pardir) SOURCE_URL = "https://github.com/webosbrew/lgtv-audio-cap" def newest_ipk(directory): matches = sorted(glob.glob(os.path.join(directory, "*.ipk")), key=os.path.getmtime, reverse=True) return matches[0] if matches else None def sha256(path): digest = hashlib.sha256() with open(path, "rb") as fh: for block in iter(lambda: fh.read(1 << 20), b""): digest.update(block) return digest.hexdigest() def main(): parser = argparse.ArgumentParser(description=__doc__.split("\n")[0]) parser.add_argument("--ipk", default=None, help="path to the ipk") parser.add_argument("--base-url", default=None, help="URL the ipk and icon will be served from") parser.add_argument("--source-url", default=SOURCE_URL, help="project page shown in the Homebrew Channel") parser.add_argument("--out", default=os.path.join(ROOT, "out", "manifest.json")) args = parser.parse_args() appinfo = json.load(open(os.path.join(ROOT, "frontend", "appinfo.json"))) ipk = args.ipk or newest_ipk(os.path.join(ROOT, "out")) if not ipk or not os.path.exists(ipk): print("no ipk found; run ./tools/build.sh first", file=sys.stderr) return 1 name = os.path.basename(ipk) base = (args.base_url or "").rstrip("/") manifest = { "id": appinfo["id"], "version": appinfo["version"], "type": appinfo.get("type", "web"), "title": appinfo.get("title", appinfo["id"]), "appDescription": appinfo.get("appDescription", ""), "iconUri": base + "/icon.png" if base else "icon.png", "sourceUrl": args.source_url, # The service reads the TV's audio devices, which are root-only. The # Homebrew Channel uses this to elevate the service at install time. "rootRequired": True, "ipkUrl": base + "/" + name if base else name, "ipkHash": {"sha256": sha256(ipk)}, "ipkSize": os.path.getsize(ipk), } os.makedirs(os.path.dirname(args.out), exist_ok=True) with open(args.out, "w") as fh: json.dump(manifest, fh, indent=2) fh.write("\n") print(json.dumps(manifest, indent=2)) print("\nwrote %s" % os.path.relpath(args.out, ROOT), file=sys.stderr) if not base: print("note: no --base-url given, so ipkUrl and iconUri are relative", file=sys.stderr) return 0 if __name__ == "__main__": sys.exit(main())