chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
89
tools/web/build-webengine-ffmpeg.py
Normal file
89
tools/web/build-webengine-ffmpeg.py
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build Blender-pinned FFmpeg audio libraries for the embedded Audaspace plugin."""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
import urllib.request
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
VERSION = "8.1"
|
||||
SHA256 = "c07039598df7d64d3c8b42c4e25b1959fc908621c6f6c2946881133f3b27eda2"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--source", type=Path)
|
||||
parser.add_argument("--jobs", type=int, default=3)
|
||||
args = parser.parse_args()
|
||||
base = ROOT / "build_web_ffmpeg"
|
||||
base.mkdir(exist_ok=True)
|
||||
source = args.source.resolve() if args.source else base / f"ffmpeg-{VERSION}"
|
||||
if not (source / "configure").is_file():
|
||||
if args.source:
|
||||
raise FileNotFoundError(source)
|
||||
filename = f"ffmpeg-{VERSION}.tar.bz2"
|
||||
caches = [base / filename, Path.home() / "working/packages" / filename,
|
||||
Path.home() / "working/build_oiio_deps/downloads" / filename]
|
||||
archive = next((path for path in caches if path.is_file()), caches[0])
|
||||
if not archive.is_file():
|
||||
partial = archive.with_suffix(".partial")
|
||||
with urllib.request.urlopen(f"https://ffmpeg.org/releases/{filename}", timeout=60) as response, partial.open("wb") as out:
|
||||
shutil.copyfileobj(response, out)
|
||||
partial.replace(archive)
|
||||
if hashlib.sha256(archive.read_bytes()).hexdigest() != SHA256:
|
||||
raise RuntimeError("Blender-pinned FFmpeg archive digest mismatch")
|
||||
with tarfile.open(archive) as package:
|
||||
package.extractall(base, filter="data")
|
||||
if (source / "RELEASE").read_text().strip() != VERSION:
|
||||
raise RuntimeError("Expected Blender-pinned FFmpeg " + VERSION)
|
||||
prefix = base / "install"
|
||||
build = base / "build"
|
||||
build.mkdir(exist_ok=True)
|
||||
em = ROOT / ".emsdk/upstream/emscripten"
|
||||
codecs = ROOT / "build_web_sndfile/install"
|
||||
env = dict(os.environ)
|
||||
env["PATH"] = str(em) + os.pathsep + env["PATH"]
|
||||
env["PKG_CONFIG_LIBDIR"] = str(codecs / "lib/pkgconfig")
|
||||
env["PKG_CONFIG_PATH"] = ""
|
||||
command = [str(source / "configure"), f"--prefix={prefix}",
|
||||
"--target-os=none", "--arch=wasm32", "--enable-cross-compile",
|
||||
"--cc=" + str(em / "emcc"), "--cxx=" + str(em / "em++"),
|
||||
"--ar=" + str(em / "emar"), "--ranlib=" + str(em / "emranlib"),
|
||||
"--nm=" + str(em.parent / "bin/llvm-nm"),
|
||||
f"--extra-cflags=-O2 -fPIC -I{codecs / 'include'}",
|
||||
f"--extra-ldflags=-L{codecs / 'lib'}", "--disable-asm", "--disable-x86asm",
|
||||
"--disable-pthreads", "--disable-w32threads", "--disable-os2threads",
|
||||
"--disable-network", "--disable-autodetect", "--disable-everything",
|
||||
"--disable-programs", "--disable-doc", "--disable-debug",
|
||||
"--disable-avdevice", "--disable-avfilter", "--disable-swscale",
|
||||
"--disable-shared", "--enable-static", "--enable-small",
|
||||
"--enable-protocol=file", "--enable-libmp3lame", "--enable-libopus",
|
||||
"--enable-libvorbis", "--pkg-config-flags=--static",
|
||||
"--enable-encoder=aac,ac3,mp2,flac,libmp3lame,libopus,libvorbis,pcm_u8,pcm_s16le,pcm_s24le,pcm_s32le,pcm_f32le,pcm_f64le",
|
||||
"--enable-decoder=aac,ac3,mp2,mp3,flac,opus,vorbis,pcm_u8,pcm_s16le,pcm_s24le,pcm_s32le,pcm_f32le,pcm_f64le",
|
||||
"--enable-parser=aac,ac3,mpegaudio,flac,opus,vorbis",
|
||||
"--enable-muxer=adts,ac3,mp2,mp3,flac,matroska,ogg,wav",
|
||||
"--enable-demuxer=aac,ac3,mp3,flac,matroska,ogg,wav,mov",
|
||||
"--enable-bsf=aac_adtstoasc,extract_extradata"]
|
||||
settings = json.dumps([command, env["PKG_CONFIG_LIBDIR"]])
|
||||
stamp = build / "web-configure.json"
|
||||
if not stamp.is_file() or stamp.read_text() != settings:
|
||||
subprocess.run(command, cwd=build, env=env, check=True)
|
||||
stamp.write_text(settings)
|
||||
subprocess.run(["make", f"-j{args.jobs}", "install"], cwd=build, env=env, check=True)
|
||||
artifacts = {str(path.relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
for path in sorted((prefix / "lib").glob("*.a"))}
|
||||
(base / "build-manifest.json").write_text(json.dumps({
|
||||
"version": VERSION, "source": str(source), "upstreamArchiveSha256": SHA256,
|
||||
"configure": command, "artifacts": artifacts,
|
||||
"validation": "NOT_RUN", "acceptance": "NOT_CLAIMED"}, indent=2) + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user