"""Turn the LGPL FFmpeg n7.1 install (mac-build/pb_build_mac.sh) into drop-in copies of the five FFmpeg dylibs that
opencv-contrib-python 4.11.0.86 bundles in cv2/.dylibs: same file names, same install ids (/DLC/cv2/.dylibs/<name>),
deps as @loader_path/<name>, ad-hoc signed like the originals. Writes the sha256 pins next to the dylibs.
Eval 1 Builder PB, 2026-10-05.  usage: python3 pb_prepare_mac_dylibs.py <install dir> <out dir>"""
import hashlib, json, re, shutil, subprocess, sys
from pathlib import Path

LIBS = {"avcodec": "libavcodec.61.19.100.dylib", "avformat": "libavformat.61.7.100.dylib",
        "avutil": "libavutil.59.39.100.dylib", "swscale": "libswscale.8.3.100.dylib",
        "swresample": "libswresample.5.3.100.dylib"}
OK = ("/usr/lib/", "/System/Library/", "@loader_path/")


def run(*a):
    return subprocess.run(a, check=True, capture_output=True, text=True).stdout


inst, out = Path(sys.argv[1]), Path(sys.argv[2])
if out.exists():
    shutil.rmtree(out)
out.mkdir(parents=True)
for short, name in LIBS.items():
    shutil.copyfile(inst / "lib" / name, out / name)
for short, name in LIBS.items():
    p = out / name
    run("install_name_tool", "-id", f"/DLC/cv2/.dylibs/{name}", str(p))
    for ln in run("otool", "-L", str(p)).splitlines()[2:]:
        dep = ln.strip().split(" (")[0]
        m = re.match(r".*/lib(avcodec|avformat|avutil|swscale|swresample)\.\d+\.dylib$", dep)
        if m and not dep.startswith("@loader_path/"):
            run("install_name_tool", "-change", dep, "@loader_path/" + LIBS[m.group(1)], str(p))
    run("codesign", "-f", "-s", "-", str(p))
pins = {}
for short, name in LIBS.items():
    p = out / name
    deps = [ln.strip().split(" (")[0] for ln in run("otool", "-L", str(p)).splitlines()[2:]]
    bad = [d for d in deps if not d.startswith(OK)]
    assert not bad, (name, bad)
    lic = re.findall(rb"(?:L?GPL version [^\x00]+|nonfree and unredistributable)", p.read_bytes())
    minos = re.findall(r"minos (\S+)", run("otool", "-l", str(p)))
    pins[name] = {"sha256": hashlib.sha256(p.read_bytes()).hexdigest(), "bytes": p.stat().st_size,
                  "id": run("otool", "-D", str(p)).splitlines()[-1], "deps": deps, "minos": minos,
                  "licence_strings": sorted({x.decode() for x in lic}),
                  "codesign": subprocess.run(["codesign", "-dv", str(p)], capture_output=True, text=True).stderr.strip().splitlines()[-1]}
cfg = re.search(rb"--prefix=[^\x00]+", (out / LIBS["avutil"]).read_bytes())
(out / "LGPL-FFMPEG-DYLIBS.json").write_text(json.dumps({"ffmpeg": "n7.1 b08d7969c550a804a59511c7b83f2dd8cc0499b8",
    "configure": cfg.group(0).decode() if cfg else None, "dylibs": pins}, indent=1) + "\n")
print(json.dumps({k: (v["sha256"][:16], v["minos"], v["licence_strings"]) for k, v in pins.items()}, indent=1))
