diff --git a/pkgs/by-name/wi/widevine-cdm/package.nix b/pkgs/by-name/wi/widevine-cdm/package.nix index bae718940d20..11160260b665 100644 --- a/pkgs/by-name/wi/widevine-cdm/package.nix +++ b/pkgs/by-name/wi/widevine-cdm/package.nix @@ -5,6 +5,7 @@ }: let + # TODO: update aarch64 to use crx3 targets = lib.genAttrs [ "aarch64-linux" "x86_64-linux" diff --git a/pkgs/by-name/wi/widevine-cdm/update-x86_64.py b/pkgs/by-name/wi/widevine-cdm/update-x86_64.py new file mode 100755 index 000000000000..4b18c1dd0b78 --- /dev/null +++ b/pkgs/by-name/wi/widevine-cdm/update-x86_64.py @@ -0,0 +1,107 @@ +#! /usr/bin/env nix-shell +#! nix-shell -i python3 -p python3 + +from __future__ import annotations +import sys, os, json, base64, re, argparse, urllib.request, time +from typing import Dict, Optional, Tuple +from pathlib import Path + +DEFAULT_JSON = "https://raw.githubusercontent.com/mozilla/gecko-dev/master/toolkit/content/gmp-sources/widevinecdm.json" +ARCH_PATTERNS = { + "linux_x86_64": ["Linux_x86_64-gcc3"], + "linux_aarch64": ["Linux_aarch64-gcc3"] +} + +def fetch_json(url: str) -> dict: + with urllib.request.urlopen(url) as r: + return json.load(r) + +def hex_to_sri(hexstr: str) -> str: + b = bytes.fromhex(hexstr) + return "sha512-" + base64.b64encode(b).decode() + +def find_widevine_vendor(data: dict) -> dict: + vendors = data.get("vendors") or {} + for key, value in vendors.items(): + if "gmp-widevinecdm" in key.lower(): + return value + + raise SystemExit("ERR: couldn't find a widevine vendor entry in JSON !") + +def judge_platforms(platforms: dict, patterns) -> Optional[Tuple[str, dict]]: + for plkey, plvalue in platforms.items(): + if plvalue.get("alias"): + continue + + low = plkey.lower() + for pat in patterns: + if pat.lower() in low: + return plkey, plvalue + + return None + +def normalize_fileurl(entry: dict) -> Optional[str]: + if entry.get("fileUrl"): + return entry["fileUrl"] + + m = entry.get("mirrorUrls") + if isinstance(m, list) and m: + return m[0] + + return None + +def extract_ver_from_url(url: str) -> Optional[str]: + m = re.search(r'[_-](\d+\.\d+\.\d+\.\d+)[_-]', url) + if m: + return m.group(1) + m = re.search(r'(\d+\.\d+\.\d+\.\d+)', url) + return m.group(1) if m else None + +def build_entry(pkey: str, pentry: dict) -> dict: + url = normalize_fileurl(pentry) + hv = pentry.get("hashValue") + + if not url or not hv: + raise SystemExit(f"ERR: platform {pkey} is missing a file URL or hash !") + + sri = hex_to_sri(hv) + version = extract_ver_from_url(url) + return {"platform_key": pkey, "url": url, "sri": sri, "version": version} + +def main(): + WIDEVINE_DIR = Path(__file__).resolve().parent + DEFAULT_FILE = WIDEVINE_DIR / "x86_64-manifest.json" + + ap = argparse.ArgumentParser() + ap.add_argument("file", type=Path, nargs="?", default=DEFAULT_FILE) + ap.add_argument("--json-url", default=DEFAULT_JSON) + args = ap.parse_args() + + print(f"# fetching {args.json_url} !", file=sys.stderr) + data = fetch_json(args.json_url) + vendor = find_widevine_vendor(data) + platforms = vendor.get("platforms") or {} # should never be null but moz could forseeably delete it + if not platforms: + raise SystemExit("ERR: no widevine platforms !") + + results = {} + for arch, patterns in ARCH_PATTERNS.items(): + found = judge_platforms(platforms, patterns) + if not found: + print(f"# WARN: no platform for {arch}. skipping !", file=sys.stderr) + continue + pkey, pentry = found + print(f"# found {arch} --> {pkey}", file=sys.stderr) + entry = build_entry(pkey, pentry) + results[arch] = entry + + if not results: + raise SystemExit("ERR: no linux platform entries !") + + linux_x64 = results["linux_x86_64"] + + args.file.write_text(json.dumps(linux_x64, indent=2) + "\n", encoding="utf-8") + print("# updated", file=sys.stderr) + +if __name__ == "__main__": + main() diff --git a/pkgs/by-name/wi/widevine-cdm/x86_64-linux.nix b/pkgs/by-name/wi/widevine-cdm/x86_64-linux.nix index ae16e951c666..35b83199467b 100644 --- a/pkgs/by-name/wi/widevine-cdm/x86_64-linux.nix +++ b/pkgs/by-name/wi/widevine-cdm/x86_64-linux.nix @@ -1,29 +1,44 @@ { lib, stdenv, - fetchzip, + fetchurl, + go-crx3, }: +let + manifest = builtins.fromJSON (builtins.readFile ./x86_64-manifest.json); +in + stdenv.mkDerivation (finalAttrs: { pname = "widevine-cdm"; - version = "4.10.2891.0"; + version = manifest.version; - src = fetchzip { - # The download 404s - url = "https://web.archive.org/web/20250725071306/https://dl.google.com/widevine-cdm/4.10.2891.0-linux-x64.zip"; - hash = "sha256-ZO6FmqJUnB9VEJ7caJt58ym8eB3/fDATri3iOWCULRI="; - stripRoot = false; + src = fetchurl { + url = manifest.url; + hash = manifest.sri; }; + nativeBuildInputs = [ go-crx3 ]; + + unpackPhase = '' + unpackDir="src" + cp "$src" "$unpackDir".crx # go-crx3 doesn't like .crx3 extensions + crx3 unpack "$unpackDir".crx + + cd "$unpackDir" + ''; + installPhase = '' runHook preInstall install -vD manifest.json $out/share/google/chrome/WidevineCdm/manifest.json - install -vD LICENSE.txt $out/share/google/chrome/WidevineCdm/LICENSE.txt - install -vD libwidevinecdm.so $out/share/google/chrome/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so + install -vD LICENSE $out/share/google/chrome/WidevineCdm/LICENSE.txt + install -vD _platform_specific/linux_x64/libwidevinecdm.so $out/share/google/chrome/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so runHook postInstall ''; + passthru.updateScript = ./update-x86_64.py; + meta = import ./meta.nix lib; }) diff --git a/pkgs/by-name/wi/widevine-cdm/x86_64-manifest.json b/pkgs/by-name/wi/widevine-cdm/x86_64-manifest.json new file mode 100644 index 000000000000..07fa91d5592b --- /dev/null +++ b/pkgs/by-name/wi/widevine-cdm/x86_64-manifest.json @@ -0,0 +1,6 @@ +{ + "platform_key": "Linux_x86_64-gcc3", + "url": "https://edgedl.me.gvt1.com/edgedl/release2/chrome_component/aclxnidwwkj5di3vduduj2gqpgpq_4.10.2891.0/oimompecagnajdejgnnjijobebaeigek_4.10.2891.0_linux_b4hin3q5s66ws2322cyyfp35lu.crx3", + "sri": "sha512-8S/4FRQQmQAO9wJ7QGJg8KUVxUQgm5/oEl6l9N2/WGAdMCIqxBoBPrCkPadrpwQUpK0WYX2fIZ8Zzfx3lNm8lA==", + "version": "4.10.2891.0" +}