"""Publish license texts and pinned upstream source materials alongside the helper. Run after tools/build_bundle.py. This does not alter or install the helper. Third-party FFmpeg dependency download/build recipes are preserved verbatim. """ from __future__ import annotations import concurrent.futures import hashlib import json import subprocess import zipfile from pathlib import Path import requests ROOT = Path(__file__).resolve().parent.parent KIT = ROOT / ".build/kit" CACHE = ROOT / ".build-cache/release-sources" FFMPEG_REVISION = "17ec99894249a68444d145ea8870713a4fb7f342" BUILD_REVISION = "8fc3fe09d1d84a10cecc2b08706e303e27e64834" def download(item): CACHE.mkdir(parents=True, exist_ok=True) target = CACHE / item["file"] if not target.is_file(): response = requests.get(item["url"], timeout=(20, 180)) response.raise_for_status() target.write_bytes(response.content) data = target.read_bytes() checksum = hashlib.sha256(data).hexdigest() if item.get("expectedSHA256") and item["expectedSHA256"] != checksum: raise ValueError(f"Source hash mismatch: {item['file']}") print(f"Source: {item['file']} ({len(data)} bytes)", flush=True) return {**item, "sha256": checksum, "bytes": len(data)} def main(): manifest = json.loads((ROOT / "dist/YouTubeDL-Helper-manifest.json").read_text(encoding="utf-8")) sources = [ {"file": f"FFmpeg-{FFMPEG_REVISION}.tar.gz", "url": f"https://codeload.github.com/FFmpeg/FFmpeg/tar.gz/{FFMPEG_REVISION}", "component": "FFmpeg"}, {"file": f"FFmpeg-Builds-{BUILD_REVISION}.tar.gz", "url": f"https://codeload.github.com/yt-dlp/FFmpeg-Builds/tar.gz/{BUILD_REVISION}", "component": "FFmpeg build and dependency source recipes"}, ] for metadata in sorted((KIT / "runtime/python/Lib/site-packages").glob("*.dist-info/METADATA")): fields = dict(line.split(": ", 1) for line in metadata.read_text(encoding="utf-8").splitlines() if line.startswith(("Name: ", "Version: "))) name, version = fields["Name"], fields["Version"] response = requests.get(f"https://pypi.org/pypi/{name}/{version}/json", timeout=30) response.raise_for_status() sdist = next((file for file in response.json()["urls"] if file["packagetype"] == "sdist"), None) if sdist: sources.append({"file": sdist["filename"], "url": sdist["url"], "expectedSHA256": sdist["digests"]["sha256"], "component": f"{name} {version}"}) with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: records = list(pool.map(download, sources)) configuration = subprocess.run([str(KIT / "runtime/bin/ffmpeg.exe"), "-version"], capture_output=True, text=True, check=True).stdout destination = ROOT / "dist/YouTubeDL-ThirdParty-Materials.zip" with zipfile.ZipFile(destination,"w",zipfile.ZIP_DEFLATED) as archive: for name in ("THIRD_PARTY.md", "LICENSE", "CONTRIBUTING.md"): archive.write(ROOT / name, name) archive.writestr("helper-manifest.json", json.dumps(manifest,indent=2)) archive.writestr("source-index.json", json.dumps(records,indent=2)) archive.writestr("FFmpeg-configuration.txt", configuration) archive.writestr("SOURCE-README.txt", f"""YouTubeDL helper {manifest['version']}: upstream source materials and licenses. The exact FFmpeg source revision is in sources/; FFmpeg-Builds scripts contain the pinned download and build recipes for its third-party libraries. Extract the FFmpeg-Builds source archive and follow its Docker build instructions. Run ./download.sh win64 gpl-shared to fetch dependency sources; build.sh is the corresponding build recipe. Read scripts.d, variants and util for configuration. The supplied FFmpeg binary is unchanged from yt-dlp/FFmpeg-Builds. Python and Node.js official sources: https://www.python.org/ftp/python/3.13.14/Python-3.13.14.tgz https://nodejs.org/dist/v24.21.0/node-v24.21.0.tar.xz Python package source distributions are included where PyPI provides them. The original installer source and Native Messaging helper source are in the YouTubeDL repository. The root LICENSE governs original YouTubeDL materials. Third-party components are distributed under their own licenses; see THIRD_PARTY.md. """) for record in records: archive.write(CACHE / record["file"], "sources/" + record["file"]) for file in KIT.rglob("*"): if not file.is_file(): continue relative = file.relative_to(KIT) if "licenses" in relative.parts or (".dist-info" in str(relative) and (file.name == "METADATA" or any(word in str(relative).lower() for word in ("license","copying","notice")))) or file.name == "LICENSE.txt": archive.write(file,"licenses/" + str(relative).replace("\\","/")) print(f"Materials: {destination.name} ({destination.stat().st_size} bytes)", flush=True) if __name__ == "__main__": main()